summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitignore41
-rw-r--r--AUTHORS.md31
-rw-r--r--CMakeLists.txt12
-rw-r--r--COPYING2
-rw-r--r--Jenkinsfile84
-rw-r--r--LICENSE.txt37
-rw-r--r--Makefile10
-rw-r--r--OFFICIAL-RELEASE-STEPS.md18
-rw-r--r--README.md93
-rw-r--r--RELEASE-NOTES.md173
-rw-r--r--artwork/ZeroTierIcon32x32.pngbin0 -> 7219 bytes
-rw-r--r--attic/Cluster.cpp (renamed from node/Cluster.cpp)265
-rw-r--r--attic/Cluster.hpp (renamed from node/Cluster.hpp)73
-rw-r--r--attic/ClusterDefinition.hpp (renamed from service/ClusterDefinition.hpp)20
-rw-r--r--attic/ClusterGeoIpService.cpp (renamed from service/ClusterGeoIpService.cpp)12
-rw-r--r--attic/ClusterGeoIpService.hpp (renamed from service/ClusterGeoIpService.hpp)10
-rw-r--r--attic/FCV.hpp101
-rw-r--r--attic/Filter.cpp408
-rw-r--r--attic/Filter.hpp284
-rw-r--r--attic/SECURITY.md84
-rw-r--r--attic/big-http-test/2015-11-10_01_50000.out.xzbin730360 -> 0 bytes
-rw-r--r--attic/big-http-test/2015-11-10_02_50000.out.xzbin2373664 -> 0 bytes
-rw-r--r--attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xzbin802932 -> 0 bytes
-rw-r--r--attic/big-http-test/Dockerfile24
-rw-r--r--attic/big-http-test/README.md12
-rw-r--r--attic/big-http-test/agent.js196
-rwxr-xr-xattic/big-http-test/big-test-kill.sh9
-rwxr-xr-xattic/big-http-test/big-test-start.sh13
-rw-r--r--attic/big-http-test/crunch-results.js65
-rwxr-xr-xattic/big-http-test/docker-main.sh16
-rw-r--r--attic/big-http-test/nodesource-el.repo6
-rw-r--r--attic/big-http-test/package.json16
-rw-r--r--attic/big-http-test/server.js53
-rw-r--r--attic/historic/anode/LICENSE.txt674
-rw-r--r--attic/historic/anode/config.mk.Darwin17
-rw-r--r--attic/historic/anode/config.mk.Linux17
-rw-r--r--attic/historic/anode/docs/anode_protocol.txt764
-rw-r--r--attic/historic/anode/libanode/Makefile33
-rw-r--r--attic/historic/anode/libanode/address.c98
-rw-r--r--attic/historic/anode/libanode/aes_digest.c85
-rw-r--r--attic/historic/anode/libanode/anode.h795
-rw-r--r--attic/historic/anode/libanode/errors.c52
-rw-r--r--attic/historic/anode/libanode/identity.c110
-rw-r--r--attic/historic/anode/libanode/impl/aes.c72
-rw-r--r--attic/historic/anode/libanode/impl/aes.h64
-rw-r--r--attic/historic/anode/libanode/impl/dictionary.c239
-rw-r--r--attic/historic/anode/libanode/impl/dictionary.h126
-rw-r--r--attic/historic/anode/libanode/impl/dns_txt.c93
-rw-r--r--attic/historic/anode/libanode/impl/dns_txt.h37
-rw-r--r--attic/historic/anode/libanode/impl/ec.c219
-rw-r--r--attic/historic/anode/libanode/impl/ec.h61
-rw-r--r--attic/historic/anode/libanode/impl/environment.c118
-rw-r--r--attic/historic/anode/libanode/impl/environment.h30
-rw-r--r--attic/historic/anode/libanode/impl/http_client.c558
-rw-r--r--attic/historic/anode/libanode/impl/http_client.h200
-rw-r--r--attic/historic/anode/libanode/impl/misc.c190
-rw-r--r--attic/historic/anode/libanode/impl/misc.h193
-rw-r--r--attic/historic/anode/libanode/impl/mutex.h34
-rw-r--r--attic/historic/anode/libanode/impl/thread.c58
-rw-r--r--attic/historic/anode/libanode/impl/thread.h65
-rw-r--r--attic/historic/anode/libanode/impl/types.h25
-rw-r--r--attic/historic/anode/libanode/network_address.c136
-rw-r--r--attic/historic/anode/libanode/secure_random.c88
-rw-r--r--attic/historic/anode/libanode/system_transport.c948
-rw-r--r--attic/historic/anode/libanode/tests/Makefile25
-rw-r--r--attic/historic/anode/libanode/tests/aes-test.c191
-rw-r--r--attic/historic/anode/libanode/tests/anode-secure_random-test.c38
-rw-r--r--attic/historic/anode/libanode/tests/anode-utils-test.c75
-rw-r--r--attic/historic/anode/libanode/tests/anode-zone-test.c47
-rw-r--r--attic/historic/anode/libanode/tests/dictionary-test.c149
-rw-r--r--attic/historic/anode/libanode/tests/ec-test.c97
-rw-r--r--attic/historic/anode/libanode/tests/environment-test.c28
-rw-r--r--attic/historic/anode/libanode/tests/http_client-test.c233
-rw-r--r--attic/historic/anode/libanode/tests/misc-test.c137
-rw-r--r--attic/historic/anode/libanode/tests/system_transport-test.c70
-rw-r--r--attic/historic/anode/libanode/uri.c185
-rw-r--r--attic/historic/anode/libanode/utils/anode-make-identity.c50
-rw-r--r--attic/historic/anode/libanode/zone.c184
-rw-r--r--attic/historic/anode/libspark/Makefile16
-rw-r--r--attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp161
-rw-r--r--attic/historic/anode/libspark/experiments/Makefile5
-rw-r--r--attic/historic/anode/libspark/streamencoder.h108
-rw-r--r--attic/historic/anode/libspark/wrapper.h66
-rw-r--r--attic/kubernetes_docs/.zerotierCliSettings18
-rw-r--r--attic/kubernetes_docs/Dockerfile19
-rw-r--r--attic/kubernetes_docs/README.md150
-rw-r--r--attic/kubernetes_docs/entrypoint.sh23
-rw-r--r--attic/kubernetes_docs/server.js8
-rw-r--r--attic/lat_lon_to_xyz.js25
-rwxr-xr-xattic/old-linux-installer/buildinstaller.sh134
-rw-r--r--attic/old-linux-installer/install.tmpl.sh182
-rwxr-xr-xattic/old-linux-installer/uninstall.sh76
-rw-r--r--attic/world/README.md (renamed from world/README.md)0
-rwxr-xr-xattic/world/build.sh (renamed from world/build.sh)0
-rw-r--r--attic/world/earth-2016-01-13.bin (renamed from world/earth-2016-01-13.bin)bin634 -> 634 bytes
-rw-r--r--attic/world/mkworld.cpp (renamed from world/mkworld.cpp)21
-rw-r--r--attic/world/old/earth-2015-11-16.bin (renamed from world/old/earth-2015-11-16.bin)bin494 -> 494 bytes
-rw-r--r--attic/world/old/earth-2015-11-20.bin (renamed from world/old/earth-2015-11-20.bin)bin792 -> 792 bytes
-rw-r--r--attic/world/old/earth-2015-12-17.bin (renamed from world/old/earth-2015-12-17.bin)bin732 -> 732 bytes
-rw-r--r--cli/README.md6
-rw-r--r--cli/zerotier.cpp335
-rw-r--r--controller/DB.cpp511
-rw-r--r--controller/DB.hpp139
-rw-r--r--controller/EmbeddedNetworkController.cpp1710
-rw-r--r--controller/EmbeddedNetworkController.hpp159
-rw-r--r--controller/FileDB.cpp150
-rw-r--r--controller/FileDB.hpp46
-rw-r--r--controller/README.md244
-rw-r--r--controller/RethinkDB.cpp488
-rw-r--r--controller/RethinkDB.hpp84
-rw-r--r--controller/SqliteNetworkController.cpp2195
-rw-r--r--controller/SqliteNetworkController.hpp181
-rw-r--r--controller/schema.sql119
-rw-r--r--controller/schema.sql.c121
-rwxr-xr-xcontroller/schema2c.sh8
-rw-r--r--debian/changelog24
-rw-r--r--debian/control4
-rw-r--r--debian/control.wheezy4
-rw-r--r--debian/postinst9
-rwxr-xr-xdebian/rules2
-rw-r--r--debian/rules.static16
-rwxr-xr-xdebian/rules.wheezy2
-rw-r--r--debian/rules.wheezy.static11
-rw-r--r--debian/source/format (renamed from debian/format)0
-rw-r--r--doc/zerotier-cli.183
-rw-r--r--doc/zerotier-idtool.184
-rw-r--r--doc/zerotier-one.8104
-rw-r--r--ext/arm32-neon-salsa2012-asm/README.md6
-rw-r--r--ext/arm32-neon-salsa2012-asm/salsa2012.h23
-rw-r--r--ext/arm32-neon-salsa2012-asm/salsa2012.s2231
-rw-r--r--ext/bin/tap-windows-ndis5/x64/WdfCoinstaller01011.dllbin1795952 -> 0 bytes
-rw-r--r--ext/bin/tap-windows-ndis5/x64/zttap200.catbin10549 -> 0 bytes
-rw-r--r--ext/bin/tap-windows-ndis5/x64/zttap200.inf79
-rw-r--r--ext/bin/tap-windows-ndis5/x64/zttap200.sysbin31896 -> 0 bytes
-rw-r--r--ext/bin/tap-windows-ndis5/x86/WdfCoinstaller01011.dllbin1629040 -> 0 bytes
-rw-r--r--ext/bin/tap-windows-ndis5/x86/zttap200.catbin10496 -> 0 bytes
-rw-r--r--ext/bin/tap-windows-ndis5/x86/zttap200.inf76
-rw-r--r--ext/bin/tap-windows-ndis5/x86/zttap200.sysbin28824 -> 0 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/certutil.exebin0 -> 903168 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msibin1453056 -> 2056704 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msibin1070080 -> 1673728 bytes
-rw-r--r--ext/bin/tap-windows-ndis6/zttap300.cerbin0 -> 1321 bytes
-rw-r--r--ext/ed25519-amd64-asm/batch.c94
-rw-r--r--ext/ed25519-amd64-asm/choose_t.s1565
-rw-r--r--ext/ed25519-amd64-asm/consts.s39
-rw-r--r--ext/ed25519-amd64-asm/fe25519.h64
-rw-r--r--ext/ed25519-amd64-asm/fe25519_add.s189
-rw-r--r--ext/ed25519-amd64-asm/fe25519_freeze.s322
-rw-r--r--ext/ed25519-amd64-asm/fe25519_getparity.c8
-rw-r--r--ext/ed25519-amd64-asm/fe25519_invert.c60
-rw-r--r--ext/ed25519-amd64-asm/fe25519_iseq.c14
-rw-r--r--ext/ed25519-amd64-asm/fe25519_iszero.c12
-rw-r--r--ext/ed25519-amd64-asm/fe25519_mul.s865
-rw-r--r--ext/ed25519-amd64-asm/fe25519_neg.c8
-rw-r--r--ext/ed25519-amd64-asm/fe25519_pack.c13
-rw-r--r--ext/ed25519-amd64-asm/fe25519_pow2523.c55
-rw-r--r--ext/ed25519-amd64-asm/fe25519_setint.c9
-rw-r--r--ext/ed25519-amd64-asm/fe25519_square.s639
-rw-r--r--ext/ed25519-amd64-asm/fe25519_sub.s189
-rw-r--r--ext/ed25519-amd64-asm/fe25519_unpack.c11
-rw-r--r--ext/ed25519-amd64-asm/ge25519.h95
-rw-r--r--ext/ed25519-amd64-asm/ge25519_add.c8
-rw-r--r--ext/ed25519-amd64-asm/ge25519_add_p1p1.s4554
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base.c7
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base_niels.data1536
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data768
-rw-r--r--ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data96
-rw-r--r--ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s2975
-rw-r--r--ext/ed25519-amd64-asm/ge25519_double.c8
-rw-r--r--ext/ed25519-amd64-asm/ge25519_double_scalarmult.c102
-rw-r--r--ext/ed25519-amd64-asm/ge25519_isneutral.c9
-rw-r--r--ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c102
-rw-r--r--ext/ed25519-amd64-asm/ge25519_nielsadd2.s5791
-rw-r--r--ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s3072
-rw-r--r--ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s2236
-rw-r--r--ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s2926
-rw-r--r--ext/ed25519-amd64-asm/ge25519_pack.c13
-rw-r--r--ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s3662
-rw-r--r--ext/ed25519-amd64-asm/ge25519_scalarmult_base.c68
-rw-r--r--ext/ed25519-amd64-asm/ge25519_unpackneg.c60
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced.s476
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s416
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s436
-rw-r--r--ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s456
-rw-r--r--ext/ed25519-amd64-asm/hram.c16
-rw-r--r--ext/ed25519-amd64-asm/hram.h8
-rw-r--r--ext/ed25519-amd64-asm/implementors5
-rw-r--r--ext/ed25519-amd64-asm/index_heap.c58
-rw-r--r--ext/ed25519-amd64-asm/index_heap.h31
-rw-r--r--ext/ed25519-amd64-asm/keypair.c25
-rw-r--r--ext/ed25519-amd64-asm/open.c49
-rw-r--r--ext/ed25519-amd64-asm/sc25519.h66
-rw-r--r--ext/ed25519-amd64-asm/sc25519_add.s232
-rw-r--r--ext/ed25519-amd64-asm/sc25519_barrett.s1188
-rw-r--r--ext/ed25519-amd64-asm/sc25519_from32bytes.c55
-rw-r--r--ext/ed25519-amd64-asm/sc25519_from64bytes.c7
-rw-r--r--ext/ed25519-amd64-asm/sc25519_from_shortsc.c9
-rw-r--r--ext/ed25519-amd64-asm/sc25519_iszero.c10
-rw-r--r--ext/ed25519-amd64-asm/sc25519_lt.s131
-rw-r--r--ext/ed25519-amd64-asm/sc25519_mul.c12
-rw-r--r--ext/ed25519-amd64-asm/sc25519_mul_shortsc.c9
-rw-r--r--ext/ed25519-amd64-asm/sc25519_slide.c49
-rw-r--r--ext/ed25519-amd64-asm/sc25519_sub_nored.s142
-rw-r--r--ext/ed25519-amd64-asm/sc25519_to32bytes.c8
-rw-r--r--ext/ed25519-amd64-asm/sc25519_window4.c27
-rw-r--r--ext/ed25519-amd64-asm/sign.c165
-rw-r--r--ext/ed25519-amd64-asm/ull4_mul.s716
-rw-r--r--ext/http-parser/http_parser.c11
-rw-r--r--ext/http-parser/http_parser.h72
-rw-r--r--ext/installfiles/linux/zerotier-containerized/Dockerfile20
-rwxr-xr-xext/installfiles/linux/zerotier-containerized/main.sh10
-rw-r--r--ext/installfiles/mac-update/updater.tmpl.sh49
-rwxr-xr-xext/installfiles/mac/ZeroTier One.pkgproj141
-rwxr-xr-xext/installfiles/mac/launch.sh6
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Info.plist59
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/MacOS/ZeroTier Onebin152736 -> 0 bytes
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/PkgInfo1
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Credits.rtf13
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/InfoPlist.stringsbin92 -> 0 bytes
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/MainMenu.nibbin25269 -> 0 bytes
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Window.nibbin3616 -> 0 bytes
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/_CodeSignature/CodeResources187
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/LICENSE25
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.pbxproj489
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcshareddata/MacGap.xccheckout41
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/Alex.xcuserdatad/UserInterfaceState.xcuserstatebin46675 -> 0 bytes
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/api.xcuserdatad/WorkspaceSettings.xcsettings10
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/UserInterfaceState.xcuserstatebin27811 -> 0 bytes
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/WorkspaceSettings.xcsettings10
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.h18
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.m159
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.h20
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.m168
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h21
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m128
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h18
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m28
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h11
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m31
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h31
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m150
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h31
-rwxr-xr-xext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m233
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h26
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m108
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h21
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m53
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h17
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m97
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h43
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m211
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h9
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m48
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Constants.h7
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.h15
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.m68
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.h20
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.m41
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.h20
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.m93
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.h49
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.m206
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.h23
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.m94
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.h10
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.m51
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Prefix.pch15
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.h13
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.m54
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Credits.rtf13
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/InfoPlist.strings2
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/MainMenu.xib3404
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Window.xib44
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/main.m14
-rw-r--r--ext/installfiles/mac/mac-ui-macgap1-wrapper/src/README.md6
-rwxr-xr-xext/installfiles/mac/postinst.sh23
-rwxr-xr-xext/installfiles/mac/preinst.sh13
-rw-r--r--ext/installfiles/mac/ui/Makefile6
-rw-r--r--ext/installfiles/mac/ui/README.md10
-rw-r--r--ext/installfiles/mac/ui/ZeroTierNetwork.jsx74
-rw-r--r--ext/installfiles/mac/ui/ZeroTierNode.jsx158
-rw-r--r--ext/installfiles/mac/ui/index.html58
-rw-r--r--ext/installfiles/mac/ui/main.js51
-rw-r--r--ext/installfiles/mac/ui/react.min.js15
-rw-r--r--ext/installfiles/mac/ui/simpleajax.min.js2
-rw-r--r--ext/installfiles/mac/ui/zerotier.css199
-rw-r--r--ext/installfiles/mac/ui/ztui.min.js1
-rwxr-xr-xext/installfiles/mac/uninstall.sh12
-rw-r--r--ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip64
-rw-r--r--ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip64
-rw-r--r--ext/installfiles/windows/ZeroTier One.aip870
-rw-r--r--ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps14
-rw-r--r--ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec8
-rw-r--r--ext/json-parser/AUTHORS20
-rw-r--r--ext/json-parser/LICENSE26
-rw-r--r--ext/json-parser/README.md97
-rw-r--r--ext/json-parser/json.c1012
-rw-r--r--ext/json-parser/json.h283
-rw-r--r--ext/json/LICENSE.MIT17
-rw-r--r--ext/json/README.md566
-rw-r--r--ext/json/json.hpp15313
-rw-r--r--ext/librethinkdbxx/.travis.yml11
-rw-r--r--ext/librethinkdbxx/COPYRIGHT16
-rw-r--r--ext/librethinkdbxx/Makefile126
-rw-r--r--ext/librethinkdbxx/README.md72
-rw-r--r--ext/librethinkdbxx/reql/add_docs.py80
-rw-r--r--ext/librethinkdbxx/reql/gen.py33
-rw-r--r--ext/librethinkdbxx/reql/python_docs.txt189
-rw-r--r--ext/librethinkdbxx/reql/ql2.proto843
-rw-r--r--ext/librethinkdbxx/src/connection.cc434
-rw-r--r--ext/librethinkdbxx/src/connection.h59
-rw-r--r--ext/librethinkdbxx/src/connection_p.h133
-rw-r--r--ext/librethinkdbxx/src/cursor.cc223
-rw-r--r--ext/librethinkdbxx/src/cursor.h76
-rw-r--r--ext/librethinkdbxx/src/cursor_p.h29
-rw-r--r--ext/librethinkdbxx/src/datum.cc449
-rw-r--r--ext/librethinkdbxx/src/datum.h287
-rw-r--r--ext/librethinkdbxx/src/error.h46
-rw-r--r--ext/librethinkdbxx/src/exceptions.h13
-rw-r--r--ext/librethinkdbxx/src/json.cc62
-rw-r--r--ext/librethinkdbxx/src/json_p.h19
-rw-r--r--ext/librethinkdbxx/src/rapidjson-config.h8
-rw-r--r--ext/librethinkdbxx/src/rapidjson/allocators.h263
-rw-r--r--ext/librethinkdbxx/src/rapidjson/document.h2575
-rw-r--r--ext/librethinkdbxx/src/rapidjson/encodedstream.h299
-rw-r--r--ext/librethinkdbxx/src/rapidjson/encodings.h716
-rw-r--r--ext/librethinkdbxx/src/rapidjson/error/en.h74
-rw-r--r--ext/librethinkdbxx/src/rapidjson/error/error.h155
-rw-r--r--ext/librethinkdbxx/src/rapidjson/filereadstream.h99
-rw-r--r--ext/librethinkdbxx/src/rapidjson/filewritestream.h104
-rw-r--r--ext/librethinkdbxx/src/rapidjson/fwd.h151
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/biginteger.h290
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/diyfp.h258
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/dtoa.h245
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/ieee754.h78
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/itoa.h304
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/meta.h181
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/pow10.h55
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/regex.h701
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/stack.h230
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/strfunc.h55
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/strtod.h269
-rw-r--r--ext/librethinkdbxx/src/rapidjson/internal/swap.h46
-rw-r--r--ext/librethinkdbxx/src/rapidjson/istreamwrapper.h115
-rw-r--r--ext/librethinkdbxx/src/rapidjson/memorybuffer.h70
-rw-r--r--ext/librethinkdbxx/src/rapidjson/memorystream.h71
-rw-r--r--ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h316
-rw-r--r--ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h300
-rw-r--r--ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h81
-rw-r--r--ext/librethinkdbxx/src/rapidjson/pointer.h1358
-rw-r--r--ext/librethinkdbxx/src/rapidjson/prettywriter.h249
-rw-r--r--ext/librethinkdbxx/src/rapidjson/rapidjson.h615
-rw-r--r--ext/librethinkdbxx/src/rapidjson/reader.h1879
-rw-r--r--ext/librethinkdbxx/src/rapidjson/schema.h2006
-rw-r--r--ext/librethinkdbxx/src/rapidjson/stream.h179
-rw-r--r--ext/librethinkdbxx/src/rapidjson/stringbuffer.h117
-rw-r--r--ext/librethinkdbxx/src/rapidjson/writer.h609
-rw-r--r--ext/librethinkdbxx/src/term.cc285
-rw-r--r--ext/librethinkdbxx/src/term.h592
-rw-r--r--ext/librethinkdbxx/src/types.cc47
-rw-r--r--ext/librethinkdbxx/src/types.h53
-rw-r--r--ext/librethinkdbxx/src/utils.cc153
-rw-r--r--ext/librethinkdbxx/src/utils.h19
-rw-r--r--ext/lz4/lz4.c1516
-rw-r--r--ext/lz4/lz4.h360
-rw-r--r--ext/miniupnpc/Changelog.txt16
-rw-r--r--ext/miniupnpc/LICENSE2
-rw-r--r--ext/miniupnpc/MANIFEST.in5
-rw-r--r--ext/miniupnpc/README7
-rw-r--r--ext/miniupnpc/apiversions.txt172
-rw-r--r--ext/miniupnpc/connecthostport.c14
-rwxr-xr-xext/miniupnpc/external-ip.sh4
-rw-r--r--ext/miniupnpc/minihttptestserver.c659
-rw-r--r--ext/miniupnpc/minisoap.c14
-rw-r--r--ext/miniupnpc/minissdpc.c67
-rw-r--r--ext/miniupnpc/miniupnpc.c2
-rw-r--r--ext/miniupnpc/miniupnpc.h2
-rw-r--r--ext/miniupnpc/miniupnpcmodule.c16
-rw-r--r--ext/miniupnpc/miniwget.c30
-rw-r--r--ext/miniupnpc/minixml.c11
-rw-r--r--ext/miniupnpc/minixmlvalid.c1
-rw-r--r--ext/miniupnpc/portlistingparse.c6
-rw-r--r--ext/miniupnpc/pymoduletest.py88
-rw-r--r--ext/miniupnpc/receivedata.c4
-rw-r--r--ext/miniupnpc/testdesc/linksys_WAG200G_desc.values14
-rw-r--r--ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml110
-rw-r--r--ext/miniupnpc/testdesc/new_LiveBox_desc.values20
-rw-r--r--ext/miniupnpc/testdesc/new_LiveBox_desc.xml90
-rw-r--r--ext/miniupnpc/testigddescparse.c187
-rw-r--r--ext/miniupnpc/testminiwget.c55
-rwxr-xr-xext/miniupnpc/testminiwget.sh96
-rw-r--r--ext/miniupnpc/testminixml.c89
-rw-r--r--ext/miniupnpc/testportlistingparse.c151
-rw-r--r--ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue3
-rw-r--r--ext/miniupnpc/testreplyparse/DeletePortMapping.xml6
-rw-r--r--ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue2
-rw-r--r--ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml2
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue3
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml3
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue5
-rw-r--r--ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml2
-rw-r--r--ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue1
-rw-r--r--ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml1
-rw-r--r--ext/miniupnpc/testreplyparse/readme.txt7
-rwxr-xr-xext/miniupnpc/testupnpigd.py84
-rw-r--r--ext/miniupnpc/testupnpreplyparse.c96
-rwxr-xr-xext/miniupnpc/testupnpreplyparse.sh14
-rw-r--r--ext/miniupnpc/upnpc.c82
-rw-r--r--ext/miniupnpc/upnpcommands.c93
-rw-r--r--ext/miniupnpc/upnpreplyparse.c16
-rw-r--r--ext/misc/linux-old-glibc-compat.c18
-rw-r--r--ext/x64-salsa2012-asm/README.md6
-rw-r--r--ext/x64-salsa2012-asm/salsa2012.h16
-rw-r--r--ext/x64-salsa2012-asm/salsa2012.s4488
-rw-r--r--include/ZeroTierDebug.h114
-rw-r--r--include/ZeroTierOne.h1443
-rw-r--r--java/jni/Android.mk47
-rw-r--r--java/jni/Application.mk4
-rw-r--r--java/jni/ZT_jniutils.cpp194
-rw-r--r--java/jni/ZT_jniutils.h52
-rw-r--r--java/jni/com_zerotierone_sdk_Node.cpp779
-rw-r--r--java/jni/com_zerotierone_sdk_Node.h2
-rw-r--r--java/src/com/zerotier/sdk/DataStoreGetListener.java8
-rw-r--r--java/src/com/zerotier/sdk/Node.java49
-rw-r--r--java/src/com/zerotier/sdk/PacketSender.java4
-rw-r--r--java/src/com/zerotier/sdk/PathChecker.java45
-rw-r--r--java/src/com/zerotier/sdk/Peer.java16
-rw-r--r--java/src/com/zerotier/sdk/PeerPhysicalPath.java8
-rw-r--r--java/src/com/zerotier/sdk/PeerRole.java8
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkConfig.java23
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkRoute.java102
-rw-r--r--linux-build-farm/README.md8
-rw-r--r--linux-build-farm/amazon-2016.03/x64/Dockerfile13
-rwxr-xr-xlinux-build-farm/build.sh69
-rw-r--r--linux-build-farm/centos-6/x64/Dockerfile13
-rw-r--r--linux-build-farm/centos-6/x86/Dockerfile13
-rw-r--r--linux-build-farm/centos-7/x64/Dockerfile10
-rw-r--r--linux-build-farm/centos-7/x86/Dockerfile22
-rw-r--r--linux-build-farm/debian-jessie/x64/Dockerfile12
-rw-r--r--linux-build-farm/debian-jessie/x86/Dockerfile12
-rw-r--r--linux-build-farm/debian-stretch/x64/Dockerfile12
-rw-r--r--linux-build-farm/debian-stretch/x86/Dockerfile12
-rw-r--r--linux-build-farm/debian-wheezy/x64/Dockerfile12
-rw-r--r--linux-build-farm/debian-wheezy/x86/Dockerfile15
-rw-r--r--linux-build-farm/fedora-22/x64/Dockerfile10
-rw-r--r--linux-build-farm/fedora-22/x86/Dockerfile19
-rwxr-xr-xlinux-build-farm/make-apt-repos.sh16
-rwxr-xr-xlinux-build-farm/make-rpm-repos.sh64
-rw-r--r--linux-build-farm/ubuntu-trusty/x64/Dockerfile12
-rw-r--r--linux-build-farm/ubuntu-trusty/x86/Dockerfile12
-rw-r--r--linux-build-farm/ubuntu-wily/x64/Dockerfile12
-rw-r--r--linux-build-farm/ubuntu-wily/x86/Dockerfile12
-rw-r--r--linux-build-farm/ubuntu-xenial/x64/Dockerfile14
-rw-r--r--linux-build-farm/ubuntu-xenial/x86/Dockerfile14
-rw-r--r--macui/ZeroTier One.xcodeproj/project.pbxproj382
-rw-r--r--macui/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata (renamed from ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/contents.xcworkspacedata)2
-rw-r--r--macui/ZeroTier One/AboutViewController.h33
-rw-r--r--macui/ZeroTier One/AboutViewController.m57
-rw-r--r--macui/ZeroTier One/AboutViewController.xib31
-rw-r--r--macui/ZeroTier One/AppDelegate.h61
-rw-r--r--macui/ZeroTier One/AppDelegate.m378
-rw-r--r--macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json59
-rw-r--r--macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.pngbin0 -> 51309 bytes
-rw-r--r--macui/ZeroTier One/Assets.xcassets/Contents.json6
-rw-r--r--macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json21
-rw-r--r--macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.pngbin0 -> 22973 bytes
-rw-r--r--macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.pngbin0 -> 17234 bytes
-rw-r--r--macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json21
-rw-r--r--macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.pngbin0 -> 17234 bytes
-rw-r--r--macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.pngbin0 -> 22973 bytes
-rw-r--r--macui/ZeroTier One/AuthtokenCopy.h26
-rw-r--r--macui/ZeroTier One/AuthtokenCopy.m97
-rw-r--r--macui/ZeroTier One/Base.lproj/MainMenu.xib680
-rw-r--r--macui/ZeroTier One/Info.plist (renamed from ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Info.plist)24
-rw-r--r--macui/ZeroTier One/JoinNetworkViewController.h40
-rw-r--r--macui/ZeroTier One/JoinNetworkViewController.m184
-rw-r--r--macui/ZeroTier One/JoinNetworkViewController.xib82
-rw-r--r--macui/ZeroTier One/Network.h68
-rw-r--r--macui/ZeroTier One/Network.m337
-rw-r--r--macui/ZeroTier One/NetworkInfoCell.h50
-rw-r--r--macui/ZeroTier One/NetworkInfoCell.m85
-rw-r--r--macui/ZeroTier One/NetworkMonitor.h45
-rw-r--r--macui/ZeroTier One/NetworkMonitor.m253
-rw-r--r--macui/ZeroTier One/NodeStatus.h35
-rw-r--r--macui/ZeroTier One/NodeStatus.m40
-rw-r--r--macui/ZeroTier One/PreferencesViewController.h (renamed from node/NonCopyable.hpp)25
-rw-r--r--macui/ZeroTier One/PreferencesViewController.m112
-rw-r--r--macui/ZeroTier One/PreferencesViewController.xib33
-rw-r--r--macui/ZeroTier One/ServiceCom.h40
-rw-r--r--macui/ZeroTier One/ServiceCom.m516
-rw-r--r--macui/ZeroTier One/ShowNetworksViewController.h36
-rw-r--r--macui/ZeroTier One/ShowNetworksViewController.m181
-rw-r--r--macui/ZeroTier One/ShowNetworksViewController.xib371
-rw-r--r--macui/ZeroTier One/ZeroTierIcon.icns (renamed from ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/ZeroTierIcon.icns)bin125598 -> 125598 bytes
-rw-r--r--macui/ZeroTier One/about.html58
-rw-r--r--macui/ZeroTier One/main.m23
-rw-r--r--make-bsd.mk173
-rw-r--r--make-freebsd.mk65
-rw-r--r--make-linux.mk335
-rw-r--r--make-mac.mk126
-rw-r--r--node/Address.hpp102
-rw-r--r--node/Array.hpp107
-rw-r--r--node/AtomicCounter.hpp69
-rw-r--r--node/BinarySemaphore.hpp97
-rw-r--r--node/Buffer.hpp207
-rw-r--r--node/C25519.cpp2416
-rw-r--r--node/C25519.hpp73
-rw-r--r--node/Capability.cpp74
-rw-r--r--node/Capability.hpp493
-rw-r--r--node/CertificateOfMembership.cpp59
-rw-r--r--node/CertificateOfMembership.hpp170
-rw-r--r--node/CertificateOfOwnership.cpp72
-rw-r--r--node/CertificateOfOwnership.hpp247
-rw-r--r--node/Constants.hpp225
-rw-r--r--node/Credential.hpp65
-rw-r--r--node/DeferredPackets.cpp100
-rw-r--r--node/DeferredPackets.hpp85
-rw-r--r--node/Dictionary.hpp157
-rw-r--r--node/Hashtable.hpp58
-rw-r--r--node/Identity.cpp87
-rw-r--r--node/Identity.hpp110
-rw-r--r--node/IncomingPacket.cpp1965
-rw-r--r--node/IncomingPacket.hpp125
-rw-r--r--node/InetAddress.cpp233
-rw-r--r--node/InetAddress.hpp247
-rw-r--r--node/MAC.hpp118
-rw-r--r--node/Membership.cpp237
-rw-r--r--node/Membership.hpp285
-rw-r--r--node/MulticastGroup.hpp73
-rw-r--r--node/Multicaster.cpp267
-rw-r--r--node/Multicaster.hpp145
-rw-r--r--node/Mutex.hpp128
-rw-r--r--node/Network.cpp1488
-rw-r--r--node/Network.hpp344
-rw-r--r--node/NetworkConfig.cpp562
-rw-r--r--node/NetworkConfig.hpp447
-rw-r--r--node/NetworkController.hpp90
-rw-r--r--node/Node.cpp970
-rw-r--r--node/Node.hpp297
-rw-r--r--node/OutboundMulticast.cpp107
-rw-r--r--node/OutboundMulticast.hpp56
-rw-r--r--node/Packet.cpp1042
-rw-r--r--node/Packet.hpp575
-rw-r--r--node/Path.cpp16
-rw-r--r--node/Path.hpp336
-rw-r--r--node/Peer.cpp841
-rw-r--r--node/Peer.hpp634
-rw-r--r--node/Poly1305.cpp22
-rw-r--r--node/Poly1305.hpp13
-rw-r--r--node/README.md2
-rw-r--r--node/Revocation.cpp55
-rw-r--r--node/Revocation.hpp197
-rw-r--r--node/RuntimeEnvironment.hpp58
-rw-r--r--node/SHA512.cpp593
-rw-r--r--node/SHA512.hpp10
-rw-r--r--node/Salsa20.cpp61
-rw-r--r--node/Salsa20.hpp128
-rw-r--r--node/SelfAwareness.cpp163
-rw-r--r--node/SelfAwareness.hpp28
-rw-r--r--node/SharedPtr.hpp82
-rw-r--r--node/Switch.cpp716
-rw-r--r--node/Switch.hpp145
-rw-r--r--node/Tag.cpp55
-rw-r--r--node/Tag.hpp210
-rw-r--r--node/Topology.cpp562
-rw-r--r--node/Topology.hpp348
-rw-r--r--node/Trace.cpp540
-rw-r--r--node/Trace.hpp176
-rw-r--r--node/Utils.cpp237
-rw-r--r--node/Utils.hpp410
-rw-r--r--node/World.hpp162
-rw-r--r--objects.mk24
-rw-r--r--one.cpp865
-rw-r--r--osdep/Arp.cpp10
-rw-r--r--osdep/Arp.hpp10
-rw-r--r--osdep/BSDEthernetTap.cpp117
-rw-r--r--osdep/BSDEthernetTap.hpp15
-rw-r--r--osdep/BackgroundResolver.cpp121
-rw-r--r--osdep/BackgroundResolver.hpp118
-rw-r--r--osdep/Binder.hpp463
-rw-r--r--osdep/BlockingQueue.hpp107
-rw-r--r--osdep/Http.cpp45
-rw-r--r--osdep/Http.hpp43
-rw-r--r--osdep/LinuxEthernetTap.cpp192
-rw-r--r--osdep/LinuxEthernetTap.hpp18
-rw-r--r--osdep/ManagedRoute.cpp344
-rw-r--r--osdep/ManagedRoute.hpp87
-rw-r--r--osdep/NeighborDiscovery.cpp272
-rw-r--r--osdep/NeighborDiscovery.hpp84
-rw-r--r--osdep/OSUtils.cpp322
-rw-r--r--osdep/OSUtils.hpp122
-rw-r--r--osdep/OSXEthernetTap.cpp108
-rw-r--r--osdep/OSXEthernetTap.hpp15
-rw-r--r--osdep/Phy.hpp39
-rw-r--r--osdep/PortMapper.cpp64
-rw-r--r--osdep/PortMapper.hpp10
-rw-r--r--osdep/TestEthernetTap.hpp161
-rw-r--r--osdep/Thread.hpp50
-rw-r--r--osdep/WindowsEthernetTap.cpp141
-rw-r--r--osdep/WindowsEthernetTap.hpp29
-rw-r--r--rule-compiler/README.md8
-rw-r--r--rule-compiler/cli.js47
-rw-r--r--rule-compiler/examples/capabilities-and-tags.ztrules40
-rw-r--r--rule-compiler/package.json18
-rw-r--r--rule-compiler/rule-compiler.js904
-rw-r--r--selftest.cpp363
-rw-r--r--service/ControlPlane.cpp628
-rw-r--r--service/ControlPlane.hpp102
-rw-r--r--service/OneService.cpp2545
-rw-r--r--service/OneService.hpp59
-rw-r--r--service/README.md200
-rw-r--r--service/SoftwareUpdater.cpp439
-rw-r--r--service/SoftwareUpdater.hpp217
-rw-r--r--tcp-proxy/Makefile7
-rw-r--r--tcp-proxy/README.md4
-rw-r--r--tcp-proxy/tcp-proxy.cpp317
-rw-r--r--version.h30
-rw-r--r--windows/TapDriver6/TapDriver6.vcxproj45
-rw-r--r--windows/TapDriver6/tap-windows.h4
-rw-r--r--windows/WinUI/APIHandler.cs460
-rw-r--r--windows/WinUI/AboutView.xaml35
-rw-r--r--windows/WinUI/AboutView.xaml.cs35
-rw-r--r--windows/WinUI/App.xaml2
-rw-r--r--windows/WinUI/App.xaml.cs8
-rw-r--r--windows/WinUI/CentralAPI.cs245
-rw-r--r--windows/WinUI/CentralLogin.cs30
-rw-r--r--windows/WinUI/CentralNetwork.cs48
-rw-r--r--windows/WinUI/CentralServer.cs23
-rw-r--r--windows/WinUI/CentralToken.cs21
-rw-r--r--windows/WinUI/CentralUser.cs51
-rw-r--r--windows/WinUI/ISwitchable.cs13
-rw-r--r--windows/WinUI/JoinNetworkView.xaml16
-rw-r--r--windows/WinUI/JoinNetworkView.xaml.cs126
-rw-r--r--windows/WinUI/MainWindow.xaml.cs78
-rw-r--r--windows/WinUI/NetworkInfoView.xaml24
-rw-r--r--windows/WinUI/NetworkInfoView.xaml.cs124
-rw-r--r--windows/WinUI/NetworkListView.xaml (renamed from windows/WinUI/MainWindow.xaml)66
-rw-r--r--windows/WinUI/NetworkListView.xaml.cs85
-rw-r--r--windows/WinUI/NetworkMonitor.cs203
-rw-r--r--windows/WinUI/NetworkNameGenerator.cs201
-rw-r--r--windows/WinUI/NetworkRoute.cs42
-rw-r--r--windows/WinUI/NetworksPage.xaml.cs73
-rw-r--r--windows/WinUI/OnboardProcess/CreateAccount.xaml38
-rw-r--r--windows/WinUI/OnboardProcess/CreateAccount.xaml.cs66
-rw-r--r--windows/WinUI/OnboardProcess/CreateOrJoin.xaml49
-rw-r--r--windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs98
-rw-r--r--windows/WinUI/OnboardProcess/EnterToken.xaml29
-rw-r--r--windows/WinUI/OnboardProcess/EnterToken.xaml.cs57
-rw-r--r--windows/WinUI/OnboardProcess/Finished.xaml27
-rw-r--r--windows/WinUI/OnboardProcess/Finished.xaml.cs37
-rw-r--r--windows/WinUI/OnboardProcess/LogIn.xaml35
-rw-r--r--windows/WinUI/OnboardProcess/LogIn.xaml.cs57
-rw-r--r--windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml27
-rw-r--r--windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs48
-rw-r--r--windows/WinUI/PageSwitcher.xaml13
-rw-r--r--windows/WinUI/PageSwitcher.xaml.cs56
-rw-r--r--windows/WinUI/PreferencesView.xaml30
-rw-r--r--windows/WinUI/PreferencesView.xaml.cs74
-rw-r--r--windows/WinUI/Properties/Resources.Designer.cs52
-rw-r--r--windows/WinUI/Properties/Resources.resx17
-rw-r--r--windows/WinUI/Resources/ZeroTierIcon.icobin0 -> 45451 bytes
-rw-r--r--windows/WinUI/Simple Styles.xaml9
-rw-r--r--windows/WinUI/Switcher.cs24
-rw-r--r--windows/WinUI/Themes/Generic.xaml1
-rw-r--r--windows/WinUI/ToolbarItem.xaml62
-rw-r--r--windows/WinUI/ToolbarItem.xaml.cs362
-rw-r--r--windows/WinUI/WinUI.csproj113
-rw-r--r--windows/WinUI/ZeroTierNetwork.cs474
-rw-r--r--windows/WinUI/packages.config3
-rw-r--r--windows/ZeroTierOne.sln222
-rw-r--r--windows/ZeroTierOne/ZeroTierOne.vcxproj737
-rw-r--r--windows/ZeroTierOne/ZeroTierOne.vcxproj.filters1055
-rw-r--r--windows/ZeroTierOneSDK.sln28
-rw-r--r--windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj257
-rw-r--r--windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters240
-rw-r--r--windows/ZeroTierOneSDK/dllmain.cpp22
-rw-r--r--windows/ZeroTierOneSDK/targetver.h8
-rw-r--r--windows/copyutil/App.config6
-rw-r--r--windows/copyutil/Program.cs40
-rw-r--r--windows/copyutil/Properties/AssemblyInfo.cs36
-rw-r--r--windows/copyutil/copyutil.csproj60
-rw-r--r--zerotier-one.spec65
682 files changed, 119020 insertions, 39233 deletions
diff --git a/.gitignore b/.gitignore
index 8bf3c55b..49733d1f 100755
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,8 @@
.DS_Store
.Apple*
Thumbs.db
+@eaDir
+._*
# Windows build droppings
/windows/ZeroTierOne.sdf
@@ -25,10 +27,20 @@ Thumbs.db
/windows/Release
/windows/WebUIWrapper/bin
/windows/WebUIWrapper/obj
+/windows/lib
/ext/installfiles/windows/ZeroTier One-SetupFiles
/ext/installfiles/windows/Prerequisites
/ext/installfiles/windows/*-cache
/ZeroTier One.msi
+/windows/.vs
+*.vcxproj.backup
+/windows/TapDriver6/Win7Debug
+/windows/TapDriver6/win7Release
+/windows/*.db
+/windows/*.opendb
+enc_temp_folder
+/windows/copyutil/bin
+/windows/copyutil/obj
# *nix/Mac build droppings
/build-*
@@ -49,7 +61,7 @@ zt1-src.tar.gz
*.pid
*.pkg
*.o
-*.a
+/*.a
*.dylib
*.so
*.so.*
@@ -59,16 +71,15 @@ zt1-src.tar.gz
*.rpm
*.autosave
*.tmp
-doc/*.1
-doc/*.2
-doc/*.8
.depend
node_modules
+zt1_update_*
debian/files
debian/zerotier-one
debian/zerotier-one*.debhelper
debian/*.log
debian/zerotier-one.substvars
+root-watcher/config.json
# Java/Android/JNI build droppings
java/obj/
@@ -83,3 +94,25 @@ windows/WinUI/obj/
windows/WinUI/bin/
windows/ZeroTierOne/Debug/
/ext/installfiles/windows/chocolatey/zerotier-one/*.nupkg
+
+# Miscellaneous mac/Xcode droppings
+.DS_Store
+.Trashes
+*.swp
+*~.nib
+DerivedData/
+build/
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+*.xccheckout
+xcuserdata/
+ext/librethinkdbxx/build
+.vscode
+__pycache__
+*~
diff --git a/AUTHORS.md b/AUTHORS.md
index aa9e9111..043ff001 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -25,13 +25,13 @@
## Third-Party Code
-These are included in ext/ for platforms that do not have them available in common repositories. Otherwise they may be linked and the package may ship with them as dependencies.
+ZeroTier includes the following third party code, either in ext/ or incorporated into the ZeroTier core.
* LZ4 compression algorithm by Yann Collet
- * Files: ext/lz4/*
+ * Files: node/Packet.cpp (bundled within anonymous namespace)
* Home page: http://code.google.com/p/lz4/
- * License grant: BSD attribution
+ * License grant: BSD 2-clause
* http-parser by Joyent, Inc. (many authors)
@@ -39,11 +39,11 @@ These are included in ext/ for platforms that do not have them available in comm
* Home page: https://github.com/joyent/http-parser/
* License grant: MIT/Expat
- * json-parser by James McLaughlin
+ * C++11 json (nlohmann/json) by Niels Lohmann
- * Files: ext/json-parser/*
- * Home page: https://github.com/udp/json-parser/
- * License grant: BSD attribution
+ * Files: ext/json/*
+ * Home page: https://github.com/nlohmann/json
+ * License grant: MIT
* TunTapOSX by Mattias Nissler
@@ -55,26 +55,19 @@ These are included in ext/ for platforms that do not have them available in comm
* tap-windows6 by the OpenVPN project
* Files: windows/TapDriver6/*
- * Home page:
- https://github.com/OpenVPN/tap-windows6/
+ * Home page: https://github.com/OpenVPN/tap-windows6/
* License grant: GNU GPL v2
* ZeroTier Modifications: change name of driver to ZeroTier, add ioctl() to get L2 multicast memberships (source is in ext/ and modifications inherit GPL)
- * Salsa20 stream cipher, Curve25519 elliptic curve cipher, Ed25519
- digital signature algorithm, and Poly1305 MAC algorithm, all by
- Daniel J. Bernstein
+ * Salsa20 stream cipher, Curve25519 elliptic curve cipher, Ed25519 digital signature algorithm, and Poly1305 MAC algorithm, all by Daniel J. Bernstein
- * Files:
- node/Salsa20.hpp
- node/C25519.hpp
- node/Poly1305.hpp
+ * Files: node/Salsa20.* node/C25519.* node/Poly1305.*
* Home page: http://cr.yp.to/
* License grant: public domain
+ * ZeroTier Modifications: slight cryptographically-irrelevant modifications for inclusion into ZeroTier core
* MiniUPNPC and libnatpmp by Thomas Bernard
- * Files:
- ext/libnatpmp/*
- ext/miniupnpc/*
+ * Files: ext/libnatpmp/* ext/miniupnpc/*
* Home page: http://miniupnp.free.fr/
* License grant: BSD attribution no-endorsement
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..fff7808e
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,12 @@
+# CMake build script for libzerotiercore.a
+
+cmake_minimum_required (VERSION 2.8)
+project (zerotiercore)
+
+set (PROJ_DIR ${PROJECT_SOURCE_DIR})
+set (ZT_DEFS -std=c++11)
+
+file(GLOB core_src_glob ${PROJ_DIR}/node/*.cpp)
+add_library(zerotiercore STATIC ${core_src_glob})
+
+target_compile_options(zerotiercore PRIVATE ${ZT_DEFS})
diff --git a/COPYING b/COPYING
index 23d42dfa..8008d3e0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,5 +1,5 @@
ZeroTier One, an endpoint server for the ZeroTier virtual network layer.
-Copyright © 2011–2016 ZeroTier, Inc.
+Copyright © 2011–2018 ZeroTier, Inc.
ZeroTier One is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 00000000..e729c334
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,84 @@
+#!/usr/bin/env groovy
+
+node('master') {
+ checkout scm
+
+ def changelog = getChangeLog currentBuild
+
+ mattermostSend "Building ${env.JOB_NAME} #${env.BUILD_NUMBER} \n Change Log: \n ${changelog}"
+}
+
+parallel 'centos7': {
+ node('centos7') {
+ try {
+ checkout scm
+
+ stage('Build Centos 7') {
+ sh 'make -f make-linux.mk'
+ }
+ }
+ catch (err) {
+ currentBuild.result = "FAILURE"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Centos 7 (<${env.BUILD_URL}|Open>)"
+
+ throw err
+ }
+ }
+}, 'android-ndk': {
+ node('android-ndk') {
+ try {
+ checkout scm
+
+ stage('Build Android NDK') {
+ sh "/android/android-ndk-r15b/ndk-build -C $WORKSPACE/java ZT1=${WORKSPACE}"
+ }
+ }
+ catch (err) {
+ currentBuild.result = "FAILURE"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Android NDK (<${env.BUILD_URL}|Open>)"
+
+ throw err
+ }
+ }
+}, 'macOS': {
+ node('macOS') {
+ try {
+ checkout scm
+
+ stage('Build macOS') {
+ sh 'make -f make-mac.mk'
+ }
+
+ stage('Build macOS UI') {
+ sh 'cd macui && xcodebuild -target "ZeroTier One" -configuration Debug'
+ }
+ }
+ catch (err) {
+ currentBuild.result = "FAILURE"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on macOS (<${env.BUILD_URL}|Open>)"
+
+ throw err
+ }
+ }
+}, 'windows': {
+ node('windows') {
+ try {
+ checkout scm
+
+ stage('Build Windows') {
+ bat '''CALL "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat" amd64
+git clean -dfx
+msbuild windows\\ZeroTierOne.sln
+'''
+ }
+ }
+ catch (err) {
+ currentBuild.result = "FAILURE"
+ mattermostSend color: '#ff0000', message: "${env.JOB_NAME} broken on Windows (<${env.BUILD_URL}|Open>)"
+
+ throw err
+ }
+ }
+}
+
+mattermostSend color: "#00ff00", message: "${env.JOB_NAME} #${env.BUILD_NUMBER} Complete (<${env.BUILD_URL}|Show More...>)"
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 00000000..8fd274be
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,37 @@
+ZeroTier One - Network Virtualization Everywhere
+Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
+
+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 <http://www.gnu.org/licenses/>.
+
+See LICENSE.GPL-3 for the full GNU GPL v3 license.
+
+--
+
+You can be released from the requirements of the license by purchasing
+a commercial license. Buying such a license is mandatory as soon as you
+develop commercial closed-source software that incorporates or links
+directly against ZeroTier software without disclosing the source code
+of your own application.
+
+--
+
+The above license does not apply to third party code included with or
+linked against by ZeroTier software. See the third party code section
+of the AUTHORS.md for an index of third party software included in
+this software repository.
+
+Licenses for third party code are all relatively permissive: MIT,
+BSD, and public domain. The only exception is the tap-windows driver
+which is under the GPLv2, but this is only needed to produce the
+binary tap device driver used by the ZeroTier service on Windows.
diff --git a/Makefile b/Makefile
index d8eb7edf..144225fc 100644
--- a/Makefile
+++ b/Makefile
@@ -11,10 +11,16 @@ ifeq ($(OSTYPE),Linux)
endif
ifeq ($(OSTYPE),FreeBSD)
- include make-freebsd.mk
+ CC=clang
+ CXX=clang++
+ ZT_BUILD_PLATFORM=7
+ include make-bsd.mk
endif
ifeq ($(OSTYPE),OpenBSD)
- include make-freebsd.mk
+ CC=egcc
+ CXX=eg++
+ ZT_BUILD_PLATFORM=9
+ include make-bsd.mk
endif
ifeq ($(OSTYPE),NetBSD)
diff --git a/OFFICIAL-RELEASE-STEPS.md b/OFFICIAL-RELEASE-STEPS.md
index 37e67914..4db5169f 100644
--- a/OFFICIAL-RELEASE-STEPS.md
+++ b/OFFICIAL-RELEASE-STEPS.md
@@ -15,6 +15,7 @@ The version must be incremented in all of the following files:
/ext/installfiles/mac/ZeroTier One.pkgproj
/ext/installfiles/windows/chocolatey/zerotier-one.nuspec
/ext/installfiles/windows/ZeroTier One.aip
+ /windows/WinUI/AboutView.xaml
The final .AIP file can only be edited on Windows with [Advanced Installer Enterprise](http://www.advancedinstaller.com/). In addition to incrementing the version be sure that a new product code is generated. (The "upgrade code" GUID on the other hand must never change.)
@@ -30,22 +31,7 @@ You will need [Packages](http://s.sudre.free.fr/Software/Packages/about.html) an
## Linux
-Mount the GPG key for *contact@zerotier.com* and then on an x86_64 box with a recent version of Docker and an Internet connection run:
-
- make distclean
- cd linux-build-farm
- ./build.sh
-
-This will build i386 and x86_64 packages. Now ssh into our build Raspberry Pi and type `make debian` there to build the Raspbian armhf package. Copy it to `debian-jessie/` inside `linux-build-farm` so that it will be included in the repositories we generate. Now generate the YUM and APT repos:
-
- rm -rf ~/.aptly*
- rm -rf /tmp/zt-rpm-repo
- ./make-apt-repos.sh
- ./make-rpm-repos.sh
-
-This will require the passphrase for *contact@zerotier.com*.
-
-The contents of ~/.aptly/public must be published as `debian/` on `download.zerotier.com`. The contents of /tmp/zt-rpm-repo are published as `redhat/` on same.
+See `LinuxBuild` environment on `linux-build` VM and use: `chroots/mount-build.sh`, `chroots/build.sh`, and the scripts in `build/` to make APT and RPM repositories.
## Windows
diff --git a/README.md b/README.md
index f9ee85ee..37f04982 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +1,65 @@
ZeroTier - A Planetary Ethernet Switch
======
-ZeroTier is a software-based managed Ethernet switch for planet Earth.
+ZeroTier is a smart programmable Ethernet switch for planet Earth.
-It erases the LAN/WAN distinction and makes VPNs, tunnels, proxies, and other kludges arising from the inflexible nature of physical networks obsolete. Everything is encrypted end-to-end and traffic takes the most direct (peer to peer) path available.
+It replaces the physical LAN/WAN boundary with a virtual one, allowing devices of any type at any location to be managed as if they all reside in the same cloud region or data center. All traffic is encrypted end-to-end and takes the most direct path available for minimum latency and maximum performance. The goals and design of ZeroTier are inspired by among other things the original [Google BeyondCorp](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/43231.pdf) paper and the [Jericho Forum](https://en.wikipedia.org/wiki/Jericho_Forum).
-This repository contains ZeroTier One, a service that provides ZeroTier network connectivity to devices running Windows, Mac, Linux, iOS, Android, and FreeBSD and makes joining virtual networks as easy as joining IRC or Slack channels. It also contains the OS-independent core ZeroTier protocol implementation in [node/](node/).
-
-Visit [ZeroTier's site](https://www.zerotier.com/) for more information and [pre-built binary packages](https://www.zerotier.com/download.shtml). Apps for Android and iOS are available for free in the Google Play and Apple app stores.
+Visit [ZeroTier's site](https://www.zerotier.com/?pk_campaign=github_ZeroTierOne) for more information and [pre-built binary packages](https://www.zerotier.com/download.shtml?pk_campaign=github_ZeroTierOne). Apps for Android and iOS are available for free in the Google Play and Apple app stores.
### Getting Started
-ZeroTier's basic operation is easy to understand. Devices have 10-digit *ZeroTier addresses* like `89e92ceee5` and networks have 16-digit network IDs like `8056c2e21c000001`. All it takes for a device to join a network is its 16-digit ID, and all it takes for a network to authorize a device is its 10-digit address. Everything else is automatic.
-
-A "device" can be anything really: desktops, laptops, phones, servers, VMs/VPSes, containers, and even (soon) apps.
-
-For testing we provide a public virtual network called *Earth* with network ID `8056c2e21c000001`. On Linux and Mac you can do this with:
-
- sudo zerotier-cli join 8056c2e21c000001
-
-Now wait about 30 seconds and check your system with `ip addr list` or `ifconfig`. You'll see a new interface whose name starts with *zt* and it should quickly get an IPv4 and an IPv6 address. Once you see it get an IP, try pinging `earth.zerotier.net` at `29.209.112.93`. If you've joined Earth from more than one system, try pinging your other machine.
-
-*(IPv4 addresses for Earth are assigned from the block 28.0.0.0/7, which is not a part of the public Internet but is non-standard for private networks. It's used to avoid IP conflicts during testing. Your networks can run any IP addressing scheme you want.)*
-
-If you don't want to belong to a giant Ethernet party line anymore, just type:
-
- sudo zerotier-cli leave 8056c2e21c000001
-
-The *zt* interface will disappear. You're no longer on the network.
-
-To create networks of your own you'll need a network controller. You can use [our hosted controller at my.zerotier.com](https://my.zerotier.com) which is free for up to 100 devices on an unlimited number of networks, or you can build your own controller and run it through its local JSON API. See [README.md in controller/](controller/) for more information.
-
-### Building from Source
-
-For Mac, Linux, and BSD, just type "make" (or "gmake" on BSD). You won't need much installed; here are the requirements for various platforms:
-
- * **Mac**: Xcode command line tools. It should build on OSX 10.7 or newer.
- * **Linux**: gcc/g++ (4.9 or newer recommended) or clang/clang++ (3.4 or newer recommended) Makefile will use clang by default if available. The Linux build will auto-detect the presence of development headers for *json-parser*, *http-parser*, *li8bnatpmp*, and *libminiupnpc* and will link against the system libraries for these if they are present and recent enough. Otherwise the bundled versions in [ext/](ext/) will be used. Type `make install` to install the binaries and other files on the system, though this will not create init.d or systemd links.
- * **FreeBSD/OpenBSD/NetBSD**: C++ compiler (G++ usually) and GNU make (gmake).
-
-Each supported platform has its own *make-XXX.mk* file that contains the actual make rules for the platform. The right .mk file is included by the main Makefile based on the GNU make *OSTYPE* variable. Take a look at the .mk file for your platform for other targets, debug build rules, etc.
+Everything in the ZeroTier world is controlled by two types of identifier: 40-bit/10-digit *ZeroTier addresses* and 64-bit/16-digit *network IDs*. A ZeroTier address identifies a node or "device" (laptop, phone, server, VM, app, etc.) while a network ID identifies a virtual Ethernet network that can be joined by devices.
+
+Another way of thinking about it is that ZeroTier addresses are port numbers on a giant planetary-sized smart switch while network IDs are VLANs to which these ports can be assigned. For more details read about VL1 and VL2 in [the ZeroTier manual](https://www.zerotier.com/manual.shtml).
+
+*Network controllers* are ZeroTier nodes that act as access control certificate authorities and configuration managers for virtual networks. The first 40 bits (or 10 digits) of a network ID is the ZeroTier address of its controller. You can create networks with our [hosted controllers](https://my.zerotier.com/) and web UI/API or [host your own](controller/) if you don't mind posting some JSON configuration info or writing a script to do so.
+
+### Project Layout
+
+ - `artwork/`: icons, logos, etc.
+ - `attic/`: old stuff and experimental code that we want to keep around for reference.
+ - `controller/`: the reference network controller implementation, which is built and included by default on desktop and server build targets.
+ - `debian/`: files for building Debian packages on Linux.
+ - `doc/`: manual pages and other documentation.
+ - `ext/`: third party libraries, binaries that we ship for convenience on some platforms (Mac and Windows), and installation support files.
+ - `include/`: include files for the ZeroTier core.
+ - `java/`: a JNI wrapper used with our Android mobile app. (The whole Android app is not open source but may be made so in the future.)
+ - `macui/`: a Macintosh menu-bar app for controlling ZeroTier One, written in Objective C.
+ - `node/`: the ZeroTier virtual Ethernet switch core, which is designed to be entirely separate from the rest of the code and able to be built as a stand-alone OS-independent library. Note to developers: do not use C++11 features in here, since we want this to build on old embedded platforms that lack C++11 support. C++11 can be used elsewhere.
+ - `osdep/`: code to support and integrate with OSes, including platform-specific stuff only built for certain targets.
+ - `service/`: the ZeroTier One service, which wraps the ZeroTier core and provides VPN-like connectivity to virtual networks for desktops, laptops, servers, VMs, and containers.
+ - `tcp-proxy/`: TCP proxy code run by ZeroTier, Inc. to provide TCP fallback (this will die soon!).
+ - `windows/`: Visual Studio solution files, Windows service code for ZeroTier One, and the Windows task bar app UI.
+
+The base path contains the ZeroTier One service main entry point (`one.cpp`), self test code, makefiles, etc.
+
+### Build and Platform Notes
+
+To build on Mac and Linux just type `make`. On FreeBSD and OpenBSD `gmake` (GNU make) is required and can be installed from packages or ports. For Windows there is a Visual Studio solution in `windows/'.
+
+ - **Mac**
+ - Xcode command line tools for OSX 10.7 or newer are required.
+ - Tap device driver kext source is in `ext/tap-mac` and a signed pre-built binary can be found in `ext/bin/tap-mac`. You should not need to build it yourself. It's a fork of [tuntaposx](http://tuntaposx.sourceforge.net) with device names changed to `zt#`, support for a larger MTU, and tun functionality removed.
+ - **Linux**
+ - The minimum compiler versions required are GCC/G++ 4.9.3 or CLANG/CLANG++ 3.4.2.
+ - Linux makefiles automatically detect and prefer clang/clang++ if present as it produces smaller and slightly faster binaries in most cases. You can override by supplying CC and CXX variables on the make command line.
+ - CentOS 7 ships with a version of GCC/G++ that is too old, but a new enough version of CLANG can be found in the *epel* repositories. Type `yum install epel-release` and then `yum install clang` to build there.
+ - **Windows**
+ - Windows 7 or newer is supported. This *may* work on Vista but isn't officially supported there. It will not work on Windows XP.
+ - We build with Visual Studio 2015. Older versions may not work. Clang or MinGW will also probably work but may require some makefile hacking.
+ - Pre-built signed Windows drivers are included in `ext/bin/tap-windows-ndis6`. The MSI files found there will install them on 32-bit and 64-bit systems. We don't recommend trying to build Windows drivers from scratch unless you know what you're doing. One does not simply "build" a Windows driver.
+ - **FreeBSD**
+ - Tested most recently on FreeBSD-11. Older versions may work but we're not sure.
+ - GCC/G++ 4.9 and gmake are required. These can be installed from packages or ports. Type `gmake` to build.
+ - **OpenBSD**
+ - There is a limit of four network memberships on OpenBSD as there are only four tap devices (`/dev/tap0` through `/dev/tap3`). We're not sure if this can be increased.
+ - OpenBSD lacks `getifmaddrs` (or any equivalent method) to get interface multicast memberships. As a result multicast will only work on OpenBSD for ARP and NDP (IP/MAC lookup) and not for other purposes.
+ - Only tested on OpenBSD 6.0. Older versions may not work.
+ - GCC/G++ 4.9 and gmake are required and can be installed using `pkg_add` or from ports. They get installed in `/usr/local/bin` as `egcc` and `eg++` and our makefile is pre-configured to use them on OpenBSD.
Typing `make selftest` will build a *zerotier-selftest* binary which unit tests various internals and reports on a few aspects of the build environment. It's a good idea to try this on novel platforms or architectures.
-Windows, of course, is special. We build for Windows with Microsoft Visual Studio 2012 on Windows 7. A solution file is located in the *windows/* subfolder. Newer versions of Visual Studio (and Windows) may work but haven't been tested. Older versions almost certainly will not, since they lack things like *stdint.h* and certain STL features. MinGW or other ports of gcc/clang to Windows should also work but haven't been tested.
-
-32 and 64 bit X86 and ARM (e.g. Raspberry Pi, Android) are officially supported. Community members have built for MIPS and Sparc without issues.
-
### Running
Running *zerotier-one* with -h will show help.
@@ -62,7 +75,7 @@ The service is controlled via the JSON API, which by default is available at 127
Here's where home folders live (by default) on each OS:
* **Linux**: `/var/lib/zerotier-one`
- * **FreeBSD**: `/var/db/zerotier-one`
+ * **FreeBSD** / **OpenBSD**: `/var/db/zerotier-one`
* **Mac**: `/Library/Application Support/ZeroTier/One`
* **Windows**: `\ProgramData\ZeroTier\One` (That's for Windows 7. The base 'shared app data' folder might be different on different Windows versions.)
@@ -88,7 +101,7 @@ ZeroTier One peers will automatically locate each other and communicate directly
Users behind certain types of firewalls and "symmetric" NAT devices may not able able to connect to external peers directly at all. ZeroTier has limited support for port prediction and will *attempt* to traverse symmetric NATs, but this doesn't always work. If P2P connectivity fails you'll be bouncing UDP packets off our relay servers resulting in slower performance. Some NAT router(s) have a configurable NAT mode, and setting this to "full cone" will eliminate this problem. If you do this you may also see a magical improvement for things like VoIP phones, Skype, BitTorrent, WebRTC, certain games, etc., since all of these use NAT traversal techniques similar to ours.
-If you're interested, there's a [technical deep dive about NAT traversal on our blog](https://www.zerotier.com/blog/?p=226). A troubleshooting tool to help you diagnose NAT issues is planned for the future as are uPnP/IGD/NAT-PMP and IPv6 transport.
+If you're interested, there's a [technical deep dive about NAT traversal on our blog](https://www.zerotier.com/blog/?p=226?pk_campaign=github_ZeroTierOne). A troubleshooting tool to help you diagnose NAT issues is planned for the future as are uPnP/IGD/NAT-PMP and IPv6 transport.
If a firewall between you and the Internet blocks ZeroTier's UDP traffic, you will fall back to last-resort TCP tunneling to rootservers over port 443 (https impersonation). This will work almost anywhere but is *very slow* compared to UDP or direct peer to peer connectivity.
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
new file mode 100644
index 00000000..a73671d7
--- /dev/null
+++ b/RELEASE-NOTES.md
@@ -0,0 +1,173 @@
+ZeroTier Release Notes
+======
+
+# 2018-04-17 -- Version 1.2.6
+
+ * Features and Core Improvements
+ * Path selection has been overhauled to improve path stability, simplify code, and prepare for multi-path and trunking in the next major release.
+ * This version introduces remote tracing for remote diagnostics. Network controllers can set a node (usually the controller itself) to receive remote tracing events from all members of the network or from select members. Events are only sent if they pertain to a given network for security reasons.
+ * Multicast replication can now be done by designated multicast replicators on a network (flagged as such at the controller) rather than by the sender. Most users won't want this, but it's useful for specialized use cases on hub-and-spoke networks and for low-power devices.
+ * Cryptographic performance improvements on several platforms.
+ * Multithreaded performance improvements throughout the code base, including the use of an inline lightweight spinlock for low-contention resources.
+ * Bugs fixed
+ * Disappearing routes on Mac (GitHub issue #600)
+ * Route flapping and path instability in some dual-stack V4/V6 networks
+ * Blacklist (in local.conf) doesn't work reliably (GitHub issue #656)
+ * Connection instabilities due to unsigned integer overflows in timing comparisons (use int64_t instead of uint64_t)
+ * Binaries don't run on some older or lower-end 32-bit ARM chips (build problem)
+ * ARM NEON crypto code crashes (build problem)
+ * Fixed some lock ordering issues revealed by "valgrind" tool
+ * The "zerotier-idtool" command could not be accessed from "zerotier-one" via command line switch
+ * Leaking sockets on some platforms when uPnP/NAT-PMP is enabled
+ * Fixed two very rare multithreading issues that were only observed on certain systems
+ * Platform-Specific Changes
+ * MacOS
+ * Installer now loads the kernel extension right away so that High Sierra users will see the prompt to authorize it. This is done in the "Security & Privacy" preference pane and must be done driectly on the console (not via remote desktop). On High Sierra and newer kexts must be authorized at the console via security settings system preferences pane.
+ * Windows
+ * The Windows installer should now install the driver without requiring a special prompt in most cases. This should make it easier for our packages to be accepted into and updated in the Chocolatey repository and should make it easier to perform remote installs across groups of machines using IT management and provisioning tools.
+ * The Windows official packages are now signed with an EV certificate (with hardware key).
+ * The Windows UI can now log into ZeroTier Central and join networks via the Central API.
+ * The `zerotier-idtool` command should now work on Windows without ugly hacks.
+ * Upgraded the installer version.
+ * Made a few changes to hopefully fix sporadic "will not uninstall" problems, though we cannot duplicate these issues ourselves.
+ * Linux
+ * Device names are now generated deterministically based on network IDs for all newly joined networks.
+ * Android
+ * Multicast now works on Android in most cases! Android apps can send and receive multicast and subscribe to multicast group IPs. Note that in some cases the app must bind to the specific correct interface for this to work.
+ * IPv6 can be disabled in UI for cases where it causes problems.
+
+# 2017-04-20 -- Version 1.2.4
+
+ * Managed routes are now only bifurcated for the default route. This is a change in behavior, though few people will probably notice. Bifurcating all managed routes was causing more trouble than it was worth for most users.
+ * Up to 2X crypto speedup on x86-64 (except Windows, which will take some porting) and 32-bit ARM platforms due to integration of fast assembly language implementations of Salsa20/12 from the [supercop](http://bench.cr.yp.to/supercop.html) code base. These were written by Daniel J. Bernstein and are in the public domain. My Macbook Pro (Core i5 2.8ghz) now does almost 1.5GiB/sec Salsa20/12 per core and a Raspberry Pi got a 2X boost. 64-bit ARM support and Windows support will take some work but should not be too hard.
+ * Refactored code that manages credentials to greatly reduce memory use in most cases. This may also result in a small performance improvement.
+ * Reworked and simplified path selection and priority logic to fix path instability and dead path persistence edge cases. There have been some sporadic reports of persistent path instabilities and dead paths hanging around that take minutes to resolve. These have proven difficult to reproduce in house, but hopefully this will fix them. In any case it seems to speed up path establishment in our tests and it makes the code simpler and more readable.
+ * Eliminated some unused cruft from the code around path management and in the peer class.
+ * Fixed an issue causing build problems on some MIPS architecture systems.
+ * Fixed Windows forgetting routes on sleep/wake or in some other circumstances. (GitHub issue #465)
+
+# 2017-03-17 -- Version 1.2.2
+
+ * A bug causing unreliable multicast propagation (GitHub issue #461).
+ * A crash in ARM binaries due to a build chain and flags problem.
+ * A bug in the network controller preventing members from being listed (GitHub issue #460).
+
+# 2017-03-14 -- Version 1.2.0
+
+Version 1.2.0 is a major milestone release representing almost nine months of work. It includes our rules engine for distributed network packet filtering and security monitoring, federated roots, and many other architectural and UI improvements and bug fixes.
+
+## New Features in 1.2.0
+
+### The ZeroTier Rules Engine
+
+The largest new feature in 1.2.0, and the product of many months of work, is our advanced network rules engine. With this release we achieve traffic control, security monitoring, and micro-segmentation capability on par with many enterprise SDN solutions designed for use in advanced data centers and corporate networks.
+
+Rules allow you to filter packets on your network and vector traffic to security observers. Security observation can be performed in-band using REDIRECT or out of band using TEE.
+
+Tags and capabilites provide advanced methods for implementing fine grained permission structures and micro-segmentation schemes without bloating the size and complexity of your rules table.
+
+See the [rules engine announcement blog post](https://www.zerotier.com/blog/?p=927) for an in-depth discussion of theory and implementation. The [manual](https://www.zerotier.com/manual.shtml) contains detailed information on rule, tag, and capability use, and the `rule-compiler/` subfolder of the ZeroTier source tree contains a JavaScript function to compile rules in our human-readable rule definition language into rules suitable for import into a network controller. (ZeroTier Central uses this same script to compile rules on [my.zerotier.com](https://my.zerotier.com/).)
+
+### Root Server Federation
+
+It's now possible to create your own root servers and add them to the root server pool on your nodes. This is done by creating what's called a "moon," which is a signed enumeration of root servers and their stable points on the network. Refer to the [manual](https://www.zerotier.com/manual.shtml) for instructions.
+
+Federated roots achieve a number of things:
+
+ * You can deploy your own infrastructure to reduce dependency on ours.
+ * You can deploy roots *inside your LAN* to ensure that network connectivity inside your facility still works if the Internet goes down. This is the first step toward making ZeroTier viable as an in-house SDN solution.
+ * Roots can be deployed inside national boundaries for countries with data residency laws or "great firewalls." (As of 1.2.0 there is still no way to force all traffic to use these roots, but that will be easy to do in a later version.)
+ * Last but not least this makes ZeroTier somewhat less centralized by eliminating any hard dependency on ZeroTier, Inc.'s infrastructure.
+
+Our roots will of course remain and continue to provide zero-configuration instant-on deployment, a secure global authority for identities, and free traffic relaying for those who can't establish peer to peer connections.
+
+### Local Configuration
+
+An element of our design philosophy is "features are bugs." This isn't an absolute dogma but more of a guiding principle. We try as hard as we can to avoid adding features, especially "knobs" that must be tweaked by a user.
+
+As of 1.2.0 we've decided that certain knobs are unavoidable, and so there is now a `local.conf` file that can be used to configure them. See the ZeroTier One documentation for these. They include:
+
+ * Blacklisting interfaces you want to make sure ZeroTier doesn't use for network traffic, such as VPNs, slow links, or backplanes designated for only certain kinds of traffic.
+ * Turning uPnP/NAT-PMP on or off.
+ * Configuring software updates on Windows and Mac platforms.
+ * Defining trusted paths (the old trusted paths file is now deprecated)
+ * Setting the ZeroTier main port so it doesn't have to be changed on the command line, which is very inconvenient in many cases.
+
+### Improved In-Band Software Updates
+
+A good software update system for Windows and Mac clients has been a missing feature in previous versions. It does exist but we've been shy about using it so far due to its fragility in some environments.
+
+We've greatly improved this mechanism in 1.2.0. Not only does it now do a better job of actually invoking the update, but it also transfers updates in-band using the ZeroTier protocol. This means it can work in environments that do not allows http/https traffic or that force it through proxies. There's also now an update channel setting: `beta` or `release` (the default).
+
+Software updates are authenticated three ways:
+
+ 1. ZeroTier's own signing key is used to sign all updates and this signature is checked prior to installation. ZeroTier, Inc.'s signatures are performed on an air-gapped machine.
+
+ 2. Updates for Mac and Windows are signed using Apple and Microsoft (DigiCert EV) keys and will not install unless these signatures are also valid.
+
+ 3. The new in-band update mechanism also authenticates the source of the update via ZeroTier's built-in security features. This provides transport security, while 1 and 2 provide security of the update at rest.
+
+Updates are now configurable via `local.conf`. There are three options: `disable`, `download`, and `apply`. The third (apply) is the default for official builds on Windows and Mac, making updates happen silently and automatically as they do for popular browsers like Chrome and Firefox. Updates are disabled by default on Linux and other Unix-type systems as these are typically updated through package managers.
+
+### Path Link Quality Awareness
+
+Version 1.2.0 is now aware of the link quality of direct paths with other 1.2.0 nodes. This information isn't used yet but is visible through the JSON API. (Quality always shows as 100% with pre-1.2.0 nodes.) Quality is measured passively with no additional overhead using a counter based packet loss detection algorithm.
+
+This information is visible from the command line via `listpeers`:
+
+ 200 listpeers XXXXXXXXXX 199.XXX.XXX.XXX/9993;10574;15250;1.00 48 1.2.0 LEAF
+ 200 listpeers XXXXXXXXXX 195.XXX.XXX.XXX/45584;467;7608;0.44 290 1.2.0 LEAF
+
+The first peer's path is at 100% (1.00), while the second peer's path is suffering quite a bit of packet loss (0.44).
+
+Link quality awareness is a precursor to intelligent multi-path and QoS support, which will in future versions bring us to feature parity with SD-WAN products like Cisco iWAN.
+
+### Security Improvements
+
+Version 1.2.0 adds anti-DOS (denial of service) rate limits and other hardening for improved resiliency against a number of denial of service attack scenarios.
+
+It also adds a mechanism for instantaneous credential revocation. This can be used to revoke certificates of membership instantly to kick a node off a network (for private networks) and also to revoke capabilities and tags. The new controller sends revocations by default when a peer is de-authorized.
+
+Revocations propagate using a "rumor mill" peer to peer algorithm. This means that a controller need only successfully send a revocation to at least one member of a network with connections to other active members. At this point the revocation will flood through the network peer to peer very quickly. This helps make revocations more robust in the face of poor connectivity with the controller or attempts to incapacitate the controller with denial of service attacks, as well as making revocations faster on huge networks.
+
+### Windows and Macintosh UI Improvements (ZeroTier One)
+
+The Mac has a whole new UI built natively in Objective-C. It provides a pulldown similar in appearance and operation to the Mac WiFi task bar menu.
+
+The Windows UI has also been improved and now provides a task bar icon that can be right-clicked to manage networks. Both now expose managed route and IP permissions, allowing nodes to easily opt in to full tunnel operation if you have a router configured on your network.
+
+### Ad-Hoc Networks
+
+A special kind of public network called an ad-hoc network may be accessed by joining a network ID with the format:
+
+ ffSSSSEEEE000000
+ | | | |
+ | | | Reserved for future use, must be 0
+ | | End of port range (hex)
+ | Start of port range (hex)
+ Reserved ZeroTier address prefix indicating a controller-less network
+
+Ad-hoc networks are public (no access control) networks that have no network controller. Instead their configuration and other credentials are generated locally. Ad-hoc networks permit only IPv6 UDP and TCP unicast traffic (no multicast or broadcast) using 6plane format NDP-emulated IPv6 addresses. In addition an ad-hoc network ID encodes an IP port range. UDP packets and TCP SYN (connection open) packets are only allowed to desintation ports within the encoded range.
+
+For example `ff00160016000000` is an ad-hoc network allowing only SSH, while `ff0000ffff000000` is an ad-hoc network allowing any UDP or TCP port.
+
+Keep in mind that these networks are public and anyone in the entire world can join them. Care must be taken to avoid exposing vulnerable services or sharing unwanted files or other resources.
+
+### Network Controller (Partial) Rewrite
+
+The network controller has been largely rewritten to use a simple in-filesystem JSON data store in place of SQLite, and it is now included by default in all Windows, Mac, Linux, and BSD builds. This means any desktop or server node running ZeroTier One can now be a controller with no recompilation needed.
+
+If you have data in an old SQLite3 controller we've included a NodeJS script in `controller/migrate-sqlite` to migrate data to the new format. If you don't migrate, members will start getting `NOT_FOUND` when they attempt to query for updates.
+
+## Major Bug Fixes in 1.2.0
+
+ * **The Windows HyperV 100% CPU bug is FINALLY DEAD**: This long-running problem turns out to have been an issue with Windows itself, but one we were triggering by placing invalid data into the Windows registry. Microsoft is aware of the issue but we've also fixed the triggering problem on our side. ZeroTier should now co-exist quite well with HyperV and should now be able to be bridged with a HyperV virtual switch.
+ * **Segmenation faults on musl-libc based Linux systems**: Alpine Linux and some embedded Linux systems that use musl libc (a minimal libc) experienced segmentation faults. These were due to a smaller default stack size. A work-around that sets the stack size for new threads has been added.
+ * **Windows firewall blocks local JSON API**: On some Windows systems the firewall likes to block 127.0.0.1:9993 for mysterious reasons. This is now fixed in the installer via the addition of another firewall exemption rule.
+ * **UI crash on embedded Windows due to missing fonts**: The MSI installer now ships fonts and will install them if they are not present, so this should be fixed.
+
+## Other Improvements in 1.2.0
+
+ * **Improved dead path detection**: ZeroTier is now more aggressive about expiring paths that do not seem to be active. If a path seems marginal it is re-confirmed before re-use.
+ * **Minor performance improvements**: We've reduced unnecessary memcpy's and made a few other performance improvements in the core.
+ * **Linux static binaries**: For our official packages (the ones in the download.zerotier.com apt and yum repositories) we now build Linux binaries with static linking. Hopefully this will stop all the bug reports relating to library inconsistencies, as well as allowing our deb packages to run on a wider variety of Debian-based distributions. (There are far too many of these to support officially!) The overhead for this is very small, especially since we built our static versions against musl-libc. Distribution maintainers are of course free to build dynamically linked versions for inclusion into distributions; this only affects our official binaries.
diff --git a/artwork/ZeroTierIcon32x32.png b/artwork/ZeroTierIcon32x32.png
new file mode 100644
index 00000000..24ff0a1c
--- /dev/null
+++ b/artwork/ZeroTierIcon32x32.png
Binary files differ
diff --git a/node/Cluster.cpp b/attic/Cluster.cpp
index f590ad1c..119aec29 100644
--- a/node/Cluster.cpp
+++ b/attic/Cluster.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_ENABLE_CLUSTER
@@ -44,6 +52,7 @@
#include "Packet.hpp"
#include "Switch.hpp"
#include "Node.hpp"
+#include "Network.hpp"
#include "Array.hpp"
namespace ZeroTier {
@@ -248,13 +257,13 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
memcpy(keytmp,_key,32);
for(int i=0;i<8;++i)
keytmp[i] ^= reinterpret_cast<const char *>(msg)[i];
- Salsa20 s20(keytmp,256,reinterpret_cast<const char *>(msg) + 8);
+ Salsa20 s20(keytmp,reinterpret_cast<const char *>(msg) + 8);
Utils::burn(keytmp,sizeof(keytmp));
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
char polykey[ZT_POLY1305_KEY_LEN];
memset(polykey,0,sizeof(polykey));
- s20.encrypt12(polykey,polykey,sizeof(polykey));
+ s20.crypt12(polykey,polykey,sizeof(polykey));
// Compute 16-byte MAC
char mac[ZT_POLY1305_MAC_LEN];
@@ -266,7 +275,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
// Decrypt!
dmsg.setSize(len - 24);
- s20.decrypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
+ s20.crypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
}
if (dmsg.size() < 4)
@@ -341,17 +350,20 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
Identity id;
ptr += id.deserialize(dmsg,ptr);
if (id) {
- RR->topology->saveIdentity(id);
-
{
Mutex::Lock _l(_remotePeers_m);
- _remotePeers[std::pair<Address,unsigned int>(id.address(),(unsigned int)fromMemberId)] = RR->node->now();
+ _RemotePeer &rp = _remotePeers[std::pair<Address,unsigned int>(id.address(),(unsigned int)fromMemberId)];
+ if (!rp.lastHavePeerReceived) {
+ RR->topology->saveIdentity((void *)0,id);
+ RR->identity.agree(id,rp.key,ZT_PEER_SECRET_KEY_LENGTH);
+ }
+ rp.lastHavePeerReceived = RR->node->now();
}
_ClusterSendQueueEntry *q[16384]; // 16384 is "tons"
unsigned int qc = _sendQueue->getByDest(id.address(),q,16384);
for(unsigned int i=0;i<qc;++i)
- this->sendViaCluster(q[i]->fromPeerAddress,q[i]->toPeerAddress,q[i]->data,q[i]->len,q[i]->unite);
+ this->relayViaCluster(q[i]->fromPeerAddress,q[i]->toPeerAddress,q[i]->data,q[i]->len,q[i]->unite);
_sendQueue->returnToPool(q,qc);
TRACE("[%u] has %s (retried %u queued sends)",(unsigned int)fromMemberId,id.address().toString().c_str(),qc);
@@ -361,7 +373,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
case CLUSTER_MESSAGE_WANT_PEER: {
const Address zeroTierAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
SharedPtr<Peer> peer(RR->topology->getPeerNoCache(zeroTierAddress));
- if ( (peer) && (peer->hasClusterOptimalPath(RR->node->now())) ) {
+ if ( (peer) && (peer->hasLocalClusterOptimalPath(RR->node->now())) ) {
Buffer<1024> buf;
peer->identity().serialize(buf);
Mutex::Lock _l2(_members[fromMemberId].lock);
@@ -396,7 +408,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
SharedPtr<Peer> localPeer(RR->topology->getPeerNoCache(localPeerAddress));
if ((localPeer)&&(numRemotePeerPaths > 0)) {
InetAddress bestLocalV4,bestLocalV6;
- localPeer->getBestActiveAddresses(now,bestLocalV4,bestLocalV6);
+ localPeer->getRendezvousAddresses(now,bestLocalV4,bestLocalV6);
InetAddress bestRemoteV4,bestRemoteV6;
for(unsigned int i=0;i<numRemotePeerPaths;++i) {
@@ -455,7 +467,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
Mutex::Lock _l2(_members[fromMemberId].lock);
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
}
- RR->sw->send(rendezvousForLocal,true,0);
+ RR->sw->send((void *)0,rendezvousForLocal,true);
}
}
} break;
@@ -466,9 +478,18 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
const unsigned int len = dmsg.at<uint16_t>(ptr); ptr += 2;
Packet outp(rcpt,RR->identity.address(),verb);
outp.append(dmsg.field(ptr,len),len); ptr += len;
- RR->sw->send(outp,true,0);
+ RR->sw->send((void *)0,outp,true);
//TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len);
} break;
+
+ case CLUSTER_MESSAGE_NETWORK_CONFIG: {
+ const SharedPtr<Network> network(RR->node->network(dmsg.at<uint64_t>(ptr)));
+ if (network) {
+ // Copy into a Packet just to conform to Network API. Eventually
+ // will want to refactor.
+ network->handleConfigChunk((void *)0,0,Address(),Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(dmsg),ptr);
+ }
+ } break;
}
} catch ( ... ) {
TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype);
@@ -494,7 +515,84 @@ void Cluster::broadcastHavePeer(const Identity &id)
}
}
-void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
+void Cluster::broadcastNetworkConfigChunk(const void *chunk,unsigned int len)
+{
+ Mutex::Lock _l(_memberIds_m);
+ for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
+ Mutex::Lock _l2(_members[*mid].lock);
+ _send(*mid,CLUSTER_MESSAGE_NETWORK_CONFIG,chunk,len);
+ }
+}
+
+int Cluster::checkSendViaCluster(const Address &toPeerAddress,uint64_t &mostRecentTs,void *peerSecret)
+{
+ const uint64_t now = RR->node->now();
+ mostRecentTs = 0;
+ int mostRecentMemberId = -1;
+ {
+ Mutex::Lock _l2(_remotePeers_m);
+ std::map< std::pair<Address,unsigned int>,_RemotePeer >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
+ for(;;) {
+ if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
+ break;
+ else if (rpe->second.lastHavePeerReceived > mostRecentTs) {
+ mostRecentTs = rpe->second.lastHavePeerReceived;
+ memcpy(peerSecret,rpe->second.key,ZT_PEER_SECRET_KEY_LENGTH);
+ mostRecentMemberId = (int)rpe->first.second;
+ }
+ ++rpe;
+ }
+ }
+
+ const uint64_t ageOfMostRecentHavePeerAnnouncement = now - mostRecentTs;
+ if (ageOfMostRecentHavePeerAnnouncement >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
+ if (ageOfMostRecentHavePeerAnnouncement >= ZT_PEER_ACTIVITY_TIMEOUT)
+ mostRecentMemberId = -1;
+
+ bool sendWantPeer = true;
+ {
+ Mutex::Lock _l(_remotePeers_m);
+ _RemotePeer &rp = _remotePeers[std::pair<Address,unsigned int>(toPeerAddress,(unsigned int)_id)];
+ if ((now - rp.lastSentWantPeer) >= ZT_CLUSTER_WANT_PEER_EVERY) {
+ rp.lastSentWantPeer = now;
+ } else {
+ sendWantPeer = false; // don't flood WANT_PEER
+ }
+ }
+ if (sendWantPeer) {
+ char tmp[ZT_ADDRESS_LENGTH];
+ toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
+ {
+ Mutex::Lock _l(_memberIds_m);
+ for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
+ Mutex::Lock _l2(_members[*mid].lock);
+ _send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
+ }
+ }
+ }
+ }
+
+ return mostRecentMemberId;
+}
+
+bool Cluster::sendViaCluster(int mostRecentMemberId,const Address &toPeerAddress,const void *data,unsigned int len)
+{
+ if ((mostRecentMemberId < 0)||(mostRecentMemberId >= ZT_CLUSTER_MAX_MEMBERS)) // sanity check
+ return false;
+ Mutex::Lock _l2(_members[mostRecentMemberId].lock);
+ for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
+ for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
+ if (i1->ss_family == i2->ss_family) {
+ TRACE("sendViaCluster sending %u bytes to %s by way of %u (%s->%s)",len,toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
+ RR->node->putPacket((void *)0,*i1,*i2,data,len);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Cluster::relayViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
{
if (len > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check
return;
@@ -502,87 +600,101 @@ void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee
const uint64_t now = RR->node->now();
uint64_t mostRecentTs = 0;
- unsigned int mostRecentMemberId = 0xffffffff;
+ int mostRecentMemberId = -1;
{
Mutex::Lock _l2(_remotePeers_m);
- std::map< std::pair<Address,unsigned int>,uint64_t >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
+ std::map< std::pair<Address,unsigned int>,_RemotePeer >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
for(;;) {
if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
break;
- else if (rpe->second > mostRecentTs) {
- mostRecentTs = rpe->second;
- mostRecentMemberId = rpe->first.second;
+ else if (rpe->second.lastHavePeerReceived > mostRecentTs) {
+ mostRecentTs = rpe->second.lastHavePeerReceived;
+ mostRecentMemberId = (int)rpe->first.second;
}
++rpe;
}
}
- const uint64_t age = now - mostRecentTs;
- if (age >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
- const bool enqueueAndWait = ((age >= ZT_PEER_ACTIVITY_TIMEOUT)||(mostRecentMemberId > 0xffff));
+ const uint64_t ageOfMostRecentHavePeerAnnouncement = now - mostRecentTs;
+ if (ageOfMostRecentHavePeerAnnouncement >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
+ // Enqueue and wait if peer seems alive, but do WANT_PEER to refresh homing
+ const bool enqueueAndWait = ((ageOfMostRecentHavePeerAnnouncement >= ZT_PEER_ACTIVITY_TIMEOUT)||(mostRecentMemberId < 0));
// Poll everyone with WANT_PEER if the age of our most recent entry is
// approaching expiration (or has expired, or does not exist).
- char tmp[ZT_ADDRESS_LENGTH];
- toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
+ bool sendWantPeer = true;
{
- Mutex::Lock _l(_memberIds_m);
- for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
- Mutex::Lock _l2(_members[*mid].lock);
- _send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
+ Mutex::Lock _l(_remotePeers_m);
+ _RemotePeer &rp = _remotePeers[std::pair<Address,unsigned int>(toPeerAddress,(unsigned int)_id)];
+ if ((now - rp.lastSentWantPeer) >= ZT_CLUSTER_WANT_PEER_EVERY) {
+ rp.lastSentWantPeer = now;
+ } else {
+ sendWantPeer = false; // don't flood WANT_PEER
+ }
+ }
+ if (sendWantPeer) {
+ char tmp[ZT_ADDRESS_LENGTH];
+ toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
+ {
+ Mutex::Lock _l(_memberIds_m);
+ for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
+ Mutex::Lock _l2(_members[*mid].lock);
+ _send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
+ }
}
}
// If there isn't a good place to send via, then enqueue this for retrying
// later and return after having broadcasted a WANT_PEER.
if (enqueueAndWait) {
- TRACE("sendViaCluster %s -> %s enqueueing to wait for HAVE_PEER",fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
+ TRACE("relayViaCluster %s -> %s enqueueing to wait for HAVE_PEER",fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
_sendQueue->enqueue(now,fromPeerAddress,toPeerAddress,data,len,unite);
return;
}
}
- Buffer<1024> buf;
- if (unite) {
- InetAddress v4,v6;
- if (fromPeerAddress) {
- SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
- if (fromPeer)
- fromPeer->getBestActiveAddresses(now,v4,v6);
- }
- uint8_t addrCount = 0;
- if (v4)
- ++addrCount;
- if (v6)
- ++addrCount;
- if (addrCount) {
- toPeerAddress.appendTo(buf);
- fromPeerAddress.appendTo(buf);
- buf.append(addrCount);
+ if (mostRecentMemberId >= 0) {
+ Buffer<1024> buf;
+ if (unite) {
+ InetAddress v4,v6;
+ if (fromPeerAddress) {
+ SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
+ if (fromPeer)
+ fromPeer->getRendezvousAddresses(now,v4,v6);
+ }
+ uint8_t addrCount = 0;
if (v4)
- v4.serialize(buf);
+ ++addrCount;
if (v6)
- v6.serialize(buf);
+ ++addrCount;
+ if (addrCount) {
+ toPeerAddress.appendTo(buf);
+ fromPeerAddress.appendTo(buf);
+ buf.append(addrCount);
+ if (v4)
+ v4.serialize(buf);
+ if (v6)
+ v6.serialize(buf);
+ }
}
- }
- {
- Mutex::Lock _l2(_members[mostRecentMemberId].lock);
- if (buf.size() > 0)
- _send(mostRecentMemberId,CLUSTER_MESSAGE_PROXY_UNITE,buf.data(),buf.size());
-
- for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
- for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
- if (i1->ss_family == i2->ss_family) {
- TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u (%s->%s)",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
- RR->node->putPacket(*i1,*i2,data,len);
- return;
+ {
+ Mutex::Lock _l2(_members[mostRecentMemberId].lock);
+ if (buf.size() > 0)
+ _send(mostRecentMemberId,CLUSTER_MESSAGE_PROXY_UNITE,buf.data(),buf.size());
+
+ for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
+ for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
+ if (i1->ss_family == i2->ss_family) {
+ TRACE("relayViaCluster relaying %u bytes from %s to %s by way of %u (%s->%s)",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
+ RR->node->putPacket((void *)0,*i1,*i2,data,len);
+ return;
+ }
}
}
- }
- TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u failed: no common endpoints with the same address family!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
- return;
+ TRACE("relayViaCluster relaying %u bytes from %s to %s by way of %u failed: no common endpoints with the same address family!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
+ }
}
}
@@ -644,8 +756,8 @@ void Cluster::doPeriodicTasks()
_lastCleanedRemotePeers = now;
Mutex::Lock _l(_remotePeers_m);
- for(std::map< std::pair<Address,unsigned int>,uint64_t >::iterator rp(_remotePeers.begin());rp!=_remotePeers.end();) {
- if ((now - rp->second) >= ZT_PEER_ACTIVITY_TIMEOUT)
+ for(std::map< std::pair<Address,unsigned int>,_RemotePeer >::iterator rp(_remotePeers.begin());rp!=_remotePeers.end();) {
+ if ((now - rp->second.lastHavePeerReceived) >= ZT_PEER_ACTIVITY_TIMEOUT)
_remotePeers.erase(rp++);
else ++rp;
}
@@ -719,7 +831,9 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
std::vector<InetAddress> best;
const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
double bestDistance = (offload ? 2147483648.0 : currentDistance);
+#ifdef ZT_TRACE
unsigned int bestMember = _id;
+#endif
{
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
@@ -731,7 +845,9 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
if (mdist < bestDistance) {
bestDistance = mdist;
+#ifdef ZT_TRACE
bestMember = *mid;
+#endif
best = m.zeroTierPhysicalEndpoints;
}
}
@@ -754,6 +870,19 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
}
}
+bool Cluster::isClusterPeerFrontplane(const InetAddress &ip) const
+{
+ Mutex::Lock _l(_memberIds_m);
+ for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
+ Mutex::Lock _l2(_members[*mid].lock);
+ for(std::vector<InetAddress>::const_iterator i2(_members[*mid].zeroTierPhysicalEndpoints.begin());i2!=_members[*mid].zeroTierPhysicalEndpoints.end();++i2) {
+ if (ip == *i2)
+ return true;
+ }
+ }
+ return false;
+}
+
void Cluster::status(ZT_ClusterStatus &status) const
{
const uint64_t now = RR->node->now();
@@ -827,16 +956,16 @@ void Cluster::_flush(uint16_t memberId)
memcpy(keytmp,m.key,32);
for(int i=0;i<8;++i)
keytmp[i] ^= m.q[i];
- Salsa20 s20(keytmp,256,m.q.field(8,8));
+ Salsa20 s20(keytmp,m.q.field(8,8));
Utils::burn(keytmp,sizeof(keytmp));
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
char polykey[ZT_POLY1305_KEY_LEN];
memset(polykey,0,sizeof(polykey));
- s20.encrypt12(polykey,polykey,sizeof(polykey));
+ s20.crypt12(polykey,polykey,sizeof(polykey));
// Encrypt m.q in place
- s20.encrypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
+ s20.crypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
// Add MAC for authentication (encrypt-then-MAC)
char mac[ZT_POLY1305_MAC_LEN];
@@ -860,7 +989,7 @@ void Cluster::_flush(uint16_t memberId)
void Cluster::_doREMOTE_WHOIS(uint64_t fromMemberId,const Packet &remotep)
{
if (remotep.payloadLength() >= ZT_ADDRESS_LENGTH) {
- Identity queried(RR->topology->getIdentity(Address(remotep.payload(),ZT_ADDRESS_LENGTH)));
+ Identity queried(RR->topology->getIdentity((void *)0,Address(remotep.payload(),ZT_ADDRESS_LENGTH)));
if (queried) {
Buffer<1024> routp;
remotep.source().appendTo(routp);
diff --git a/node/Cluster.hpp b/attic/Cluster.hpp
index dafbf425..74b091f5 100644
--- a/node/Cluster.hpp
+++ b/attic/Cluster.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CLUSTER_HPP
@@ -88,6 +96,11 @@
*/
#define ZT_CLUSTER_SEND_QUEUE_DATA_MAX 1500
+/**
+ * We won't send WANT_PEER to other members more than every (ms) per recipient
+ */
+#define ZT_CLUSTER_WANT_PEER_EVERY 1000
+
namespace ZeroTier {
class RuntimeEnvironment;
@@ -216,14 +229,13 @@ public:
/**
* Replicate a network config for a network we belong to:
- * <[8] 64-bit network ID>
- * <[2] 16-bit length of network config>
- * <[...] serialized network config>
+ * <[...] network config chunk>
*
* This is used by clusters to avoid every member having to query
* for the same netconf for networks all members belong to.
*
- * TODO: not implemented yet!
+ * The first field of a network config chunk is the network ID,
+ * so this can be checked to look up the network on receipt.
*/
CLUSTER_MESSAGE_NETWORK_CONFIG = 7
};
@@ -268,7 +280,38 @@ public:
void broadcastHavePeer(const Identity &id);
/**
- * Send this packet via another node in this cluster if another node has this peer
+ * Broadcast a network config chunk to other members of cluster
+ *
+ * @param chunk Chunk data
+ * @param len Length of chunk
+ */
+ void broadcastNetworkConfigChunk(const void *chunk,unsigned int len);
+
+ /**
+ * If the cluster has this peer, prepare the packet to send via cluster
+ *
+ * Note that outp is only armored (or modified at all) if the return value is a member ID.
+ *
+ * @param toPeerAddress Value of outp.destination(), simply to save additional lookup
+ * @param ts Result: set to time of last HAVE_PEER from the cluster
+ * @param peerSecret Result: Buffer to fill with peer secret on valid return value, must be at least ZT_PEER_SECRET_KEY_LENGTH bytes
+ * @return -1 if cluster does not know this peer, or a member ID to pass to sendViaCluster()
+ */
+ int checkSendViaCluster(const Address &toPeerAddress,uint64_t &mostRecentTs,void *peerSecret);
+
+ /**
+ * Send data via cluster front plane (packet head or fragment)
+ *
+ * @param haveMemberId Member ID that has this peer as returned by prepSendviaCluster()
+ * @param toPeerAddress Destination peer address
+ * @param data Packet or packet fragment data
+ * @param len Length of packet or fragment
+ * @return True if packet was sent (and outp was modified via armoring)
+ */
+ bool sendViaCluster(int haveMemberId,const Address &toPeerAddress,const void *data,unsigned int len);
+
+ /**
+ * Relay a packet via the cluster
*
* This is used in the outgoing packet and relaying logic in Switch to
* relay packets to other cluster members. It isn't PROXY_SEND-- that is
@@ -280,7 +323,7 @@ public:
* @param len Length of packet or fragment
* @param unite If true, also request proxy unite across cluster
*/
- void sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
+ void relayViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
/**
* Send a distributed query to other cluster members
@@ -324,6 +367,12 @@ public:
bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
/**
+ * @param ip Address to check
+ * @return True if this is a cluster frontplane address (excluding our addresses)
+ */
+ bool isClusterPeerFrontplane(const InetAddress &ip) const;
+
+ /**
* Fill out ZT_ClusterStatus structure (from core API)
*
* @param status Reference to structure to hold result (anything there is replaced)
@@ -391,7 +440,15 @@ private:
std::vector<uint16_t> _memberIds;
Mutex _memberIds_m;
- std::map< std::pair<Address,unsigned int>,uint64_t > _remotePeers; // we need ordered behavior and lower_bound here
+ struct _RemotePeer
+ {
+ _RemotePeer() : lastHavePeerReceived(0),lastSentWantPeer(0) {}
+ ~_RemotePeer() { Utils::burn(key,ZT_PEER_SECRET_KEY_LENGTH); }
+ uint64_t lastHavePeerReceived;
+ uint64_t lastSentWantPeer;
+ uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]; // secret key from identity agreement
+ };
+ std::map< std::pair<Address,unsigned int>,_RemotePeer > _remotePeers; // we need ordered behavior and lower_bound here
Mutex _remotePeers_m;
uint64_t _lastFlushed;
diff --git a/service/ClusterDefinition.hpp b/attic/ClusterDefinition.hpp
index 441cc04d..b6317ff7 100644
--- a/service/ClusterDefinition.hpp
+++ b/attic/ClusterDefinition.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CLUSTERDEFINITION_HPP
@@ -64,11 +72,11 @@ public:
return;
char myAddressStr[64];
- Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
+ Utils::ztsnprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
- std::vector<std::string> lines(Utils::split(cf.c_str(),"\r\n","",""));
+ std::vector<std::string> lines(OSUtils::split(cf.c_str(),"\r\n","",""));
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
- std::vector<std::string> fields(Utils::split(l->c_str()," \t","",""));
+ std::vector<std::string> fields(OSUtils::split(l->c_str()," \t","",""));
if ((fields.size() < 5)||(fields[0][0] == '#')||(fields[0] != myAddressStr))
continue;
@@ -93,7 +101,7 @@ public:
md.id = (unsigned int)id;
if (fields.size() >= 6) {
- std::vector<std::string> xyz(Utils::split(fields[5].c_str(),",","",""));
+ std::vector<std::string> xyz(OSUtils::split(fields[5].c_str(),",","",""));
md.x = (xyz.size() > 0) ? Utils::strToInt(xyz[0].c_str()) : 0;
md.y = (xyz.size() > 1) ? Utils::strToInt(xyz[1].c_str()) : 0;
md.z = (xyz.size() > 2) ? Utils::strToInt(xyz[2].c_str()) : 0;
@@ -102,7 +110,7 @@ public:
md.clusterEndpoint.fromString(fields[3]);
if (!md.clusterEndpoint)
continue;
- std::vector<std::string> zips(Utils::split(fields[4].c_str(),",","",""));
+ std::vector<std::string> zips(OSUtils::split(fields[4].c_str(),",","",""));
for(std::vector<std::string>::iterator zip(zips.begin());zip!=zips.end();++zip) {
InetAddress i;
i.fromString(*zip);
diff --git a/service/ClusterGeoIpService.cpp b/attic/ClusterGeoIpService.cpp
index 3ad69753..2dcc9179 100644
--- a/service/ClusterGeoIpService.cpp
+++ b/attic/ClusterGeoIpService.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_ENABLE_CLUSTER
@@ -101,7 +109,7 @@ bool ClusterGeoIpService::locate(const InetAddress &ip,int &x,int &y,int &z)
void ClusterGeoIpService::_parseLine(const char *line,std::vector<_V4E> &v4db,std::vector<_V6E> &v6db,int ipStartColumn,int ipEndColumn,int latitudeColumn,int longitudeColumn)
{
- std::vector<std::string> ls(Utils::split(line,",\t","\\","\"'"));
+ std::vector<std::string> ls(OSUtils::split(line,",\t","\\","\"'"));
if ( ((ipStartColumn >= 0)&&(ipStartColumn < (int)ls.size()))&&
((ipEndColumn >= 0)&&(ipEndColumn < (int)ls.size()))&&
((latitudeColumn >= 0)&&(latitudeColumn < (int)ls.size()))&&
diff --git a/service/ClusterGeoIpService.hpp b/attic/ClusterGeoIpService.hpp
index ff2fcdb8..380f944f 100644
--- a/service/ClusterGeoIpService.hpp
+++ b/attic/ClusterGeoIpService.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CLUSTERGEOIPSERVICE_HPP
diff --git a/attic/FCV.hpp b/attic/FCV.hpp
new file mode 100644
index 00000000..0fb9e250
--- /dev/null
+++ b/attic/FCV.hpp
@@ -0,0 +1,101 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * A really simple fixed capacity vector
+ *
+ * This class does no bounds checking, so the user must ensure that
+ * no more than C elements are ever added and that accesses are in
+ * bounds.
+ *
+ * @tparam T Type to contain
+ * @tparam C Capacity of vector
+ */
+template<typename T,unsigned long C>
+class FCV
+{
+public:
+ FCV() : _s(0) {}
+ ~FCV() { clear(); }
+
+ FCV(const FCV &v) :
+ _s(v._s)
+ {
+ for(unsigned long i=0;i<_s;++i) {
+ new (reinterpret_cast<T *>(_mem + (sizeof(T) * i))) T(reinterpret_cast<const T *>(v._mem)[i]);
+ }
+ }
+
+ inline FCV &operator=(const FCV &v)
+ {
+ clear();
+ _s = v._s;
+ for(unsigned long i=0;i<_s;++i) {
+ new (reinterpret_cast<T *>(_mem + (sizeof(T) * i))) T(reinterpret_cast<const T *>(v._mem)[i]);
+ }
+ return *this;
+ }
+
+ typedef T * iterator;
+ typedef const T * const_iterator;
+ typedef unsigned long size_type;
+
+ inline iterator begin() { return (T *)_mem; }
+ inline iterator end() { return (T *)(_mem + (sizeof(T) * _s)); }
+ inline iterator begin() const { return (const T *)_mem; }
+ inline iterator end() const { return (const T *)(_mem + (sizeof(T) * _s)); }
+
+ inline T &operator[](const size_type i) { return reinterpret_cast<T *>(_mem)[i]; }
+ inline const T &operator[](const size_type i) const { return reinterpret_cast<const T *>(_mem)[i]; }
+
+ inline T &front() { return reinterpret_cast<T *>(_mem)[0]; }
+ inline const T &front() const { return reinterpret_cast<const T *>(_mem)[0]; }
+ inline T &back() { return reinterpret_cast<T *>(_mem)[_s - 1]; }
+ inline const T &back() const { return reinterpret_cast<const T *>(_mem)[_s - 1]; }
+
+ inline void push_back(const T &v) { new (reinterpret_cast<T *>(_mem + (sizeof(T) * _s++))) T(v); }
+ inline void pop_back() { reinterpret_cast<T *>(_mem + (sizeof(T) * --_s))->~T(); }
+
+ inline size_type size() const { return _s; }
+ inline size_type capacity() const { return C; }
+
+ inline void clear()
+ {
+ for(unsigned long i=0;i<_s;++i)
+ reinterpret_cast<T *>(_mem + (sizeof(T) * i))->~T();
+ _s = 0;
+ }
+
+private:
+ char _mem[sizeof(T) * C];
+ unsigned long _s;
+};
+
+} // namespace ZeroTier
diff --git a/attic/Filter.cpp b/attic/Filter.cpp
deleted file mode 100644
index a701e8b7..00000000
--- a/attic/Filter.cpp
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, 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 <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <algorithm>
-
-#include "RuntimeEnvironment.hpp"
-#include "Logger.hpp"
-#include "Filter.hpp"
-#include "Utils.hpp"
-
-namespace ZeroTier {
-
-const char *const Filter::UNKNOWN_NAME = "(unknown)";
-const Range<unsigned int> Filter::ANY;
-
-static inline Range<unsigned int> __parseRange(char *r)
- throw(std::invalid_argument)
-{
- char *saveptr = (char *)0;
- unsigned int a = 0;
- unsigned int b = 0;
- unsigned int fn = 0;
- for(char *f=Utils::stok(r,"-",&saveptr);(f);f=Utils::stok((char *)0,"-",&saveptr)) {
- if (*f) {
- switch(fn++) {
- case 0:
- if (*f != '*')
- a = b = (unsigned int)strtoul(f,(char **)0,10);
- break;
- case 1:
- if (*f != '*')
- b = (unsigned int)strtoul(f,(char **)0,10);
- break;
- default:
- throw std::invalid_argument("rule range must be <int>, <int>-<int>, or *");
- }
- }
- }
- return Range<unsigned int>(a,b);
-}
-
-Filter::Rule::Rule(const char *s)
- throw(std::invalid_argument)
-{
- char *saveptr = (char *)0;
- char tmp[256];
- if (!Utils::scopy(tmp,sizeof(tmp),s))
- throw std::invalid_argument("rule string too long");
- unsigned int fn = 0;
- for(char *f=Utils::stok(tmp,";",&saveptr);(f);f=Utils::stok((char *)0,";",&saveptr)) {
- if (*f) {
- switch(fn++) {
- case 0:
- _etherType = __parseRange(f);
- break;
- case 1:
- _protocol = __parseRange(f);
- break;
- case 2:
- _port = __parseRange(f);
- break;
- default:
- throw std::invalid_argument("rule string has unknown extra fields");
- }
- }
- }
- if (fn != 3)
- throw std::invalid_argument("rule string must contain 3 fields");
-}
-
-bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int len) const
- throw(std::invalid_argument)
-{
- if ((!_etherType)||(_etherType(etype))) { // ethertype is ANY, or matches
- // Ethertype determines meaning of protocol and port
- switch(etype) {
- case ZT_ETHERTYPE_IPV4:
- if (len > 20) {
- if ((!_protocol)||(_protocol(((const uint8_t *)data)[9]))) { // protocol is ANY or match
- if (!_port) // port is ANY
- return true;
-
- // Don't match on fragments beyond fragment 0. If we've blocked
- // fragment 0, further fragments will fall on deaf ears anyway.
- if ((Utils::ntoh(((const uint16_t *)data)[3]) & 0x1fff))
- return false;
-
- // Internet header length determines where data begins, in multiples of 32 bits
- unsigned int ihl = 4 * (((const uint8_t *)data)[0] & 0x0f);
-
- switch(((const uint8_t *)data)[9]) { // port's meaning depends on IP protocol
- case ZT_IPPROTO_ICMP:
- // For ICMP, port is ICMP type
- return _port(((const uint8_t *)data)[ihl]);
- case ZT_IPPROTO_TCP:
- case ZT_IPPROTO_UDP:
- case ZT_IPPROTO_SCTP:
- case ZT_IPPROTO_UDPLITE:
- // For these, port is destination port. Protocol designers were
- // nice enough to put the field in the same place.
- return _port(((const uint16_t *)data)[(ihl / 2) + 1]);
- default:
- // port has no meaning for other IP types, so ignore it
- return true;
- }
-
- return false; // no match on port
- }
- } else throw std::invalid_argument("undersized IPv4 packet");
- break;
-
- case ZT_ETHERTYPE_IPV6:
- if (len > 40) {
- int nextHeader = ((const uint8_t *)data)[6];
- unsigned int pos = 40;
- while ((pos < len)&&(nextHeader >= 0)&&(nextHeader != 59)) { // 59 == no next header
- fprintf(stderr,"[rule] V6: start header parse, header %.2x pos %d\n",nextHeader,pos);
-
- switch(nextHeader) {
- case 0: // hop-by-hop options
- case 60: // destination options
- case 43: // routing
- case 135: // mobility (mobile IPv6 options)
- if (_protocol((unsigned int)nextHeader))
- return true; // match if our goal was to match any of these
- nextHeader = ((const uint8_t *)data)[pos];
- pos += 8 + (8 * ((const uint8_t *)data)[pos + 1]);
- break;
- case 44: // fragment
- if (_protocol(44))
- return true; // match if our goal was to match fragments
- nextHeader = ((const uint8_t *)data)[pos];
- pos += 8;
- break;
- case ZT_IPPROTO_AH: // AH
- return _protocol(ZT_IPPROTO_AH); // true if AH is matched protocol, otherwise false since packet will be IPsec
- case ZT_IPPROTO_ESP: // ESP
- return _protocol(ZT_IPPROTO_ESP); // true if ESP is matched protocol, otherwise false since packet will be IPsec
- case ZT_IPPROTO_ICMPV6:
- // Only match ICMPv6 if we've selected it specifically
- if (_protocol(ZT_IPPROTO_ICMPV6)) {
- // Port is interpreted as ICMPv6 type
- if ((!_port)||(_port(((const uint8_t *)data)[pos])))
- return true;
- }
- break;
- case ZT_IPPROTO_TCP:
- case ZT_IPPROTO_UDP:
- case ZT_IPPROTO_SCTP:
- case ZT_IPPROTO_UDPLITE:
- // If we encounter any of these, match if protocol matches or is wildcard as
- // we'll consider these the "real payload" if present.
- if ((!_protocol)||(_protocol(nextHeader))) {
- if ((!_port)||(_port(((const uint16_t *)data)[(pos / 2) + 1])))
- return true; // protocol matches or is ANY, port is ANY or matches
- }
- break;
- default: {
- char foo[128];
- Utils::snprintf(foo,sizeof(foo),"unrecognized IPv6 header type %d",(int)nextHeader);
- throw std::invalid_argument(foo);
- }
- }
-
- fprintf(stderr,"[rule] V6: end header parse, next header %.2x, new pos %d\n",nextHeader,pos);
- }
- } else throw std::invalid_argument("undersized IPv6 packet");
- break;
-
- default:
- // For other ethertypes, protocol and port are ignored. What would they mean?
- return true;
- }
- }
-
- return false;
-}
-
-std::string Filter::Rule::toString() const
-{
- char buf[128];
- std::string s;
-
- switch(_etherType.magnitude()) {
- case 0:
- s.push_back('*');
- break;
- case 1:
- Utils::snprintf(buf,sizeof(buf),"%u",_etherType.start);
- s.append(buf);
- break;
- default:
- Utils::snprintf(buf,sizeof(buf),"%u-%u",_etherType.start,_etherType.end);
- s.append(buf);
- break;
- }
- s.push_back(';');
- switch(_protocol.magnitude()) {
- case 0:
- s.push_back('*');
- break;
- case 1:
- Utils::snprintf(buf,sizeof(buf),"%u",_protocol.start);
- s.append(buf);
- break;
- default:
- Utils::snprintf(buf,sizeof(buf),"%u-%u",_protocol.start,_protocol.end);
- s.append(buf);
- break;
- }
- s.push_back(';');
- switch(_port.magnitude()) {
- case 0:
- s.push_back('*');
- break;
- case 1:
- Utils::snprintf(buf,sizeof(buf),"%u",_port.start);
- s.append(buf);
- break;
- default:
- Utils::snprintf(buf,sizeof(buf),"%u-%u",_port.start,_port.end);
- s.append(buf);
- break;
- }
-
- return s;
-}
-
-Filter::Filter(const char *s)
- throw(std::invalid_argument)
-{
- char tmp[16384];
- if (!Utils::scopy(tmp,sizeof(tmp),s))
- throw std::invalid_argument("filter string too long");
- char *saveptr = (char *)0;
- unsigned int fn = 0;
- for(char *f=Utils::stok(tmp,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
- try {
- _rules.push_back(Rule(f));
- ++fn;
- } catch (std::invalid_argument &exc) {
- char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"invalid rule at index %u: %s",fn,exc.what());
- throw std::invalid_argument(tmp);
- }
- }
- std::sort(_rules.begin(),_rules.end());
-}
-
-std::string Filter::toString() const
-{
- std::string s;
-
- for(std::vector<Rule>::const_iterator r(_rules.begin());r!=_rules.end();++r) {
- if (s.length() > 0)
- s.push_back(',');
- s.append(r->toString());
- }
-
- return s;
-}
-
-void Filter::add(const Rule &r)
-{
- for(std::vector<Rule>::iterator rr(_rules.begin());rr!=_rules.end();++rr) {
- if (r == *rr)
- return;
- }
- _rules.push_back(r);
- std::sort(_rules.begin(),_rules.end());
-}
-
-const char *Filter::etherTypeName(const unsigned int etherType)
- throw()
-{
- switch(etherType) {
- case ZT_ETHERTYPE_IPV4: return "ETHERTYPE_IPV4";
- case ZT_ETHERTYPE_ARP: return "ETHERTYPE_ARP";
- case ZT_ETHERTYPE_RARP: return "ETHERTYPE_RARP";
- case ZT_ETHERTYPE_ATALK: return "ETHERTYPE_ATALK";
- case ZT_ETHERTYPE_AARP: return "ETHERTYPE_AARP";
- case ZT_ETHERTYPE_IPX_A: return "ETHERTYPE_IPX_A";
- case ZT_ETHERTYPE_IPX_B: return "ETHERTYPE_IPX_B";
- case ZT_ETHERTYPE_IPV6: return "ETHERTYPE_IPV6";
- }
- return UNKNOWN_NAME;
-}
-
-const char *Filter::ipProtocolName(const unsigned int ipp)
- throw()
-{
- switch(ipp) {
- case ZT_IPPROTO_ICMP: return "IPPROTO_ICMP";
- case ZT_IPPROTO_IGMP: return "IPPROTO_IGMP";
- case ZT_IPPROTO_TCP: return "IPPROTO_TCP";
- case ZT_IPPROTO_UDP: return "IPPROTO_UDP";
- case ZT_IPPROTO_GRE: return "IPPROTO_GRE";
- case ZT_IPPROTO_ESP: return "IPPROTO_ESP";
- case ZT_IPPROTO_AH: return "IPPROTO_AH";
- case ZT_IPPROTO_ICMPV6: return "IPPROTO_ICMPV6";
- case ZT_IPPROTO_OSPF: return "IPPROTO_OSPF";
- case ZT_IPPROTO_IPIP: return "IPPROTO_IPIP";
- case ZT_IPPROTO_IPCOMP: return "IPPROTO_IPCOMP";
- case ZT_IPPROTO_L2TP: return "IPPROTO_L2TP";
- case ZT_IPPROTO_SCTP: return "IPPROTO_SCTP";
- case ZT_IPPROTO_FC: return "IPPROTO_FC";
- case ZT_IPPROTO_UDPLITE: return "IPPROTO_UDPLITE";
- case ZT_IPPROTO_HIP: return "IPPROTO_HIP";
- }
- return UNKNOWN_NAME;
-}
-
-const char *Filter::icmpTypeName(const unsigned int icmpType)
- throw()
-{
- switch(icmpType) {
- case ZT_ICMP_ECHO_REPLY: return "ICMP_ECHO_REPLY";
- case ZT_ICMP_DESTINATION_UNREACHABLE: return "ICMP_DESTINATION_UNREACHABLE";
- case ZT_ICMP_SOURCE_QUENCH: return "ICMP_SOURCE_QUENCH";
- case ZT_ICMP_REDIRECT: return "ICMP_REDIRECT";
- case ZT_ICMP_ALTERNATE_HOST_ADDRESS: return "ICMP_ALTERNATE_HOST_ADDRESS";
- case ZT_ICMP_ECHO_REQUEST: return "ICMP_ECHO_REQUEST";
- case ZT_ICMP_ROUTER_ADVERTISEMENT: return "ICMP_ROUTER_ADVERTISEMENT";
- case ZT_ICMP_ROUTER_SOLICITATION: return "ICMP_ROUTER_SOLICITATION";
- case ZT_ICMP_TIME_EXCEEDED: return "ICMP_TIME_EXCEEDED";
- case ZT_ICMP_BAD_IP_HEADER: return "ICMP_BAD_IP_HEADER";
- case ZT_ICMP_TIMESTAMP: return "ICMP_TIMESTAMP";
- case ZT_ICMP_TIMESTAMP_REPLY: return "ICMP_TIMESTAMP_REPLY";
- case ZT_ICMP_INFORMATION_REQUEST: return "ICMP_INFORMATION_REQUEST";
- case ZT_ICMP_INFORMATION_REPLY: return "ICMP_INFORMATION_REPLY";
- case ZT_ICMP_ADDRESS_MASK_REQUEST: return "ICMP_ADDRESS_MASK_REQUEST";
- case ZT_ICMP_ADDRESS_MASK_REPLY: return "ICMP_ADDRESS_MASK_REPLY";
- case ZT_ICMP_TRACEROUTE: return "ICMP_TRACEROUTE";
- case ZT_ICMP_MOBILE_HOST_REDIRECT: return "ICMP_MOBILE_HOST_REDIRECT";
- case ZT_ICMP_MOBILE_REGISTRATION_REQUEST: return "ICMP_MOBILE_REGISTRATION_REQUEST";
- case ZT_ICMP_MOBILE_REGISTRATION_REPLY: return "ICMP_MOBILE_REGISTRATION_REPLY";
- }
- return UNKNOWN_NAME;
-}
-
-const char *Filter::icmp6TypeName(const unsigned int icmp6Type)
- throw()
-{
- switch(icmp6Type) {
- case ZT_ICMP6_DESTINATION_UNREACHABLE: return "ICMP6_DESTINATION_UNREACHABLE";
- case ZT_ICMP6_PACKET_TOO_BIG: return "ICMP6_PACKET_TOO_BIG";
- case ZT_ICMP6_TIME_EXCEEDED: return "ICMP6_TIME_EXCEEDED";
- case ZT_ICMP6_PARAMETER_PROBLEM: return "ICMP6_PARAMETER_PROBLEM";
- case ZT_ICMP6_ECHO_REQUEST: return "ICMP6_ECHO_REQUEST";
- case ZT_ICMP6_ECHO_REPLY: return "ICMP6_ECHO_REPLY";
- case ZT_ICMP6_MULTICAST_LISTENER_QUERY: return "ICMP6_MULTICAST_LISTENER_QUERY";
- case ZT_ICMP6_MULTICAST_LISTENER_REPORT: return "ICMP6_MULTICAST_LISTENER_REPORT";
- case ZT_ICMP6_MULTICAST_LISTENER_DONE: return "ICMP6_MULTICAST_LISTENER_DONE";
- case ZT_ICMP6_ROUTER_SOLICITATION: return "ICMP6_ROUTER_SOLICITATION";
- case ZT_ICMP6_ROUTER_ADVERTISEMENT: return "ICMP6_ROUTER_ADVERTISEMENT";
- case ZT_ICMP6_NEIGHBOR_SOLICITATION: return "ICMP6_NEIGHBOR_SOLICITATION";
- case ZT_ICMP6_NEIGHBOR_ADVERTISEMENT: return "ICMP6_NEIGHBOR_ADVERTISEMENT";
- case ZT_ICMP6_REDIRECT_MESSAGE: return "ICMP6_REDIRECT_MESSAGE";
- case ZT_ICMP6_ROUTER_RENUMBERING: return "ICMP6_ROUTER_RENUMBERING";
- case ZT_ICMP6_NODE_INFORMATION_QUERY: return "ICMP6_NODE_INFORMATION_QUERY";
- case ZT_ICMP6_NODE_INFORMATION_RESPONSE: return "ICMP6_NODE_INFORMATION_RESPONSE";
- case ZT_ICMP6_INV_NEIGHBOR_SOLICITATION: return "ICMP6_INV_NEIGHBOR_SOLICITATION";
- case ZT_ICMP6_INV_NEIGHBOR_ADVERTISEMENT: return "ICMP6_INV_NEIGHBOR_ADVERTISEMENT";
- case ZT_ICMP6_MLDV2: return "ICMP6_MLDV2";
- case ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST: return "ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST";
- case ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY: return "ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY";
- case ZT_ICMP6_MOBILE_PREFIX_SOLICITATION: return "ICMP6_MOBILE_PREFIX_SOLICITATION";
- case ZT_ICMP6_MOBILE_PREFIX_ADVERTISEMENT: return "ICMP6_MOBILE_PREFIX_ADVERTISEMENT";
- case ZT_ICMP6_CERTIFICATION_PATH_SOLICITATION: return "ICMP6_CERTIFICATION_PATH_SOLICITATION";
- case ZT_ICMP6_CERTIFICATION_PATH_ADVERTISEMENT: return "ICMP6_CERTIFICATION_PATH_ADVERTISEMENT";
- case ZT_ICMP6_MULTICAST_ROUTER_ADVERTISEMENT: return "ICMP6_MULTICAST_ROUTER_ADVERTISEMENT";
- case ZT_ICMP6_MULTICAST_ROUTER_SOLICITATION: return "ICMP6_MULTICAST_ROUTER_SOLICITATION";
- case ZT_ICMP6_MULTICAST_ROUTER_TERMINATION: return "ICMP6_MULTICAST_ROUTER_TERMINATION";
- case ZT_ICMP6_RPL_CONTROL_MESSAGE: return "ICMP6_RPL_CONTROL_MESSAGE";
- }
- return UNKNOWN_NAME;
-}
-
-} // namespace ZeroTier
diff --git a/attic/Filter.hpp b/attic/Filter.hpp
deleted file mode 100644
index 4bea3715..00000000
--- a/attic/Filter.hpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, 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 <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifndef _ZT_FILTER_HPP
-#define _ZT_FILTER_HPP
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-#include <vector>
-#include <utility>
-#include <stdexcept>
-
-#include "Range.hpp"
-
-/* Ethernet frame types that might be relevant to us */
-#define ZT_ETHERTYPE_IPV4 0x0800
-#define ZT_ETHERTYPE_ARP 0x0806
-#define ZT_ETHERTYPE_RARP 0x8035
-#define ZT_ETHERTYPE_ATALK 0x809b
-#define ZT_ETHERTYPE_AARP 0x80f3
-#define ZT_ETHERTYPE_IPX_A 0x8137
-#define ZT_ETHERTYPE_IPX_B 0x8138
-#define ZT_ETHERTYPE_IPV6 0x86dd
-
-/* IP protocols we might care about */
-#define ZT_IPPROTO_ICMP 0x01
-#define ZT_IPPROTO_IGMP 0x02
-#define ZT_IPPROTO_TCP 0x06
-#define ZT_IPPROTO_UDP 0x11
-#define ZT_IPPROTO_GRE 0x2f
-#define ZT_IPPROTO_ESP 0x32
-#define ZT_IPPROTO_AH 0x33
-#define ZT_IPPROTO_ICMPV6 0x3a
-#define ZT_IPPROTO_OSPF 0x59
-#define ZT_IPPROTO_IPIP 0x5e
-#define ZT_IPPROTO_IPCOMP 0x6c
-#define ZT_IPPROTO_L2TP 0x73
-#define ZT_IPPROTO_SCTP 0x84
-#define ZT_IPPROTO_FC 0x85
-#define ZT_IPPROTO_UDPLITE 0x88
-#define ZT_IPPROTO_HIP 0x8b
-
-/* IPv4 ICMP types */
-#define ZT_ICMP_ECHO_REPLY 0
-#define ZT_ICMP_DESTINATION_UNREACHABLE 3
-#define ZT_ICMP_SOURCE_QUENCH 4
-#define ZT_ICMP_REDIRECT 5
-#define ZT_ICMP_ALTERNATE_HOST_ADDRESS 6
-#define ZT_ICMP_ECHO_REQUEST 8
-#define ZT_ICMP_ROUTER_ADVERTISEMENT 9
-#define ZT_ICMP_ROUTER_SOLICITATION 10
-#define ZT_ICMP_TIME_EXCEEDED 11
-#define ZT_ICMP_BAD_IP_HEADER 12
-#define ZT_ICMP_TIMESTAMP 13
-#define ZT_ICMP_TIMESTAMP_REPLY 14
-#define ZT_ICMP_INFORMATION_REQUEST 15
-#define ZT_ICMP_INFORMATION_REPLY 16
-#define ZT_ICMP_ADDRESS_MASK_REQUEST 17
-#define ZT_ICMP_ADDRESS_MASK_REPLY 18
-#define ZT_ICMP_TRACEROUTE 30
-#define ZT_ICMP_MOBILE_HOST_REDIRECT 32
-#define ZT_ICMP_MOBILE_REGISTRATION_REQUEST 35
-#define ZT_ICMP_MOBILE_REGISTRATION_REPLY 36
-
-/* IPv6 ICMP types */
-#define ZT_ICMP6_DESTINATION_UNREACHABLE 1
-#define ZT_ICMP6_PACKET_TOO_BIG 2
-#define ZT_ICMP6_TIME_EXCEEDED 3
-#define ZT_ICMP6_PARAMETER_PROBLEM 4
-#define ZT_ICMP6_ECHO_REQUEST 128
-#define ZT_ICMP6_ECHO_REPLY 129
-#define ZT_ICMP6_MULTICAST_LISTENER_QUERY 130
-#define ZT_ICMP6_MULTICAST_LISTENER_REPORT 131
-#define ZT_ICMP6_MULTICAST_LISTENER_DONE 132
-#define ZT_ICMP6_ROUTER_SOLICITATION 133
-#define ZT_ICMP6_ROUTER_ADVERTISEMENT 134
-#define ZT_ICMP6_NEIGHBOR_SOLICITATION 135
-#define ZT_ICMP6_NEIGHBOR_ADVERTISEMENT 136
-#define ZT_ICMP6_REDIRECT_MESSAGE 137
-#define ZT_ICMP6_ROUTER_RENUMBERING 138
-#define ZT_ICMP6_NODE_INFORMATION_QUERY 139
-#define ZT_ICMP6_NODE_INFORMATION_RESPONSE 140
-#define ZT_ICMP6_INV_NEIGHBOR_SOLICITATION 141
-#define ZT_ICMP6_INV_NEIGHBOR_ADVERTISEMENT 142
-#define ZT_ICMP6_MLDV2 143
-#define ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST 144
-#define ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY 145
-#define ZT_ICMP6_MOBILE_PREFIX_SOLICITATION 146
-#define ZT_ICMP6_MOBILE_PREFIX_ADVERTISEMENT 147
-#define ZT_ICMP6_CERTIFICATION_PATH_SOLICITATION 148
-#define ZT_ICMP6_CERTIFICATION_PATH_ADVERTISEMENT 149
-#define ZT_ICMP6_MULTICAST_ROUTER_ADVERTISEMENT 151
-#define ZT_ICMP6_MULTICAST_ROUTER_SOLICITATION 152
-#define ZT_ICMP6_MULTICAST_ROUTER_TERMINATION 153
-#define ZT_ICMP6_RPL_CONTROL_MESSAGE 155
-
-namespace ZeroTier {
-
-class RuntimeEnvironment;
-
-/**
- * A simple Ethernet frame level filter
- *
- * This doesn't specify actions, since it's used as a deny filter. The rule
- * in ZT1 is "that which is not explicitly prohibited is allowed." (Except for
- * ethertypes, which are handled by a whitelist.)
- */
-class Filter
-{
-public:
- /**
- * Value returned by etherTypeName, etc. on unknown
- *
- * These static methods return precisely this, so a pointer equality
- * check will work.
- */
- static const char *const UNKNOWN_NAME;
-
- /**
- * An empty range as a more idiomatic way of specifying a wildcard match
- */
- static const Range<unsigned int> ANY;
-
- /**
- * A filter rule
- */
- class Rule
- {
- public:
- Rule()
- throw() :
- _etherType(),
- _protocol(),
- _port()
- {
- }
-
- /**
- * Construct a rule from a string-serialized value
- *
- * @param s String formatted rule, such as returned by toString()
- * @throws std::invalid_argument String formatted rule is not valid
- */
- Rule(const char *s)
- throw(std::invalid_argument);
-
- /**
- * Construct a new rule
- *
- * @param etype Ethernet type or empty range for ANY
- * @param prot Protocol or empty range for ANY (meaning depends on ethertype, e.g. IP protocol numbers)
- * @param prt Port or empty range for ANY (only applies to some protocols)
- */
- Rule(const Range<unsigned int> &etype,const Range<unsigned int> &prot,const Range<unsigned int> &prt)
- throw() :
- _etherType(etype),
- _protocol(prot),
- _port(prt)
- {
- }
-
- inline const Range<unsigned int> &etherType() const throw() { return _etherType; }
- inline const Range<unsigned int> &protocol() const throw() { return _protocol; }
- inline const Range<unsigned int> &port() const throw() { return _port; }
-
- /**
- * Test this rule against a frame
- *
- * @param etype Type of ethernet frame
- * @param data Ethernet frame data
- * @param len Length of ethernet frame
- * @return True if rule matches
- * @throws std::invalid_argument Frame invalid or not parseable
- */
- bool operator()(unsigned int etype,const void *data,unsigned int len) const
- throw(std::invalid_argument);
-
- /**
- * Serialize rule as string
- *
- * @return Human readable representation of rule
- */
- std::string toString() const;
-
- inline bool operator==(const Rule &r) const throw() { return ((_etherType == r._etherType)&&(_protocol == r._protocol)&&(_port == r._port)); }
- inline bool operator!=(const Rule &r) const throw() { return !(*this == r); }
- inline bool operator<(const Rule &r) const
- throw()
- {
- if (_etherType < r._etherType)
- return true;
- else if (_etherType == r._etherType) {
- if (_protocol < r._protocol)
- return true;
- else if (_protocol == r._protocol) {
- if (_port < r._port)
- return true;
- }
- }
- return false;
- }
- inline bool operator>(const Rule &r) const throw() { return (r < *this); }
- inline bool operator<=(const Rule &r) const throw() { return !(r < *this); }
- inline bool operator>=(const Rule &r) const throw() { return !(*this < r); }
-
- private:
- Range<unsigned int> _etherType;
- Range<unsigned int> _protocol;
- Range<unsigned int> _port;
- };
-
- Filter() {}
-
- /**
- * @param s String-serialized filter representation
- */
- Filter(const char *s)
- throw(std::invalid_argument);
-
- /**
- * @return Comma-delimited list of string-format rules
- */
- std::string toString() const;
-
- /**
- * Add a rule to this filter
- *
- * @param r Rule to add to filter
- */
- void add(const Rule &r);
-
- inline bool operator()(unsigned int etype,const void *data,unsigned int len) const
- throw(std::invalid_argument)
- {
- for(std::vector<Rule>::const_iterator r(_rules.begin());r!=_rules.end();++r) {
- if ((*r)(etype,data,len))
- return true;
- }
- return false;
- }
-
- static const char *etherTypeName(const unsigned int etherType)
- throw();
- static const char *ipProtocolName(const unsigned int ipp)
- throw();
- static const char *icmpTypeName(const unsigned int icmpType)
- throw();
- static const char *icmp6TypeName(const unsigned int icmp6Type)
- throw();
-
-private:
- std::vector<Rule> _rules;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/attic/SECURITY.md b/attic/SECURITY.md
deleted file mode 100644
index d663f84a..00000000
--- a/attic/SECURITY.md
+++ /dev/null
@@ -1,84 +0,0 @@
-ZeroTier Security
-======
-
-## Summary
-
-
-## Using ZeroTier Securely
-
-### Overall Recommendations
-
-*TL;DR: same as anything else: defense in depth defense in depth defense in depth.*
-
-We encourage our users to treat private ZeroTier networks as being rougly equivalent in security to WPA2-enterprise securied WiFi or on-premise wired Ethernet. (Public networks on the other hand are open by design.) That means they're networks with perimeters, but like all networks the compromise of any participating device or network controller allows an attacker to breach this perimeter.
-
-**Never trust the network.** Many modern security professionals discourage reliance on network perimeters as major components in any security strategy, and we strongly agree regardless of whether your network is physical or virtual.
-
-As part of a defense in depth approach **we specifically encourage the use of other secure protocols and authentication systems over ZeroTier networks**. While the use of secure encrypted protocols like SSH and SSL over ZeroTier adds a bit more overhead, it greatly reduces the chance of total compromise.
-
-Imagine that the per-day probability of a major "0-day" security flaw in ZeroTier and OpenSSH are both roughly 0.001 or one per thousand days. Using both at the same time gives you a cumulative 0-day risk of roughly 0.000001 or one per one million days.
-
-Those are made-up numbers. In reality these probabilities can't be known ahead of time. History shows that a 0-day could be found in anything tomorrow, next week, or never. But layers of security give you an overall posture that is the product -- more than the sum -- of its parts. That's how defense in depth works.
-
-### ZeroTier Specifics
-
-#### Protect Your Identity
-
-Each ZeroTier device has an identity. The secret portion of this identity is stored in a file called "identity.secret." *Protect this file.* If it's stolen your device's identity (as represented by its 10-digit ZeroTier address) can easily be stolen or impersonated and your traffic can be decrypted or man-in-the-middle'd.
-
-#### Protect Your Controller
-
-The second major component of ZeroTier network security is the network controller. It's responsible for issuing certificates and configuration information to all network members. That makes it a certificate authority. Compromise of the controller allows an attacker to join or disrupt any network the controller controls. It does *not*, however, allow an attacker to decrypt peer to peer unicast traffic.
-
-If you are using our controller-as-a-service at [my.zerotier.com](https://my.zerotier.com), you are delegating this responsibility to us.
-
-## Security Priorities
-
-These are our security "must-haves." If the system fails in any of these objectives it is broken.
-
-* ZeroTier must be secure against remote vulnerabilities. This includes things like unauthorized remote control, remote penetration of the device using ZeroTier as a vector, or remote injection of malware.
-
-* The content (but not meta-data) of communication must be secure against eavesdropping on the wire by any known means. (We can't warrant against secret vulnerabilities against ciphers, etc., or anything else we don't know about.)
-
-* Communication must be secure against man-in-the-middle attacks and remote device impersonation.
-
-## Security Non-Priorities
-
-There are a few aspects of security we knowingly do not address, since doing so would be beyond scope or would conflict too greatly with other priorities.
-
-* ZeroTier makes no effort to conceal communication meta-data such as source and destination addresses and the amount of information transferred between peers. To do this more or less requires onion routing or other "heavy" approaches to anonymity, and this is beyond scope.
-
-* ZeroTier does not implement complex certificate chains, X.509, or other feature-rich (some would say feature-laden) cryptographic stuff. We only implement the crypto we need to get the job done.
-
-* We don't take extraordinary measures to preserve security under conditions in which an endpoint device has been penetrated by other means (e.g. "rooted" by third party malware) or physicall compromised. If someone steals your keys they've stolen your keys, and if they've "pwned" your device they can easily eavesdrop on everything directly.
-
-## Insecurities and Areas for Improvement
-
-The only perfectly secure system is one that is off. All real world systems have potential security weaknesses. If possible, we like to know what these are and acknowledge their existence.
-
-In some cases we plan to improve these. In other cases we have deliberately decided to "punt" on them in favor of some other priority (see philosophy). We may or may not revisit this decision in the future.
-
-* We don't implement forward secrecy / ephemeral keys. A [discussion of this can be found at the closed GitHub issue for this feature](https://github.com/zerotier/ZeroTierOne/issues/204). In short: we've decided to "punt" on this feature because it introduces complexity and state negotiation. One of the design goals of ZeroTier is "reliability convergence" -- the reliability of ZeroTier virtual networks should rapidly converge with that of the underlying physical wire. Any state that must be negotiated prior to communication multiplies the probability of delay or failure due to packet loss. We *may* revisit this decision at a later date.
-
-## Secure Coding Practices
-
-The first line of defense employed against remote vulnerabilities and other major security flaws is the use of secure coding practices. These are, in no particular order:
-
-* All parsing of remote messages is performed via higher level safe bounds-checked data structures and interfaces. See node/Buffer.hpp for one of the core elements of this.
-
-* C++ exceptions are used to ensure that any unhandled failure or error condition (such as a bounds checking violation) results in the safe and complete termination of message processing. Invalid messages are dropped and ignored.
-
-* Minimalism is a secure coding practice. There is an exponential relationship between complexity and the probability of bugs, and complex designs are much harder to audit and reason about.
-
-* Our build scripts try to enable any OS and compiler level security features such as ASLR and "stack canaries" on non-debug builds.
-
-## Cryptographic Security Practices
-
-* We use [boring crypto](https://cr.yp.to/talks/2015.10.05/slides-djb-20151005-a4.pdf). A single symmetric algorithm (Salsa20/12), a single asymmetric algorithm (Curve25519 ECDH-256), and a single MAC (Poly1305). The way these algorithms are used is identical to how they're used in the NaCl reference implementation. The protocol supports selection of alternative algorithms but only for "future proofing" in the case that a serious flaw is discovered in any of these. Avoiding algorithm bloat and cryptographic state negotiation helps guard against down-grade, "oracle," and other protocol level attacks.
-
-* Authenticated encryption is employed with authentication being performed prior to any other operations on received messages. See also: [the cryptographic doom principle](https://moxie.org/blog/the-cryptographic-doom-principle/).
-
-* "Never branch on anything secret" -- deterministic-time comparisons and other operations are used in cryptographic operations. See Utils::secureEq() in node/Utils.hpp.
-
-* OS-derived crypographic random numbers (/dev/urandom or Windows CryptGenRandom) are further randomized using encryption by a secondary key with a secondary source of entropy to guard against CSPRNG bugs. Such OS-level CSPRNG bugs have been found in the past. See Utils::getSecureRandom() in node/Utils.hpp.
-
diff --git a/attic/big-http-test/2015-11-10_01_50000.out.xz b/attic/big-http-test/2015-11-10_01_50000.out.xz
deleted file mode 100644
index d3e2a666..00000000
--- a/attic/big-http-test/2015-11-10_01_50000.out.xz
+++ /dev/null
Binary files differ
diff --git a/attic/big-http-test/2015-11-10_02_50000.out.xz b/attic/big-http-test/2015-11-10_02_50000.out.xz
deleted file mode 100644
index 0154da79..00000000
--- a/attic/big-http-test/2015-11-10_02_50000.out.xz
+++ /dev/null
Binary files differ
diff --git a/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz b/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz
deleted file mode 100644
index 3ae3555e..00000000
--- a/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz
+++ /dev/null
Binary files differ
diff --git a/attic/big-http-test/Dockerfile b/attic/big-http-test/Dockerfile
deleted file mode 100644
index e19b3fee..00000000
--- a/attic/big-http-test/Dockerfile
+++ /dev/null
@@ -1,24 +0,0 @@
-FROM centos:latest
-
-MAINTAINER https://www.zerotier.com/
-
-EXPOSE 9993/udp
-
-ADD nodesource-el.repo /etc/yum.repos.d/nodesource-el.repo
-RUN yum -y update && yum install -y nodejs && yum clean all
-
-RUN mkdir -p /var/lib/zerotier-one
-RUN mkdir -p /var/lib/zerotier-one/networks.d
-RUN touch /var/lib/zerotier-one/networks.d/ffffffffffffffff.conf
-
-ADD package.json /
-RUN npm install
-
-ADD zerotier-one /
-RUN chmod a+x /zerotier-one
-
-ADD agent.js /
-ADD docker-main.sh /
-RUN chmod a+x /docker-main.sh
-
-CMD ["./docker-main.sh"]
diff --git a/attic/big-http-test/README.md b/attic/big-http-test/README.md
deleted file mode 100644
index 23a95605..00000000
--- a/attic/big-http-test/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-HTTP one-to-all test
-======
-
-*This is really internal use code. You're free to test it out but expect to do some editing/tweaking to make it work. We used this to run some massive scale tests of our new geo-cluster-based root server infrastructure prior to taking it live.*
-
-Before using this code you will want to edit agent.js to change SERVER_HOST to the IP address of where you will run server.js. This should typically be an open Internet IP, since this makes reporting not dependent upon the thing being tested. Also note that this thing does no security of any kind. It's designed for one-off tests run over a short period of time, not to be anything that runs permanently. You will also want to edit the Dockerfile if you want to build containers and change the network ID to the network you want to run tests over.
-
-This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts.
-
-The Dockerfile builds an image that launches the agent. The image must be "docker run" with "--device=/dev/net/tun --privileged" to permit it to open a tun/tap device within the container. (Unfortunately CAP_NET_ADMIN may not work due to a bug in Docker and/or Linux.) You can run a bunch with a command like:
-
- for ((n=0;n<10;n++)); do docker run --device=/dev/net/tun --privileged -d zerotier/http-test; done
diff --git a/attic/big-http-test/agent.js b/attic/big-http-test/agent.js
deleted file mode 100644
index 9ab2e019..00000000
--- a/attic/big-http-test/agent.js
+++ /dev/null
@@ -1,196 +0,0 @@
-// ZeroTier distributed HTTP test agent
-
-// ---------------------------------------------------------------------------
-// Customizable parameters:
-
-// Time between startup and first test attempt
-var TEST_STARTUP_LAG = 10000;
-
-// Maximum interval between test attempts (actual timing is random % this)
-var TEST_INTERVAL_MAX = (60000 * 10);
-
-// Test timeout in ms
-var TEST_TIMEOUT = 30000;
-
-// Where should I get other agents' IDs and POST results?
-var SERVER_HOST = '52.26.196.147';
-var SERVER_PORT = 18080;
-
-// Which port do agents use to serve up test data to each other?
-var AGENT_PORT = 18888;
-
-// Payload size in bytes
-var PAYLOAD_SIZE = 5000;
-
-// ---------------------------------------------------------------------------
-
-var ipaddr = require('ipaddr.js');
-var os = require('os');
-var http = require('http');
-var async = require('async');
-
-var express = require('express');
-var app = express();
-
-// Find our ZeroTier-assigned RFC4193 IPv6 address
-var thisAgentId = null;
-var interfaces = os.networkInterfaces();
-if (!interfaces) {
- console.error('FATAL: os.networkInterfaces() failed.');
- process.exit(1);
-}
-for(var ifname in interfaces) {
- var ifaddrs = interfaces[ifname];
- if (Array.isArray(ifaddrs)) {
- for(var i=0;i<ifaddrs.length;++i) {
- if (ifaddrs[i].family == 'IPv6') {
- try {
- var ipbytes = ipaddr.parse(ifaddrs[i].address).toByteArray();
- if ((ipbytes.length === 16)&&(ipbytes[0] == 0xfd)&&(ipbytes[9] == 0x99)&&(ipbytes[10] == 0x93)) {
- thisAgentId = '';
- for(var j=0;j<16;++j) {
- var tmp = ipbytes[j].toString(16);
- if (tmp.length === 1)
- thisAgentId += '0';
- thisAgentId += tmp;
- }
- }
- } catch (e) {
- console.error(e);
- }
- }
- }
- }
-}
-if (thisAgentId === null) {
- console.error('FATAL: no ZeroTier-assigned RFC4193 IPv6 addresses found on any local interface!');
- process.exit(1);
-}
-
-//console.log(thisAgentId);
-
-// Create a random (and therefore not very compressable) payload
-var payload = new Buffer(PAYLOAD_SIZE);
-for(var xx=0;xx<PAYLOAD_SIZE;++xx) {
- payload.writeUInt8(Math.round(Math.random() * 255.0),xx);
-}
-
-function agentIdToIp(agentId)
-{
- var ip = '';
- ip += agentId.substr(0,4);
- ip += ':';
- ip += agentId.substr(4,4);
- ip += ':';
- ip += agentId.substr(8,4);
- ip += ':';
- ip += agentId.substr(12,4);
- ip += ':';
- ip += agentId.substr(16,4);
- ip += ':';
- ip += agentId.substr(20,4);
- ip += ':';
- ip += agentId.substr(24,4);
- ip += ':';
- ip += agentId.substr(28,4);
- return ip;
-};
-
-var lastTestResult = null;
-var allOtherAgents = {};
-
-function doTest()
-{
- var submit = http.request({
- host: SERVER_HOST,
- port: SERVER_PORT,
- path: '/'+thisAgentId,
- method: 'POST'
- },function(res) {
- var body = '';
- res.on('data',function(chunk) { body += chunk.toString(); });
- res.on('end',function() {
-
- if (body) {
- try {
- var peers = JSON.parse(body);
- if (Array.isArray(peers)) {
- for(var xx=0;xx<peers.length;++xx)
- allOtherAgents[peers[xx]] = true;
- }
- } catch (e) {}
- }
-
- var agents = Object.keys(allOtherAgents);
- if (agents.length > 1) {
-
- var target = agents[Math.floor(Math.random() * agents.length)];
- while (target === thisAgentId)
- target = agents[Math.floor(Math.random() * agents.length)];
-
- var testRequest = null;
- var timeoutId = null;
- timeoutId = setTimeout(function() {
- if (testRequest !== null)
- testRequest.abort();
- timeoutId = null;
- },TEST_TIMEOUT);
- var startTime = Date.now();
-
- testRequest = http.get({
- host: agentIdToIp(target),
- port: AGENT_PORT,
- path: '/'
- },function(res) {
- var bytes = 0;
- res.on('data',function(chunk) { bytes += chunk.length; });
- res.on('end',function() {
- lastTestResult = {
- source: thisAgentId,
- target: target,
- time: (Date.now() - startTime),
- bytes: bytes,
- timedOut: (timeoutId === null),
- error: null
- };
- if (timeoutId !== null)
- clearTimeout(timeoutId);
- return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
- });
- }).on('error',function(e) {
- lastTestResult = {
- source: thisAgentId,
- target: target,
- time: (Date.now() - startTime),
- bytes: 0,
- timedOut: (timeoutId === null),
- error: e.toString()
- };
- if (timeoutId !== null)
- clearTimeout(timeoutId);
- return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
- });
-
- } else {
- return setTimeout(doTest,1000);
- }
-
- });
- }).on('error',function(e) {
- console.log('POST failed: '+e.toString());
- return setTimeout(doTest,1000);
- });
- if (lastTestResult !== null) {
- submit.write(JSON.stringify(lastTestResult));
- lastTestResult = null;
- }
- submit.end();
-};
-
-// Agents just serve up a test payload
-app.get('/',function(req,res) { return res.status(200).send(payload); });
-
-var expressServer = app.listen(AGENT_PORT,function () {
- // Start timeout-based loop
- setTimeout(doTest(),TEST_STARTUP_LAG);
-});
diff --git a/attic/big-http-test/big-test-kill.sh b/attic/big-http-test/big-test-kill.sh
deleted file mode 100755
index fa7f3cc4..00000000
--- a/attic/big-http-test/big-test-kill.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-# Kills all running Docker containers on all big-test-hosts
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-
-pssh -h big-test-hosts -x '-t -t' -i -OUserKnownHostsFile=/dev/null -OStrictHostKeyChecking=no -t 0 -p 256 "sudo docker ps -aq | xargs -r sudo docker rm -f"
-
-exit 0
diff --git a/attic/big-http-test/big-test-start.sh b/attic/big-http-test/big-test-start.sh
deleted file mode 100755
index 2411eeda..00000000
--- a/attic/big-http-test/big-test-start.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-
-# More than 500 container seems to result in a lot of sporadic failures, probably due to Linux kernel scaling issues with virtual network ports
-# 250 with a 16GB RAM VM like Amazon m4.xlarge seems good
-NUM_CONTAINERS=250
-CONTAINER_IMAGE=zerotier/http-test
-SCALE_UP_DELAY=10
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-
-pssh -h big-test-hosts -x '-t -t' -i -OUserKnownHostsFile=/dev/null -OStrictHostKeyChecking=no -t 0 -p 256 "sudo sysctl -w net.netfilter.nf_conntrack_max=262144 ; for ((n=0;n<$NUM_CONTAINERS;n++)); do sudo docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep $SCALE_UP_DELAY; done"
-
-exit 0
diff --git a/attic/big-http-test/crunch-results.js b/attic/big-http-test/crunch-results.js
deleted file mode 100644
index 50e5c49a..00000000
--- a/attic/big-http-test/crunch-results.js
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Pipe the output of server.js into this to convert raw test results into bracketed statistics
-// suitable for graphing.
-//
-
-// Time duration per statistical bracket
-var BRACKET_SIZE = 10000;
-
-// Number of bytes expected from each test
-var EXPECTED_BYTES = 5000;
-
-var readline = require('readline');
-var rl = readline.createInterface({
- input: process.stdin,
- output: process.stdout,
- terminal: false
-});
-
-var count = 0.0;
-var overallCount = 0.0;
-var totalFailures = 0.0;
-var totalOverallFailures = 0.0;
-var totalMs = 0;
-var totalData = 0;
-var devices = {};
-var lastBracketTs = 0;
-
-rl.on('line',function(line) {
- line = line.trim();
- var ls = line.split(',');
- if (ls.length == 7) {
- var ts = parseInt(ls[0]);
- var fromId = ls[1];
- var toId = ls[2];
- var ms = parseFloat(ls[3]);
- var bytes = parseInt(ls[4]);
- var timedOut = (ls[5] == 'true') ? true : false;
- var errMsg = ls[6];
-
- count += 1.0;
- overallCount += 1.0;
- if ((bytes !== EXPECTED_BYTES)||(timedOut)) {
- totalFailures += 1.0;
- totalOverallFailures += 1.0;
- }
- totalMs += ms;
- totalData += bytes;
-
- devices[fromId] = true;
- devices[toId] = true;
-
- if (lastBracketTs === 0)
- lastBracketTs = ts;
-
- if (((ts - lastBracketTs) >= BRACKET_SIZE)&&(count > 0.0)) {
- console.log(count.toString()+','+overallCount.toString()+','+(totalMs / count)+','+(totalFailures / count)+','+(totalOverallFailures / overallCount)+','+totalData+','+Object.keys(devices).length);
-
- count = 0.0;
- totalFailures = 0.0;
- totalMs = 0;
- totalData = 0;
- lastBracketTs = ts;
- }
- } // else ignore junk
-});
diff --git a/attic/big-http-test/docker-main.sh b/attic/big-http-test/docker-main.sh
deleted file mode 100755
index 29cdced9..00000000
--- a/attic/big-http-test/docker-main.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
-
-/zerotier-one -d >>zerotier-one.out 2>&1
-
-# Wait for ZeroTier to start and join the network
-while [ ! -d "/proc/sys/net/ipv6/conf/zt0" ]; do
- sleep 0.25
-done
-
-# Wait just a bit longer for stuff to settle
-sleep 5
-
-exec node --harmony /agent.js >>agent.out 2>&1
-#exec node --harmony /agent.js
diff --git a/attic/big-http-test/nodesource-el.repo b/attic/big-http-test/nodesource-el.repo
deleted file mode 100644
index b785d3d0..00000000
--- a/attic/big-http-test/nodesource-el.repo
+++ /dev/null
@@ -1,6 +0,0 @@
-[nodesource]
-name=Node.js Packages for Enterprise Linux 7 - $basearch
-baseurl=https://rpm.nodesource.com/pub_4.x/el/7/$basearch
-failovermethod=priority
-enabled=1
-gpgcheck=0
diff --git a/attic/big-http-test/package.json b/attic/big-http-test/package.json
deleted file mode 100644
index 173a6f99..00000000
--- a/attic/big-http-test/package.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "name": "zerotier-test-http",
- "version": "1.0.0",
- "description": "ZeroTier in-network HTTP test",
- "main": "agent.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "ZeroTier, Inc.",
- "license": "GPL-3.0",
- "dependencies": {
- "async": "^1.5.0",
- "express": "^4.13.3",
- "ipaddr.js": "^1.0.3"
- }
-}
diff --git a/attic/big-http-test/server.js b/attic/big-http-test/server.js
deleted file mode 100644
index 629784da..00000000
--- a/attic/big-http-test/server.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// ZeroTier distributed HTTP test coordinator and result-reporting server
-
-// ---------------------------------------------------------------------------
-// Customizable parameters:
-
-var SERVER_PORT = 18080;
-
-// ---------------------------------------------------------------------------
-
-var fs = require('fs');
-
-var express = require('express');
-var app = express();
-
-app.use(function(req,res,next) {
- req.rawBody = '';
- req.on('data', function(chunk) { req.rawBody += chunk.toString(); });
- req.on('end', function() { return next(); });
-});
-
-var knownAgents = {};
-
-app.post('/:agentId',function(req,res) {
- var agentId = req.params.agentId;
- if ((!agentId)||(agentId.length !== 32))
- return res.status(404).send('');
-
- if (req.rawBody) {
- var receiveTime = Date.now();
- var resultData = null;
- try {
- resultData = JSON.parse(req.rawBody);
- console.log(Date.now().toString()+','+resultData.source+','+resultData.target+','+resultData.time+','+resultData.bytes+','+resultData.timedOut+',"'+((resultData.error) ? resultData.error : '')+'"');
- } catch (e) {}
- }
-
- knownAgents[agentId] = true;
- var thisUpdate = [];
- var agents = Object.keys(knownAgents);
- if (agents.length < 100)
- thisUpdate = agents;
- else {
- for(var xx=0;xx<100;++xx)
- thisUpdate.push(agents[Math.floor(Math.random() * agents.length)]);
- }
-
- return res.status(200).send(JSON.stringify(thisUpdate));
-});
-
-var expressServer = app.listen(SERVER_PORT,function () {
- console.log('LISTENING ON '+SERVER_PORT);
- console.log('');
-});
diff --git a/attic/historic/anode/LICENSE.txt b/attic/historic/anode/LICENSE.txt
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/attic/historic/anode/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<http://www.gnu.org/licenses/>.
+
+ 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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/attic/historic/anode/config.mk.Darwin b/attic/historic/anode/config.mk.Darwin
new file mode 100644
index 00000000..a5cbeeec
--- /dev/null
+++ b/attic/historic/anode/config.mk.Darwin
@@ -0,0 +1,17 @@
+CC=gcc
+CXX=g++
+
+#ARCH_FLAGS=-arch x86_64 -arch i386 -arch ppc
+
+DEFS=-DHAS_DEV_URANDOM
+CXXDEFS=-DBOOST_DISABLE_ASSERTS -DBOOST_NO_TYPEID -DNDEBUG
+
+CFLAGS=-mmacosx-version-min=10.4 -std=c99 -O6 -ftree-vectorize -Wall $(DEFS) $(ARCH_FLAGS)
+CXXFLAGS=-mmacosx-version-min=10.4 -Drestrict=__restrict__ -O6 -ftree-vectorize -Wall $(DEFS) $(CXXDEFS) $(ARCH_FLAGS)
+
+LDFLAGS=-mmacosx-version-min=10.4 $(ARCH_FLAGS)
+DLLFLAGS=$(ARCH_FLAGS) -shared
+DLLEXT=dylib
+
+LIBANODE_LIBS=-lcrypto -lpthread -lresolv
+LIBSPARK_LIBS=-lz
diff --git a/attic/historic/anode/config.mk.Linux b/attic/historic/anode/config.mk.Linux
new file mode 100644
index 00000000..072fa4ce
--- /dev/null
+++ b/attic/historic/anode/config.mk.Linux
@@ -0,0 +1,17 @@
+CC=gcc
+CXX=g++
+
+DEFS=-DHAS_DEV_URANDOM
+
+CFLAGS=-std=c99 -O6 -fPIC -Wall $(DEFS)
+CXXFLAGS=-Drestrict=__restrict__ -O6 -Wall $(DEFS) -I..
+
+#CFLAGS=-g -Wall $(DEFS)
+#CXXFLAGS=-g -Wall $(DEFS)
+
+LDFLAGS=
+DLLFLAGS=-shared
+DLLEXT=so
+
+LIBANODE_LIBS=-lcrypto -lresolv -pthread
+LIBSPARK_LIBS=-lz
diff --git a/attic/historic/anode/docs/anode_protocol.txt b/attic/historic/anode/docs/anode_protocol.txt
new file mode 100644
index 00000000..0adb9236
--- /dev/null
+++ b/attic/historic/anode/docs/anode_protocol.txt
@@ -0,0 +1,764 @@
+*****************************************************************************
+Anode Protocol Specification Draft
+Version 0.8
+
+(c)2009-2010 Adam Ierymenko
+*****************************************************************************
+
+Table of Contents
+
+*****************************************************************************
+
+1. Introduction
+
+Anode provides three components that work together to provide a global,
+secure, and mobile addressing system for computer networks:
+
+1) An addressing system based on public key cryptography enabling network
+ devices or applications to assign themselves secure, unique, and globally
+ reachable network addresses in a flat address space.
+
+2) A system enabling network participants holding global addresses to locate
+ one another on local or global networks with "zero configuration."
+
+3) A communications protocol for communication between addressed network
+ participants that requires no special operating system support and no
+ changes to existing network infrastructure.
+
+Using Anode, both fixed and mobile applications and devices can communicate
+directly as if they were all connected to the same VPN. Anode restores the
+original vision of the Internet as a "flat" network where anything can talk
+to anything, and adds the added benefits of address mobility and strong
+protection against address spoofing and other protocol level attacks.
+
+1.1. Design Philosophy
+
+Anode's design philosophy is the classical "KISS" principle: "Keep It Simple
+Stupid." Anode's design principles are:
+
+#1: Do not try to solve too many problems at once, and stay in scope.
+
+Anode does not attempt to solve too many problems at once. It attempts to
+solve the problems of mobile addressing, address portability, and "flat"
+addressing in the presence of NAT or other barriers.
+
+It does not attempt to duplicate the full functionality of SSL, X.509, SSH,
+XMPP, an enterprise service bus, a pub/sub architecture, BitTorrent, etc. All
+of those protocols and services can be used over Anode if their functionality
+is desired.
+
+#2: Avoid state management.
+
+State multiplies the complexity and failure modes of network protocols. State
+also tends to get in the way of the achievement of new features implicitly
+(see principle #4). Avoid state whenever possible.
+
+#3: Avoid algorithm and dependency bloat.
+
+Anode uses only elliptic curve Diffie-Hellman (EC-DH) and AES-256. No other
+cryptographic algorithms or hash functions are presently necessary. This
+yields implementations compact enough for embedded devices.
+
+Anode also requires few or no dependencies, depending on whether the two
+needed cryptographic algorithms are obtained through a library or included.
+No other protocols or libraries are required in an implementation.
+
+#4: Achieve features implicitly.
+
+Use a simple stateless design that allows features to be achieved implicitly
+rather than specified explicitly. For example, Anode can do multi-homing and
+could be used to build a mesh network, but neither of these features is
+explicitly specified.
+
+*****************************************************************************
+
+2. Core Concepts and Algorithms
+
+This section describes addresses, zones, common algorithms, and other core
+concepts.
+
+2.1. Zones
+
+A zone is a 32-bit integer encoded into every Anode address. Zones serve to
+assist in the location of peers by address on global IP networks. They are
+not presently significant for local communications, though they could be
+used to partition addresses into groups or link them with configuration
+options.
+
+Each zone has a corresponding zone file which can be fetched in a number of
+ways (see below). A zone file is a flat text format dictionary of the format
+"key=value" separated by carriage returns. Line feeds are ignored, and any
+character may be escaped with a backslash (\) character. Blank lines are
+ignored.
+
+The following entries must appear in a zone file:
+
+n=<zone name>
+d=<zone description>
+c=<zone contact, e-mail address of zone administrator>
+r=<zone revision, monotonically increasing integer with each edit>
+ttl=<seconds before zone file should be re-checked for changes>
+
+Additional fields may appear as well, including fields specific to special
+applications or protocols supported within the zone. Some of these are
+defined in this document.
+
+Zone file fetching mechanisms are described below. Multiple mechanisms are
+specified to enable fallback in the event that one mechanism is not available.
+
+2.1.1. Zone File Retrieval
+
+Zone files are retrieved via HTTP, with the HTTP address being formed in one
+of two ways.
+
+The preferred DNS method:
+
+To fetch a zone file via DNS, use the zone ID to generate a host name and URI
+of the form:
+
+ http://a--XXXXXXXX.net/z
+
+The XXXXXXXX field is the zone ID in hexadecimal.
+
+The fallback IP method:
+
+For fallback in the absence of DNS, the zone ID can be used directly as an
+IPv4 or IPv4-mapped-to-IPv6 IP address. A URI is generated of the form:
+
+ http://ip_address/z
+
+Support for this method requires that a zone ID be chosen to correspond to a
+permanent IPv4 (preferably mappable to IPv6 space as well) IP address.
+
+2.1.2. Zone ID Reservation
+
+By convention, a zone ID is considered reserved when a domain of the form
+"a--XXXXXXXX.net" (where XXXXXXXX is the ID in hex) is registered.
+
+It is recommended that this be done even for zone IDs not used for global
+address location in order to globally reserve them.
+
+2.2. Addresses
+
+Anode addresses are binary strings containing a 32-bit zone ID, a public key,
+and possibly other fields. Only one address type is presently defined:
+
+|---------------------------------------------------------------------------|
+| Name | Type ID | Elliptic Curve Parameters | Total Length |
+|---------------------------------------------------------------------------|
+| ANODE-256-40 | 1 | NIST-P-256 | 40 |
+|---------------------------------------------------------------------------|
+
+|---------------------------------------------------------------------------|
+| Name | Binary Layout |
+|---------------------------------------------------------------------------|
+| ANODE-256-40 | <type[1]><zone[4]><unused[2]><public key[33]> |
+|---------------------------------------------------------------------------|
+
+The public key is a "compressed" form elliptic curve public key as described
+in RFC5480.
+
+The unused section of the address must be zero. These bytes are reserved for
+future use.
+
+2.2.1. ASCII Format For Addresses
+
+Addresses are encoded in ASCII using base-32, which provides a quotable and
+printable encoding that is of manageable length and is case-insensitive. For
+example, an ANODE-256-40 address is 64 characters long in base-32 encoding.
+
+2.3. Relaying
+
+An Anode peer may optionally relay packets to any other reachable peer.
+Relaying is accomplished by sending a packet to a peer with the recipient set
+to the final recipient. The receiving peer will, if relaying is allowed and if
+it knows of or can reach the recipient, forward the packet.
+
+No error is returned if relaying fails, so relay paths are treated as possible
+paths for communication until a return is received in the same way as direct
+paths.
+
+Relaying can be used by peers to send messages indirectly, locate one
+another, and determine network location information to facilitate the
+establishment of direct communications.
+
+Peers may refuse to relay or may limit the transmission rate at which packets
+can be relayed.
+
+2.3.1. Zone Relays
+
+If a zone's addresses are globally reachable on global IP networks, it must
+have one or more zone relays. These must have globally reachable public
+static IP addresses.
+
+Zone relays are specified in the zone file in the following format:
+
+ zr.<address checksum>=<ip>[,<ip>]:<udp port>:<tcp port>:<anode addresses>
+
+The address checksum is the sum of the bytes in the Anode address modulus
+the number of "zr" entries, in hexadecimal. For example, if a zone had four
+global relays its zone file could contain the lines:
+
+ zr.0=1.2.3.4:4343:4344:klj4j3...
+ zr.1=2.3.4.5:4343:4344:00194j...
+ zr.2=3.4.5.6:4343:4344:1j42zz...
+ zr.3=4.5.6.7:4343:4344:z94j1q...
+
+The relay would be chosen by taking the sum of the bytes in the address
+modulo 4. For example, if the bytes of an address sum to 5081 then relay
+zr.1 would be used to communicate with that address.
+
+If more than one IP address is listed for a given relay, the peer must choose
+at random from among the addresses of the desired type (IPv4 or IPv6).
+
+Each relay must have one Anode address for every address type supported within
+the zone. (At present there is only one address type defined.)
+
+Peers should prefer UDP and fall back to TCP only if UDP is not available.
+
+To make itself available, a peer must make itself known to its designated zone
+relay. This is accomplished by sending a PING message.
+
+2.4. Key Agreement and Derivation
+
+Key agreement is performed using elliptic curve Diffie-Hellman. This yields
+a raw key whose size depends on the elliptic curve parameters in use.
+
+The following algorithm is used to derive a key of any length from a raw
+key generated through key agreement:
+
+1) Zero the derived key buffer.
+2) Determine the largest of the original raw key or the derived key.
+3) Loop from 0 to the largest length determined in step 2, XOR each byte of
+ the derived key buffer with the corresponding byte of the original key
+ buffer with each index being modulus the length of the respective buffer.
+
+2.5. Message Authentication
+
+For message authentication, CMAC-AES (with AES-256) is used. This is also
+known in some literature as OMAC1-AES. The key is derived from key agreement
+between the key pair of the sending peer and the address of the recipient.
+
+2.6. AES-DIGEST
+
+To maintain cryptographic algorithm frugality, a cryptographic hash function
+is constructed from the AES-256 cipher. This hash function uses the common
+Davis-Meyer construction with Merkle-Damgård length padding.
+
+It is described by the following pseudocode:
+
+ byte previous_digest[16]
+ byte digest[16] = { 0,0,... }
+ byte block[32] = { 0,0,... }
+ integer block_counter = 0
+
+ ; digest message
+ for each byte b of message
+ block[block_counter] = b
+ block_counter = block_counter + 1
+ if block_counter == 32 then
+ block_counter = 0
+ save digest[] in previous_digest[]
+ encrypt digest[] with aes-256 using block[] as 256-bit aes-256 key
+ xor digest[] with previous_digest[]
+ end if
+ next
+
+ ; append end marker, do final block
+ block[block_counter] = 0x80
+ block_counter = block_counter + 1
+ zero rest of block[] from block_counter to 15
+ save digest[] in previous_digest[]
+ encrypt digest[] with aes-256 using block[] as 256-bit aes-256 key
+ xor digest[] with previous_digest[]
+
+ ; Merkle-Damgård length padding
+ zero first 8 bytes of block[]
+ fill last 8 bytes of block[] w/64-bit length in big-endian order
+ save digest[] in previous_digest[]
+ encrypt digest[] with aes-256 using block[] as 256-bit aes-128 key
+ xor digest[] with previous_digest[]
+
+ ; digest[] now contains 128-bit message digest
+
+2.7. Short Address Identifiers (Address IDs)
+
+A short 8-byte version of the Anode address is used in the protocol to reduce
+transmission overhead when both sides are already aware of the other's full
+address.
+
+The short address identifier is formed by computing the AES-DIGEST of the
+full address and then XORing the first 8 bytes of the digest with the last
+8 bytes to yield an 8-byte shortened digest.
+
+2.8. DNS Resolution of Anode Addresses
+
+Anode addresses can be saved in DNS TXT records in the following format:
+
+anode:<address in base32 ASCII encoding>
+
+This permits Anode addresses to be resolved from normal DNS host name.
+
+2.9. Packet Transmission Mechanisms
+
+2.9.1. UDP Transmission
+
+The recommended method of sending Anode packets is UDP. Each packet is simply
+sent as a UDP packet.
+
+2.9.2. TCP Transmission
+
+To send packets over TCP, each packet is prefixed by its size as a 16-bit
+integer.
+
+2.9.3. HTTP Transmission
+
+Anode packets may be submitted in HTTP POST transactions for transport over
+networks where HTTP is the only available protocol.
+
+Anode packets are simply prefixed with a 16-byte packet size and concatenated
+together just as they are in a TCP stream. One or more packets may be sent
+with each HTTP POST transaction for improved performance.
+
+Since this method is intended for use in "hostile" or highly restricted
+circumstances, no additional details such as special headers or MIME types
+are specified to allow maximum flexibility. Peers should ignore anything
+other than the payload.
+
+2.10. Endpoints
+
+An endpoint indicates a place where Anode packets may be sent. The following
+endpoint types are specified:
+
+|---------------------------------------------------------------------------|
+| Endpoint Type | Description | Address Format |
+|---------------------------------------------------------------------------|
+| 0x00 | Unspecified | (none) |
+| 0x01 | Ethernet | <mac[6]> |
+| 0x02 | UDP/IPv4 | <ip[4]><port[2]> |
+| 0x03 | TCP/IPv4 | <ip[4]><port[2]> |
+| 0x04 | UDP/IPv6 | <ip[16]><port[2]> |
+| 0x05 | TCP/IPv6 | <ip[16]><port[2]> |
+| 0x06 | HTTP | <null-terminated full URI> |
+|---------------------------------------------------------------------------|
+
+Endpoints are encoded by beginning with a single byte indicating the endpoint
+type followed by the address information required for the given type.
+
+Note that IP ports bear no relationship to Anode protocol ports.
+
+2.11. Notes
+
+All integers in the protocol are transmitted in network (big endian) byte
+order.
+
+*****************************************************************************
+
+3. Common Packet Format
+
+A common header is used for all Anode packets:
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Hop Count | 1 | 8-bit hop count (not included in MAC) |
+| Flags | 1 | 8-bit flags |
+| MAC | 8 | 8 byte shortened CMAC-AES of packet |
+| Sender Address | ? | Full address or short ID of sender |
+| Recipient Address | ? | Full address or short ID of recipient |
+| Peer IDs | 1 | Two 4-bit peer IDs: sender, recipient |
+| Message Type | 1 | 8-bit message type |
+| Message | ? | Message payload |
+|---------------------------------------------------------------------------|
+
+3.1. Hop Count
+
+The hop count begins at zero and must be incremented by each peer that relays
+the packet to another peer. The hop count must not wrap to zero at 255.
+
+Because the hop count is modified in transit, it is not included in MAC
+calculation or authentication.
+
+The hop count is used to prioritize endpoints that are direct over endpoints
+that involve relaying, or to prioritize closer routes over more distant
+ones.
+
+3.2. Flags and Flag Behavior
+
+|---------------------------------------------------------------------------|
+| Flag | Description |
+|---------------------------------------------------------------------------|
+| 0x01 | Sender address fully specified |
+| 0x02 | Recipient address fully specified |
+| 0x04 | Authentication error response |
+|---------------------------------------------------------------------------|
+
+If flag 0x01 is set, then the sender address will be the full address rather
+than a short address identifier. The length of the address can be determined
+from the first byte of the address, which always specifies the address type.
+Flag 0x02 has the same meaning for the recipient address.
+
+A peer must send fully specified sender addresses until it receives a response
+from the recipient. At this point the sender may assume that the recipient
+knows its address and use short a short sender address instead. This
+assumption should time out, with a recommended timeout of 60 seconds.
+
+There is presently no need to send fully specified recipient addresses, but
+the flag is present in case it is needed and must be honored.
+
+Flag 0x04 indicates that this is an error response containing a failed
+authentication error. Since authentication failed, this packet may not have
+a valid MAC. Packets with this flag must never have any effect other than
+to inform of an error. This error, since it is unauthenticated, must never
+have any side effects such as terminating a connection.
+
+3.3. MAC
+
+The MAC is calculated as follows:
+
+1) Temporarily set the 64-bit/8-byte MAC field in the packet to the packet's
+ size as a 64-bit big-endian integer.
+2) Calculate the MAC for the entire packet (excluding the first byte) using
+ the key agreed upon between the sender and the recipient, resulting in a
+ 16 byte full CMAC-AES MAC.
+3) Derive the 8 byte packet MAC by XORing the first 8 bytes of the full 16
+ byte CMAC-AES MAC with the last 8 bytes. Place this into the packet's MAC
+ field.
+
+3.4. Peer IDs
+
+Peer IDs provide a method for up to 15 different peers to share an address,
+each with a unique ID allowing packets to be routed to them individually.
+
+A peer ID of zero indicates "any" or "unspecified." Real peers must have a
+nonzero peer ID. In the normal single peer per address case, any peer ID may
+be used. If multiple peers are to share an address, some implementation-
+dependent method must be used to ensure that each peer has a unique peer ID.
+
+Relaying peers must follow these rules based on the recipient peer ID when
+relaying messages:
+
+ - IF the peer ID is zero or if the peer ID is not known, the message must
+ be forwarded to a random endpoint for the given recipient address.
+ - IF the peer ID is nonzero and matches one or more known endpoints for the
+ given recipient address and peer ID, the message must only be sent to
+ a matching endpoint.
+
+A receiving peer should process any message that it receives regardless of
+whether its recipient peer ID is correct. The peer ID is primarily for relays.
+
+Peers should typically send messages with a nonzero recipient peer ID when
+responding to or involved in a conversation with a specific peer (e.g. a
+streaming connection), and send zero recipient peer IDs otherwise.
+
+3.5. Short Address Conflict Disambiguation
+
+In the unlikely event of two Anode addresses with the same short identifier,
+the recipient should use MAC validation to disambiguate. The peer ID must not
+be relied upon for this purpose.
+
+*****************************************************************************
+
+4. Basic Signaling and Transport Protocol
+
+4.1. Message Types
+
+|---------------------------------------------------------------------------|
+| Type | ID | Description |
+|---------------------------------------------------------------------------|
+| ERROR | 0x00 | Error response |
+| PING | 0x01 | Echo request |
+| PONG | 0x02 | Echo response |
+| EPC_REQ | 0x03 | Endpoint check request |
+| EPC | 0x04 | Endpoint check response |
+| EPI | 0x05 | Endpoint information |
+| NAT_T | 0x06 | NAT traversal message |
+| NETID_REQ | 0x07 | Request network address identification and/or test |
+| NETID | 0x08 | Response to network address identification request |
+| DGRAM | 0x09 | Simple UDP-like datagram |
+|---------------------------------------------------------------------------|
+
+4.2. Message Details
+
+4.2.1. ERROR
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Error Code | 2 | 16-bit error code |
+| Error Arguments | ? | Error arguments, depending on error type |
+|---------------------------------------------------------------------------|
+
+Error arguments are empty unless otherwise stated below.
+
+Error codes:
+
+|---------------------------------------------------------------------------|
+| Error Code | Description |
+|---------------------------------------------------------------------------|
+| 0x01 | Message not valid |
+| 0x02 | Message authentication or decryption failed |
+| 0x03 | Relaying and related features not authorized |
+| 0x04 | Relay recipient not reachable |
+|---------------------------------------------------------------------------|
+
+Generation of errors is optional. A peer may choose to ignore invalid
+messages or to throttle the sending of errors.
+
+4.2.2. PING
+
+(Payload unspecified.)
+
+Request echo of payload as PONG message.
+
+4.2.3. PONG
+
+(Payload unspecified.)
+
+Echoed payload of received PING message.
+
+4.2.4. EPC_REQ
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID |
+|---------------------------------------------------------------------------|
+
+Request echo of request ID in EPC message, used to check and learn endpoints.
+
+To learn a network endpoint for a peer, CHECK_REQ is sent. If CHECK is
+returned with a valid request ID, the endpoint is considered valid.
+
+4.2.5. EPC
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID echoed back |
+|---------------------------------------------------------------------------|
+
+Response to EPC_REQ containing request ID.
+
+4.2.6. EPI
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Flags | 1 | 8-bit flags |
+| Endpoint | ? | Endpoint type and address |
+| NAT-T mode | 1 | 8-bit NAT traversal mode |
+| NAT-T options | ? | Options related to specified NAT-T mode |
+|---------------------------------------------------------------------------|
+
+EPI stands for EndPoint Identification, and is sent to notify another peer of
+a network endpoint where the sending peer is reachable.
+
+If the receiving peer is interested in communicating with the sending peer,
+the receiving peer must send EPC_REQ to the sending peer at the specified
+endpoint to check the validity of that endpoint. The endpoint is learned if a
+valid EPC is returned.
+
+If the endpoint in EPI is unspecified, the actual source of the EPI message
+is the endpoint. This allows EPI messages to be broadcast on a local LAN
+segment to advertise the presence of an address on a local network. EPI
+broadcasts on local IP networks must be made to UDP port 8737.
+
+Usually EPI is sent via relays (usually zone relays) to inform a peer of an
+endpoint for direct communication.
+
+There are presently no flags, so flags must be zero.
+
+4.2.7. NAT_T
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| NAT-T mode | 1 | 8-bit NAT traversal mode |
+| NAT-T options | ? | Options related to specified NAT-T mode |
+|---------------------------------------------------------------------------|
+
+NAT_T is used to send messages specific to certain NAT traversal modes.
+
+4.2.8. NETID_REQ
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID |
+| Endpoint | ? | Endpoint type and address information |
+|---------------------------------------------------------------------------|
+
+When a NETID_REQ message is received, the recipient attempts to echo it back
+as a NETID message to the specified endpoint address. If the endpoint is
+unspecified, the recipient must fill it in with the actual origin of the
+NETID_REQ message. This allows a peer to cooperate with another peer (usually
+a zone relay) to empirically determine its externally visible network
+address information.
+
+A peer may ignore NETID_REQ or respond with an error if it does not allow
+relaying.
+
+4.2.9. NETID
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Request ID | 4 | 32-bit request ID echoed back |
+| Endpoint Type | 1 | 8-bit endpoint type |
+| Endpoint Address | ? | Endpoint Address (size depends on type) |
+|---------------------------------------------------------------------------|
+
+NETID is sent in response to NETID_REQ to the specified endpoint address. It
+always contains the endpoint address to which it was sent.
+
+4.2.10. DGRAM
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Source Port | 2 | 16-bit source port |
+| Destination Port | 2 | 16-bit destination port |
+| Payload | ? | Datagram packet payload |
+|---------------------------------------------------------------------------|
+
+A datagram is a UDP-like message without flow control or delivery assurance.
+
+*****************************************************************************
+
+5. Stream Protocol
+
+The stream protocol is very similar to TCP, though it omits some features
+that are not required since they are taken care of by the encapsulating
+protocol. SCTP was also an inspiration in the design.
+
+5.1. Message Types
+
+|---------------------------------------------------------------------------|
+| Type | ID | Description |
+|---------------------------------------------------------------------------|
+| S_OPEN | 20 | Initiate a streaming connection (like TCP SYN) |
+| S_CLOSE | 21 | Terminate a streaming connection (like TCP RST/FIN) |
+| S_DATA | 22 | Data packet |
+| S_ACK | 23 | Acknowedge receipt of one or more data packets |
+| S_DACK | 24 | Combination of DATA and ACK |
+|---------------------------------------------------------------------------|
+
+5.2. Message Details
+
+5.2.1. S_OPEN
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Window Size | 2 | 16-bit window size in 1024-byte increments |
+| Init. Seq. Number | 4 | 32-bit initial sequence number |
+| Flags | 1 | 8-bit flags |
+|---------------------------------------------------------------------------|
+
+The OPEN message corresponds to TCP SYN, and initiates a connection. It
+specifies the initial window size for the sender and the sender's initial
+sequence number, which should be randomly chosen to prevent replay attacks.
+
+If OPEN is successful, the recipient sends its own OPEN to establish the
+connetion. If OPEN is unsuccessful, CLOSE is sent with its initial and current
+sequence numbers equal and an appropriate reason such as "connection refused."
+
+The sender link ID must be unique for a given recipient.
+
+If flag 01 is set, the sender link ID is actually a source port where the
+sender might be listening for connections as well. This exactly duplicates
+the behavior of standard TCP. Otherwise, the sender link ID is simply an
+arbitrary number that the sender uses to identify the connection with this
+recipient and there is no port of origin. Ports of origin are optional for
+Anode streaming connections to permit greater scalability.
+
+5.2.2. S_CLOSE
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Flags | 1 | 8-bit flags |
+| Reason | 1 | 8-bit close reason |
+| Init. Seq. Number | 4 | 32-bit initial sequence number |
+| Sequence Number | 4 | 32-bit current sequence number |
+|---------------------------------------------------------------------------|
+
+The CLOSE message serves a function similar to TCP FIN. The initial sequence
+number is the original starting sequence number sent with S_OPEN, while the
+current sequence number is the sequence number corresponding to the close
+and must be ACKed to complete the close operation. The use of the initial
+sequence number helps to serve as a key to prevent replay attacks.
+
+CLOSE is also used to indicate a failed OPEN attempt. In this case the current
+sequence number will be equal to the initial sequence number and no ACK will
+be expected.
+
+There are currently no flags, so flags must be zero.
+
+The reason field describes the reason for the close:
+
+|---------------------------------------------------------------------------|
+| Reason Code | Description |
+|---------------------------------------------------------------------------|
+| 00 | Application closed connection |
+| 01 | Connection refused |
+| 02 | Protocol error |
+| 03 | Timed out |
+|---------------------------------------------------------------------------|
+
+Established connections will usually be closed with reason 00, while reason
+01 is usually provided if an OPEN is received but the port is not bound.
+
+5.2.3. S_DATA
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Sequence Number | 4 | 32-bit sequence number |
+| Payload | ? | Data payload |
+|---------------------------------------------------------------------------|
+
+The DATA message carries a packet of data, with the sequence number
+determining order. The sequence number is monotonically incremented with
+each data packet, and wraps at the maximum value of an unsigned 32-bit
+integer.
+
+5.2.4. S_ACK
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Window Size | 2 | 16-bit window size in 1024-byte increments |
+| Acknowledgements | ? | One or more acknowledgements (see below) |
+|---------------------------------------------------------------------------|
+
+Each acknowledgement is a 32-bit integer followed by an 8-bit integer (5 bytes
+total). The 32-bit integer is the first sequence number to acknowledge, and
+the 8-bit integer is the number of sequential following sequence numbers to
+acknowledge. For example "1, 4" would acknowledge sequence numbers 1, 2, 3,
+and 4.
+
+5.2.5. S_DACK
+
+|---------------------------------------------------------------------------|
+| Field | Length | Description |
+|---------------------------------------------------------------------------|
+| Sender Link ID | 2 | 16-bit sender link ID |
+| Destination Port | 2 | 16-bit destination port |
+| Window Size | 2 | 16-bit window size in 1024-byte increments |
+| Num. Acks | 1 | 8-bit number of acknowledgements |
+| Acknowledgements | ? | One or more acknowledgements |
+| Payload | ? | Data payload |
+|---------------------------------------------------------------------------|
+
+The DACK message combines ACK and DATA, allowing two peers that are both
+transmitting data to efficiently ACK without a separate packet.
diff --git a/attic/historic/anode/libanode/Makefile b/attic/historic/anode/libanode/Makefile
new file mode 100644
index 00000000..088587bd
--- /dev/null
+++ b/attic/historic/anode/libanode/Makefile
@@ -0,0 +1,33 @@
+SYSNAME:=${shell uname}
+SYSNAME!=uname
+include ../config.mk.${SYSNAME}
+
+LIBANODE_OBJS= \
+ impl/aes.o \
+ impl/dictionary.o \
+ impl/dns_txt.o \
+ impl/ec.o \
+ impl/environment.o \
+ impl/misc.o \
+ impl/thread.o \
+ address.o \
+ aes_digest.o \
+ errors.o \
+ identity.o \
+ network_address.o \
+ secure_random.o \
+ system_transport.o \
+ uri.o
+# zone.o
+
+all: $(LIBANODE_OBJS)
+ ar rcs libanode.a $(LIBANODE_OBJS)
+ ranlib libanode.a
+ $(CC) $(CFLAGS) -o utils/anode-make-identity utils/anode-make-identity.c $(LIBANODE_OBJS) $(LIBANODE_LIBS)
+
+clean: force
+ rm -f $(LIBANODE_OBJS)
+ rm -f libanode.$(DLLEXT) libanode.a
+ rm -f utils/anode-make-identity
+
+force: ;
diff --git a/attic/historic/anode/libanode/address.c b/attic/historic/anode/libanode/address.c
new file mode 100644
index 00000000..e7ab7837
--- /dev/null
+++ b/attic/historic/anode/libanode/address.c
@@ -0,0 +1,98 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include "impl/aes.h"
+#include "impl/ec.h"
+#include "impl/misc.h"
+#include "impl/types.h"
+#include "anode.h"
+
+int AnodeAddress_calc_short_id(
+ const AnodeAddress *address,
+ AnodeAddressId *short_address_id)
+{
+ unsigned char digest[16];
+
+ switch(AnodeAddress_get_type(address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ Anode_aes_digest(address->bits,ANODE_ADDRESS_LENGTH_ANODE_256_40,digest);
+ break;
+ default:
+ return ANODE_ERR_ADDRESS_INVALID;
+ }
+
+ *((uint64_t *)short_address_id->bits) = ((uint64_t *)digest)[0] ^ ((uint64_t *)digest)[1];
+
+ return 0;
+}
+
+int AnodeAddress_get_zone(const AnodeAddress *address,AnodeZone *zone)
+{
+ switch(AnodeAddress_get_type(address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ *((uint32_t *)&(zone->bits[0])) = *((uint32_t *)&(address->bits[1]));
+ return 0;
+ }
+ return ANODE_ERR_ADDRESS_INVALID;
+}
+
+int AnodeAddress_to_string(const AnodeAddress *address,char *buf,int len)
+{
+ const unsigned char *inptr;
+ char *outptr;
+ unsigned int i;
+
+ switch(AnodeAddress_get_type(address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ if (len < (((ANODE_ADDRESS_LENGTH_ANODE_256_40 / 5) * 8) + 1))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ inptr = (const unsigned char *)address->bits;
+ outptr = buf;
+ for(i=0;i<(ANODE_ADDRESS_LENGTH_ANODE_256_40 / 5);++i) {
+ Anode_base32_5_to_8(inptr,outptr);
+ inptr += 5;
+ outptr += 8;
+ }
+ *outptr = (char)0;
+ return ((ANODE_ADDRESS_LENGTH_ANODE_256_40 / 5) * 8);
+ }
+ return ANODE_ERR_ADDRESS_INVALID;
+}
+
+int AnodeAddress_from_string(const char *str,AnodeAddress *address)
+{
+ const char *blk_start = str;
+ const char *ptr = str;
+ unsigned int address_len = 0;
+
+ while (*ptr) {
+ if ((unsigned long)(ptr - blk_start) == 8) {
+ if ((address_len + 5) > sizeof(address->bits))
+ return ANODE_ERR_ADDRESS_INVALID;
+ Anode_base32_8_to_5(blk_start,(unsigned char *)&(address->bits[address_len]));
+ address_len += 5;
+ blk_start = ptr;
+ }
+ ++ptr;
+ }
+
+ if (ptr != blk_start)
+ return ANODE_ERR_ADDRESS_INVALID;
+ if (AnodeAddress_get_type(address) != ANODE_ADDRESS_ANODE_256_40)
+ return ANODE_ERR_ADDRESS_INVALID;
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/aes_digest.c b/attic/historic/anode/libanode/aes_digest.c
new file mode 100644
index 00000000..07b0fc7a
--- /dev/null
+++ b/attic/historic/anode/libanode/aes_digest.c
@@ -0,0 +1,85 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include "anode.h"
+#include "impl/aes.h"
+#include "impl/misc.h"
+#include "impl/types.h"
+
+void Anode_aes_digest(const void *const message,unsigned long message_len,void *const hash)
+{
+ unsigned char previous_digest[16];
+ unsigned char digest[16];
+ unsigned char block[32];
+ const unsigned char *in = (const unsigned char *)message;
+ const unsigned char *end = in + message_len;
+ unsigned long block_counter;
+ AnodeAesExpandedKey expkey;
+
+ ((uint64_t *)digest)[0] = 0ULL;
+ ((uint64_t *)digest)[1] = 0ULL;
+ ((uint64_t *)block)[0] = 0ULL;
+ ((uint64_t *)block)[1] = 0ULL;
+ ((uint64_t *)block)[2] = 0ULL;
+ ((uint64_t *)block)[3] = 0ULL;
+
+ /* Davis-Meyer hash function built from block cipher */
+ block_counter = 0;
+ while (in != end) {
+ block[block_counter++] = *(in++);
+ if (block_counter == 32) {
+ block_counter = 0;
+ ((uint64_t *)previous_digest)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)previous_digest)[1] = ((uint64_t *)digest)[1];
+ Anode_aes256_expand_key(block,&expkey);
+ Anode_aes256_encrypt(&expkey,digest,digest);
+ ((uint64_t *)digest)[0] ^= ((uint64_t *)previous_digest)[0];
+ ((uint64_t *)digest)[1] ^= ((uint64_t *)previous_digest)[1];
+ }
+ }
+
+ /* Davis-Meyer end marker */
+ block[block_counter++] = 0x80;
+ while (block_counter != 32) block[block_counter++] = 0;
+ ((uint64_t *)previous_digest)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)previous_digest)[1] = ((uint64_t *)digest)[1];
+ Anode_aes256_expand_key(block,&expkey);
+ Anode_aes256_encrypt(&expkey,digest,digest);
+ ((uint64_t *)digest)[0] ^= ((uint64_t *)previous_digest)[0];
+ ((uint64_t *)digest)[1] ^= ((uint64_t *)previous_digest)[1];
+
+ /* Merkle-Damgård length padding */
+ ((uint64_t *)block)[0] = 0ULL;
+ if (sizeof(message_len) >= 8) { /* 32/64 bit? this will get optimized out */
+ block[8] = (uint8_t)((uint64_t)message_len >> 56);
+ block[9] = (uint8_t)((uint64_t)message_len >> 48);
+ block[10] = (uint8_t)((uint64_t)message_len >> 40);
+ block[11] = (uint8_t)((uint64_t)message_len >> 32);
+ } else ((uint32_t *)block)[2] = 0;
+ block[12] = (uint8_t)(message_len >> 24);
+ block[13] = (uint8_t)(message_len >> 16);
+ block[14] = (uint8_t)(message_len >> 8);
+ block[15] = (uint8_t)message_len;
+ ((uint64_t *)previous_digest)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)previous_digest)[1] = ((uint64_t *)digest)[1];
+ Anode_aes256_expand_key(block,&expkey);
+ Anode_aes256_encrypt(&expkey,digest,digest);
+ ((uint64_t *)digest)[0] ^= ((uint64_t *)previous_digest)[0];
+ ((uint64_t *)digest)[1] ^= ((uint64_t *)previous_digest)[1];
+
+ ((uint64_t *)hash)[0] = ((uint64_t *)digest)[0];
+ ((uint64_t *)hash)[1] = ((uint64_t *)digest)[1];
+}
diff --git a/attic/historic/anode/libanode/anode.h b/attic/historic/anode/libanode/anode.h
new file mode 100644
index 00000000..e0c51e2e
--- /dev/null
+++ b/attic/historic/anode/libanode/anode.h
@@ -0,0 +1,795 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_ANODE_H
+#define _ANODE_ANODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#define ANODE_ADDRESS_LENGTH_ANODE_256_40 40
+#define ANODE_ADDRESS_MAX_LENGTH 40
+#define ANODE_ADDRESS_SECRET_LENGTH_ANODE_256_40 32
+#define ANODE_ADDRESS_MAX_SECRET_LENGTH 32
+
+#define ANODE_ADDRESS_ID_LENGTH 8
+#define ANODE_ZONE_LENGTH 4
+
+#define ANODE_ERR_NONE 0
+#define ANODE_ERR_INVALID_ARGUMENT (-10000)
+#define ANODE_ERR_OUT_OF_MEMORY (-10001)
+#define ANODE_ERR_INVALID_URI (-10002)
+#define ANODE_ERR_BUFFER_TOO_SMALL (-10003)
+#define ANODE_ERR_ADDRESS_INVALID (-10010)
+#define ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED (-10011)
+#define ANODE_ERR_CONNECTION_CLOSED (-10012)
+#define ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE (-10013)
+#define ANODE_ERR_CONNECT_FAILED (-10014)
+#define ANODE_ERR_UNABLE_TO_BIND (-10015)
+#define ANODE_ERR_TOO_MANY_OPEN_SOCKETS (-10016)
+#define ANODE_ERR_DNS_NAME_NOT_FOUND_OR_TIMED_OUT (-10017)
+
+/**
+ * Get a human-readable error description for an error code
+ *
+ * The value of 'err' can be either negative or positive.
+ *
+ * @param err Error code
+ * @return Human-readable description
+ */
+extern const char *Anode_strerror(int err);
+
+/* ----------------------------------------------------------------------- */
+/* Secure random source */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Opaque secure random instance
+ */
+typedef void AnodeSecureRandom;
+
+/**
+ * Initialize a secure random source
+ *
+ * No cleanup/destructor is necessary.
+ *
+ * @param srng Random structure to initialize
+ */
+extern AnodeSecureRandom *AnodeSecureRandom_new();
+
+/**
+ * Generate random bytes
+ *
+ * @param srng Secure random source
+ * @param buf Buffer to fill
+ * @param count Number of bytes to generate
+ */
+extern void AnodeSecureRandom_gen_bytes(AnodeSecureRandom *srng,void *buf,long count);
+
+/**
+ * Destroy and free a secure random instance
+ *
+ * @param srng Secure random source
+ */
+extern void AnodeSecureRandom_delete(AnodeSecureRandom *srng);
+
+/* ----------------------------------------------------------------------- */
+/* AES-256 derived Davis-Meyer hash function */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Digest a message using AES-DIGEST to yield a 16-byte hash code
+ *
+ * @param message Message to digest
+ * @param message_len Length of message in bytes
+ * @param hash Buffer to store 16 byte hash code
+ */
+extern void Anode_aes_digest(
+ const void *const message,
+ unsigned long message_len,
+ void *const hash);
+
+/* ----------------------------------------------------------------------- */
+/* Address Types and Components */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Anode address
+ *
+ * The first byte always identifies the address type, which right now can
+ * only be type 1 (ANODE-256-40).
+ */
+typedef struct
+{
+ char bits[ANODE_ADDRESS_MAX_LENGTH];
+} AnodeAddress;
+
+/**
+ * 8-byte short Anode address ID
+ */
+typedef struct
+{
+ char bits[ANODE_ADDRESS_ID_LENGTH];
+} AnodeAddressId;
+
+/**
+ * 4-byte Anode zone ID
+ */
+typedef struct
+{
+ char bits[ANODE_ZONE_LENGTH];
+} AnodeZone;
+
+/**
+ * Anode address types
+ */
+enum AnodeAddressType
+{
+ ANODE_ADDRESS_ANODE_256_40 = 1
+};
+
+/**
+ * Get the type of an Anode address
+ *
+ * This is a shortcut macro for just looking at the first byte and casting
+ * it to the AnodeAddressType enum.
+ *
+ * @param a Pointer to address
+ * @return Type as enum AnodeAddressType
+ */
+#define AnodeAddress_get_type(a) ((enum AnodeAddressType)((a)->bits[0]))
+
+/**
+ * Calculate the short 8 byte address ID from an address
+ *
+ * @param address Binary address
+ * @param short_address_id Buffer to store 8-byte short address ID
+ * @return 0 on success or error code on failure
+ */
+extern int AnodeAddress_calc_short_id(
+ const AnodeAddress *address,
+ AnodeAddressId *short_address_id);
+
+/**
+ * Extract the zone from an anode address
+ *
+ * @param address Binary address
+ * @param zone Zone value-result parameter to fill on success
+ * @return 0 on success or error code on failure
+ */
+extern int AnodeAddress_get_zone(const AnodeAddress *address,AnodeZone *zone);
+
+/**
+ * Convert an address to an ASCII string
+ *
+ * Anode addresses are 64 characters in ASCII form, so the buffer should
+ * have 65 bytes of space.
+ *
+ * @param address Address to convert
+ * @param buf Buffer to receive address in string form (should have 65 bytes of space)
+ * @param len Length of buffer
+ * @return Length of resulting string or a negative error code on error
+ */
+extern int AnodeAddress_to_string(const AnodeAddress *address,char *buf,int len);
+
+/**
+ * Convert a string into an address
+ *
+ * @param str Address in string form
+ * @param address Address buffer to receive result
+ * @return Zero on sucess or error code on error
+ */
+extern int AnodeAddress_from_string(const char *str,AnodeAddress *address);
+
+/**
+ * Supported network address types
+ */
+enum AnodeNetworkAddressType
+{
+ ANODE_NETWORK_ADDRESS_IPV4 = 0,
+ ANODE_NETWORK_ADDRESS_IPV6 = 1,
+ ANODE_NETWORK_ADDRESS_ETHERNET = 2, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_USB = 3, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_BLUETOOTH = 4, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_IPC = 5, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_80211S = 6, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_SERIAL = 7, /* reserved but unused */
+ ANODE_NETWORK_ADDRESS_ANODE_256_40 = 8
+};
+
+/**
+ * Anode network address
+ *
+ * This can contain an address of any type: IPv4, IPv6, or Anode, and is used
+ * with the common transport API.
+ *
+ * The length of the address stored in bits[] is determined by the type.
+ */
+typedef struct
+{
+ enum AnodeNetworkAddressType type;
+ char bits[ANODE_ADDRESS_MAX_LENGTH];
+} AnodeNetworkAddress;
+
+/**
+ * An endpoint with an address and a port
+ */
+typedef struct
+{
+ AnodeNetworkAddress address;
+ int port;
+} AnodeNetworkEndpoint;
+
+/* Constants for binding to any address (v4 or v6) */
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_ANY_V4;
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_ANY_V6;
+
+/* Local host address in v4 and v6 */
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_LOCAL_V4;
+extern const AnodeNetworkAddress AnodeNetworkAddress_IP_LOCAL_V6;
+
+/**
+ * Convert a network address to an ASCII string
+ *
+ * The buffer must have room for a 15 character string for IPv4, a 40 byte
+ * string for IPv6, and a 64 byte string for Anode addresses. This does not
+ * include the trailing null.
+ *
+ * @param address Address to convert
+ * @param buf Buffer to receive address in string form
+ * @param len Length of buffer
+ * @return Length of resulting string or a negative error code on error
+ */
+extern int AnodeNetworkAddress_to_string(const AnodeNetworkAddress *address,char *buf,int len);
+
+/**
+ * Convert a string into a network address of the correct type
+ *
+ * @param str Address in string form
+ * @param address Address buffer to receive result
+ * @return Zero on sucess or error code on error
+ */
+extern int AnodeNetworkAddress_from_string(const char *str,AnodeNetworkAddress *address);
+
+/**
+ * Fill a network endpoint from a C-API sockaddr structure
+ *
+ * The argument must be struct sockaddr_in for IPv4 or sockaddr_in6 for IPv6.
+ * The common sin_family field will be used to differentiate.
+ *
+ * @param sockaddr Pointer to proper sockaddr structure
+ * @param endpoint Endpoint structure to fill
+ * @return Zero on success or error on failure
+ */
+extern int AnodeNetworkEndpoint_from_sockaddr(const void *sockaddr,AnodeNetworkEndpoint *endpoint);
+
+/**
+ * Fill a sockaddr from a network endpoint
+ *
+ * To support either IPv4 or IPv6 addresses, there is a sockaddr_storage
+ * structure in most C APIs. If you supply anything other than an IP address
+ * such as an Anode address, this will return an error.
+ *
+ * @param endpoint Endpoint structure to convert
+ * @param sockaddr Sockaddr structure storage
+ * @param sockaddr_len Length of sockaddr structure storage in bytes
+ * @return Zero on success or error on failure
+ */
+extern int AnodeNetworkEndpoint_to_sockaddr(const AnodeNetworkEndpoint *endpoint,void *sockaddr,int sockaddr_len);
+
+/* ----------------------------------------------------------------------- */
+/* Identity Generation and Management */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Anode identity structure containing address and secret key
+ *
+ * This structure is memcpy-safe, and its members are accessible.
+ */
+typedef struct
+{
+ /* The public Anode address */
+ AnodeAddress address;
+
+ /* Short address ID */
+ AnodeAddressId address_id;
+
+ /* The secret key corresponding with the public address */
+ /* Secret length is determined by address type */
+ char secret[ANODE_ADDRESS_MAX_SECRET_LENGTH];
+} AnodeIdentity;
+
+/**
+ * Generate a new identity
+ *
+ * This generates a public/private key pair and from that generates an
+ * identity containing an address and a secret key.
+ *
+ * @param identity Destination structure to store new identity
+ * @param zone Zone ID
+ * @param type Type of identity to generate
+ * @return Zero on success, error on failure
+ */
+extern int AnodeIdentity_generate(
+ AnodeIdentity *identity,
+ const AnodeZone *zone,
+ enum AnodeAddressType type);
+
+/**
+ * Convert an Anode identity to a string representation
+ *
+ * @param identity Identity to convert
+ * @param dest String buffer
+ * @param dest_len Length of string buffer
+ * @return Length of string created or negative error code on failure
+ */
+extern int AnodeIdentity_to_string(
+ const AnodeIdentity *identity,
+ char *dest,
+ int dest_len);
+
+/**
+ * Convert a string representation to an Anode identity structure
+ *
+ * @param identity Destination structure to fill
+ * @param str C-string containing string representation
+ * @return Zero on success or negative error code on failure
+ */
+extern int AnodeIdentity_from_string(
+ AnodeIdentity *identity,
+ const char *str);
+
+/* ----------------------------------------------------------------------- */
+/* Transport API */
+/* ----------------------------------------------------------------------- */
+
+struct _AnodeTransport;
+typedef struct _AnodeTransport AnodeTransport;
+struct _AnodeEvent;
+typedef struct _AnodeEvent AnodeEvent;
+
+/**
+ * Anode socket
+ */
+typedef struct
+{
+ /* Type of socket (read-only) */
+ enum {
+ ANODE_SOCKET_DATAGRAM = 1,
+ ANODE_SOCKET_STREAM_LISTEN = 2,
+ ANODE_SOCKET_STREAM_CONNECTION = 3
+ } type;
+
+ /* Socket state */
+ enum {
+ ANODE_SOCKET_CLOSED = 0,
+ ANODE_SOCKET_OPEN = 1,
+ ANODE_SOCKET_CONNECTING = 2,
+ } state;
+
+ /* Local address or remote address for stream connections (read-only) */
+ AnodeNetworkEndpoint endpoint;
+
+ /* Name of owning class (read-only) */
+ const char *class_name;
+
+ /* Pointers for end user use (writable) */
+ void *user_ptr[2];
+
+ /* Special handler to receive events or null for default (writable) */
+ void (*event_handler)(const AnodeEvent *event);
+} AnodeSocket;
+
+/**
+ * Anode transport I/O event
+ */
+struct _AnodeEvent
+{
+ enum {
+ ANODE_TRANSPORT_EVENT_DATAGRAM_RECEIVED = 1,
+ ANODE_TRANSPORT_EVENT_STREAM_INCOMING_CONNECT = 2,
+ ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_ESTABLISHED = 3,
+ ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_FAILED = 4,
+ ANODE_TRANSPORT_EVENT_STREAM_CLOSED = 5,
+ ANODE_TRANSPORT_EVENT_STREAM_DATA_RECEIVED = 6,
+ ANODE_TRANSPORT_EVENT_STREAM_AVAILABLE_FOR_WRITE = 7,
+ ANODE_TRANSPORT_EVENT_DNS_RESULT = 8
+ } type;
+
+ AnodeTransport *transport;
+
+ /* Anode socket corresponding to this event */
+ AnodeSocket *sock;
+
+ /* Originating endpoint for incoming datagrams */
+ AnodeNetworkEndpoint *datagram_from;
+
+ /* DNS lookup results */
+ const char *dns_name;
+ AnodeNetworkAddress *dns_addresses;
+ int dns_address_count;
+
+ /* Error code or 0 for none */
+ int error_code;
+
+ /* Data for incoming datagrams and stream received events */
+ int data_length;
+ char *data;
+};
+
+/**
+ * Enum used for dns_resolve method in transport to specify query rules
+ *
+ * This can be specified for ipv4, ipv6, and Anode address types to tell the
+ * DNS resolver when to bother querying for addresses of the given type.
+ * NEVER means to never query for this type, and ALWAYS means to always
+ * query. IF_NO_PREVIOUS means to query for this type if no addresses were
+ * found in previous queries. Addresses are queried in the order of ipv4,
+ * ipv6, then Anode, so if you specify IF_NO_PREVIOUS for all three you will
+ * get addresses in that order of priority.
+ */
+enum AnodeTransportDnsIncludeMode
+{
+ ANODE_TRANSPORT_DNS_QUERY_NEVER = 0,
+ ANODE_TRANSPORT_DNS_QUERY_ALWAYS = 1,
+ ANODE_TRANSPORT_DNS_QUERY_IF_NO_PREVIOUS = 2
+};
+
+struct _AnodeTransport
+{
+ /**
+ * Set the default event handler
+ *
+ * @param transport Transport engine
+ * @param event_handler Default event handler
+ */
+ void (*set_default_event_handler)(AnodeTransport *transport,
+ void (*event_handler)(const AnodeEvent *event));
+
+ /**
+ * Enqueue a function to be executed during a subsequent call to poll()
+ *
+ * This can be called from other threads, so it can be used to pass a
+ * message to the I/O thread in multithreaded applications.
+ *
+ * If it is called from the same thread, the function is still queued to be
+ * run later rather than being run instantly.
+ *
+ * The order in which invoked functions are called is undefined.
+ *
+ * @param transport Transport engine
+ * @param ptr Arbitrary pointer to pass to function to be called
+ * @param func Function to be called
+ */
+ void (*invoke)(AnodeTransport *transport,
+ void *ptr,
+ void (*func)(void *));
+
+ /**
+ * Initiate a forward DNS query
+ *
+ * @param transport Transport instance
+ * @param name DNS name to query
+ * @param event_handler Event handler or null for default event path
+ * @param ipv4_include_mode Inclusion mode for IPv4 addresses
+ * @param ipv6_include_mode Inclusion mode for IPv6 addresses
+ * @param anode_include_mode Inclusion mode for Anode addresses
+ */
+ void (*dns_resolve)(AnodeTransport *transport,
+ const char *name,
+ void (*event_handler)(const AnodeEvent *),
+ enum AnodeTransportDnsIncludeMode ipv4_include_mode,
+ enum AnodeTransportDnsIncludeMode ipv6_include_mode,
+ enum AnodeTransportDnsIncludeMode anode_include_mode);
+
+ /**
+ * Open a datagram socket
+ *
+ * @param transport Transport instance
+ * @param local_address Local address to bind
+ * @param local_port Local port to bind
+ * @param error_code Value-result parameter to receive error code on error
+ * @return Listen socket or null if error (check error_code in error case)
+ */
+ AnodeSocket *(*datagram_listen)(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code);
+
+ /**
+ * Open a socket to listen for incoming stream connections
+ *
+ * @param transport Transport instance
+ * @param local_address Local address to bind
+ * @param local_port Local port to bind
+ * @param error_code Value-result parameter to receive error code on error
+ * @return Listen socket or null if error (check error_code in error case)
+ */
+ AnodeSocket *(*stream_listen)(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code);
+
+ /**
+ * Send a datagram to a network endpoint
+ *
+ * @param transport Transport instance
+ * @param socket Originating datagram socket
+ * @param data Data to send
+ * @param data_len Length of data to send
+ * @param to_endpoint Destination endpoint
+ * @return Zero on success or error code on error
+ */
+ int (*datagram_send)(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len,
+ const AnodeNetworkEndpoint *to_endpoint);
+
+ /**
+ * Initiate an outgoing stream connection attempt
+ *
+ * For IPv4 and IPv6 addresses, this will initiate a TCP connection. For
+ * Anode addresses, Anode's internal streaming protocol will be used.
+ *
+ * @param transport Transport instance
+ * @param to_endpoint Destination endpoint
+ * @param error_code Error code value-result parameter, filled on error
+ * @return Stream socket object or null on error (check error_code)
+ */
+ AnodeSocket *(*stream_connect)(AnodeTransport *transport,
+ const AnodeNetworkEndpoint *to_endpoint,
+ int *error_code);
+
+ /**
+ * Indicate that you are interested in writing to a stream
+ *
+ * This does nothing if the socket is not a stream connection or is not
+ * connected.
+ *
+ * @param transport Transport instance
+ * @param sock Stream connection
+ */
+ void (*stream_start_writing)(AnodeTransport *transport,
+ AnodeSocket *sock);
+
+ /**
+ * Indicate that you are no longer interested in writing to a stream
+ *
+ * This does nothing if the socket is not a stream connection or is not
+ * connected.
+ *
+ * @param transport Transport instance
+ * @param sock Stream connection
+ */
+ void (*stream_stop_writing)(AnodeTransport *transport,
+ AnodeSocket *sock);
+
+ /**
+ * Send data to a stream connection
+ *
+ * This must be called after a stream is indicated to be ready for writing.
+ * It returns the number of bytes actually written, or a negative error
+ * code on failure.
+ *
+ * A return value of zero can occur here, and simply indicates that nothing
+ * was sent. This may occur with certain network stacks on certain
+ * platforms.
+ *
+ * @param transport Transport engine
+ * @param sock Stream socket
+ * @param data Data to send
+ * @param data_len Maximum data to send in bytes
+ * @return Actual data sent or negative error code on error
+ */
+ int (*stream_send)(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len);
+
+ /**
+ * Close a socket
+ *
+ * If the socket is a stream connection in the connected state, this
+ * will generate a stream closed event with a zero error_code to indicate
+ * a normal close.
+ *
+ * @param transport Transport engine
+ * @param sock Socket object
+ */
+ void (*close)(AnodeTransport *transport,
+ AnodeSocket *sock);
+
+ /**
+ * Run main polling loop
+ *
+ * This should be called repeatedly from the I/O thread of your main
+ * process. It blocks until one or more events occur, and then returns
+ * the number of events. Error returns here are fatal and indicate
+ * serious problems such as build or platform issues or a lack of any
+ * network interface.
+ *
+ * Functions queued with invoke() are also called inside here.
+ *
+ * @param transport Transport engine
+ * @return Number of events handled or negative on (fatal) error
+ */
+ int (*poll)(AnodeTransport *transport);
+
+ /**
+ * Check whether transport supports an address type
+ *
+ * Inheriting classes should call their base if they do not natively
+ * speak the specified type.
+ *
+ * @param transport Transport engine
+ * @param at Address type
+ * @return Nonzero if true
+ */
+ int (*supports_address_type)(const AnodeTransport *transport,
+ enum AnodeNetworkAddressType at);
+
+ /**
+ * Get the instance of AnodeTransport under this one (if any)
+ *
+ * @param transport Transport engine
+ * @return Base instance or null if none
+ */
+ AnodeTransport *(*base_instance)(const AnodeTransport *transport);
+
+ /**
+ * @param transport Transport engine
+ * @return Class name of this instance
+ */
+ const char *(*class_name)(AnodeTransport *transport);
+
+ /**
+ * Delete this transport and its base transports
+ *
+ * The 'transport' pointer and any streams or sockets it owns are no longer
+ * valid after this call.
+ *
+ * @param transport Transport engine
+ */
+ void (*delete)(AnodeTransport *transport);
+};
+
+/**
+ * Construct a new system transport
+ *
+ * This is the default base for AnodeTransport, and it is constructed
+ * automatically if 'base' is null in AnodeTransport_new(). However, it also
+ * exposed to the user so that specialized transports (such as those that use
+ * proxy servers) can be developed on top of it. These in turn can be supplied
+ * as 'base' to AnodeTransport_new() to talk Anode over these transports.
+ *
+ * The system transport supports IP protocols and possibly others.
+ *
+ * @param base Base class or null for none (usually null)
+ * @return Base transport engine instance
+ */
+extern AnodeTransport *AnodeSystemTransport_new(AnodeTransport *base);
+
+/**
+ * Construct a new Anode core transport
+ *
+ * This is the transport that talks Anode using the specified base transport.
+ * Requests for other address types are passed through to the base. If the
+ * base is null, an instance of AnodeSystemTransport is used.
+ *
+ * Since transport engines inherit their functionality, this transport
+ * will also do standard IP and everything else that the system transport
+ * supports. Most users will just want to construct this with a null base.
+ *
+ * @param base Base transport to use, or null to use SystemTransport
+ * @return Anode transport engine or null on error
+ */
+extern AnodeTransport *AnodeCoreTransport_new(AnodeTransport *base);
+
+/* ----------------------------------------------------------------------- */
+/* URI Parser */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * URI broken down by component
+ */
+typedef struct
+{
+ char scheme[8];
+ char username[64];
+ char password[64];
+ char host[128];
+ char path[256];
+ char query[256];
+ char fragment[64];
+ int port;
+} AnodeURI;
+
+/**
+ * URI parser
+ *
+ * A buffer too small error will occur if any field is too large for the
+ * AnodeURI structure.
+ *
+ * @param parsed_uri Structure to fill with parsed URI data
+ * @param uri_string URI in string format
+ * @return Zero on success or error on failure
+ */
+extern int AnodeURI_parse(AnodeURI *parsed_uri,const char *uri_string);
+
+/**
+ * Output a URI in string format
+ *
+ * @param uri URI to output as string
+ * @param buf Buffer to store URI string
+ * @param len Length of buffer
+ * @return Buffer or null on error
+ */
+extern char *AnodeURI_to_string(const AnodeURI *uri,char *buf,int len);
+
+/* ----------------------------------------------------------------------- */
+/* Zone File Lookup and Dictionary */
+/* ----------------------------------------------------------------------- */
+
+/**
+ * Zone file dictionary
+ */
+typedef void AnodeZoneFile;
+
+/**
+ * Start asynchronous zone fetch
+ *
+ * When the zone is retrieved, the lookup handler is called. If zone lookup
+ * failed, the zone file argument to the handler will be null.
+ *
+ * @param transport Transport engine
+ * @param zone Zone ID
+ * @param user_ptr User pointer
+ * @param zone_lookup_handler Handler for Anode zone lookup
+ */
+extern void AnodeZoneFile_lookup(
+ AnodeTransport *transport,
+ const AnodeZone *zone,
+ void *ptr,
+ void (*zone_lookup_handler)(const AnodeZone *,AnodeZoneFile *,void *));
+
+/**
+ * Look up a key in a zone file
+ *
+ * @param zone Zone file object
+ * @param key Key to get in zone file
+ */
+extern const char *AnodeZoneFile_get(const AnodeZoneFile *zone,const char *key);
+
+/**
+ * Free a zone file
+ *
+ * @param zone Zone to free
+ */
+extern void AnodeZoneFile_free(AnodeZoneFile *zone);
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/historic/anode/libanode/errors.c b/attic/historic/anode/libanode/errors.c
new file mode 100644
index 00000000..6836bdc4
--- /dev/null
+++ b/attic/historic/anode/libanode/errors.c
@@ -0,0 +1,52 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include "anode.h"
+
+struct AnodeErrDesc
+{
+ int code;
+ const char *desc;
+};
+
+#define TOTAL_ERRORS 12
+static const struct AnodeErrDesc ANODE_ERRORS[TOTAL_ERRORS] = {
+ { ANODE_ERR_NONE, "No error (success)" },
+ { ANODE_ERR_INVALID_ARGUMENT, "Invalid argument" },
+ { ANODE_ERR_OUT_OF_MEMORY, "Out of memory" },
+ { ANODE_ERR_INVALID_URI, "Invalid URI" },
+ { ANODE_ERR_BUFFER_TOO_SMALL, "Supplied buffer too small" },
+ { ANODE_ERR_ADDRESS_INVALID, "Address invalid" },
+ { ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED, "Address type not supported"},
+ { ANODE_ERR_CONNECTION_CLOSED, "Connection closed"},
+ { ANODE_ERR_CONNECT_FAILED, "Connect failed"},
+ { ANODE_ERR_UNABLE_TO_BIND, "Unable to bind to address"},
+ { ANODE_ERR_TOO_MANY_OPEN_SOCKETS, "Too many open sockets"},
+ { ANODE_ERR_DNS_NAME_NOT_FOUND_OR_TIMED_OUT, "DNS name not found or timed out"}
+};
+
+extern const char *Anode_strerror(int err)
+{
+ int i;
+ int negerr = -err;
+
+ for(i=0;i<TOTAL_ERRORS;++i) {
+ if ((ANODE_ERRORS[i].code == err)||(ANODE_ERRORS[i].code == negerr))
+ return ANODE_ERRORS[i].desc;
+ }
+
+ return "Unknown error";
+}
diff --git a/attic/historic/anode/libanode/identity.c b/attic/historic/anode/libanode/identity.c
new file mode 100644
index 00000000..a40f6987
--- /dev/null
+++ b/attic/historic/anode/libanode/identity.c
@@ -0,0 +1,110 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "impl/types.h"
+#include "impl/ec.h"
+#include "impl/misc.h"
+#include "anode.h"
+
+int AnodeIdentity_generate(AnodeIdentity *identity,const AnodeZone *zone,enum AnodeAddressType type)
+{
+ struct AnodeECKeyPair kp;
+
+ switch(type) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ if (!AnodeECKeyPair_generate(&kp))
+ return ANODE_ERR_OUT_OF_MEMORY;
+
+ identity->address.bits[0] = (unsigned char)ANODE_ADDRESS_ANODE_256_40;
+
+ identity->address.bits[1] = zone->bits[0];
+ identity->address.bits[2] = zone->bits[1];
+ identity->address.bits[3] = zone->bits[2];
+ identity->address.bits[4] = zone->bits[3];
+
+ identity->address.bits[5] = 0;
+ identity->address.bits[6] = 0;
+
+ Anode_memcpy((void *)&(identity->address.bits[7]),(const void *)kp.pub.key,ANODE_EC_PUBLIC_KEY_BYTES);
+ Anode_memcpy((void *)identity->secret,(const void *)kp.priv.key,kp.priv.bytes);
+
+ AnodeAddress_calc_short_id(&identity->address,&identity->address_id);
+
+ AnodeECKeyPair_destroy(&kp);
+
+ return 0;
+ }
+
+ return ANODE_ERR_INVALID_ARGUMENT;
+}
+
+int AnodeIdentity_to_string(const AnodeIdentity *identity,char *dest,int dest_len)
+{
+ char hexbuf[128];
+ char strbuf[128];
+ int n;
+
+ if ((n = AnodeAddress_to_string(&identity->address,strbuf,sizeof(strbuf))) <= 0)
+ return n;
+
+ switch(AnodeAddress_get_type(&identity->address)) {
+ case ANODE_ADDRESS_ANODE_256_40:
+ Anode_to_hex((const unsigned char *)identity->secret,ANODE_ADDRESS_SECRET_LENGTH_ANODE_256_40,hexbuf,sizeof(hexbuf));
+ n = snprintf(dest,dest_len,"ANODE-256-40:%s:%s",strbuf,hexbuf);
+ if (n >= dest_len)
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ return n;
+ }
+
+ return ANODE_ERR_INVALID_ARGUMENT;
+}
+
+int AnodeIdentity_from_string(AnodeIdentity *identity,const char *str)
+{
+ char buf[1024];
+ char *id_name;
+ char *address;
+ char *secret;
+ int ec;
+
+ Anode_str_copy(buf,str,sizeof(buf));
+
+ id_name = buf;
+ if (!id_name) return 0;
+ if (!*id_name) return 0;
+ address = (char *)Anode_strchr(id_name,':');
+ if (!address) return 0;
+ if (!*address) return 0;
+ *(address++) = (char)0;
+ secret = (char *)Anode_strchr(address,':');
+ if (!secret) return 0;
+ if (!*secret) return 0;
+ *(secret++) = (char)0;
+
+ if (Anode_strcaseeq("ANODE-256-40",id_name)) {
+ if ((ec = AnodeAddress_from_string(address,&identity->address)))
+ return ec;
+ if (Anode_strlen(secret) != (ANODE_ADDRESS_SECRET_LENGTH_ANODE_256_40 * 2))
+ return ANODE_ERR_INVALID_ARGUMENT;
+ Anode_from_hex(secret,(unsigned char *)identity->secret,sizeof(identity->secret));
+ AnodeAddress_calc_short_id(&identity->address,&identity->address_id);
+ return 0;
+ }
+
+ return ANODE_ERR_INVALID_ARGUMENT;
+}
diff --git a/attic/historic/anode/libanode/impl/aes.c b/attic/historic/anode/libanode/impl/aes.c
new file mode 100644
index 00000000..90e5e23b
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/aes.c
@@ -0,0 +1,72 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include "aes.h"
+
+void Anode_cmac_aes256(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *restrict data,
+ unsigned long data_len,
+ unsigned char *restrict mac)
+{
+ unsigned char cbc[16];
+ unsigned char pad[16];
+ const unsigned char *restrict pos = data;
+ unsigned long i;
+ unsigned long remaining = data_len;
+ unsigned char c;
+
+ ((uint64_t *)((void *)cbc))[0] = 0ULL;
+ ((uint64_t *)((void *)cbc))[1] = 0ULL;
+
+ while (remaining >= 16) {
+ ((uint64_t *)((void *)cbc))[0] ^= ((uint64_t *)((void *)pos))[0];
+ ((uint64_t *)((void *)cbc))[1] ^= ((uint64_t *)((void *)pos))[1];
+ pos += 16;
+ if (remaining > 16)
+ Anode_aes256_encrypt(expkey,cbc,cbc);
+ remaining -= 16;
+ }
+
+ ((uint64_t *)((void *)pad))[0] = 0ULL;
+ ((uint64_t *)((void *)pad))[1] = 0ULL;
+ Anode_aes256_encrypt(expkey,pad,pad);
+
+ c = pad[0] & 0x80;
+ for(i=0;i<15;++i)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[15] <<= 1;
+ if (c)
+ pad[15] ^= 0x87;
+
+ if (remaining||(!data_len)) {
+ for(i=0;i<remaining;++i)
+ cbc[i] ^= *(pos++);
+ cbc[remaining] ^= 0x80;
+
+ c = pad[0] & 0x80;
+ for(i=0;i<15;++i)
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+ pad[15] <<= 1;
+ if (c)
+ pad[15] ^= 0x87;
+ }
+
+ ((uint64_t *)((void *)mac))[0] = ((uint64_t *)((void *)pad))[0] ^ ((uint64_t *)((void *)cbc))[0];
+ ((uint64_t *)((void *)mac))[1] = ((uint64_t *)((void *)pad))[1] ^ ((uint64_t *)((void *)cbc))[1];
+
+ Anode_aes256_encrypt(expkey,mac,mac);
+}
diff --git a/attic/historic/anode/libanode/impl/aes.h b/attic/historic/anode/libanode/impl/aes.h
new file mode 100644
index 00000000..25e33933
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/aes.h
@@ -0,0 +1,64 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_AES_H
+#define _ANODE_AES_H
+
+#include <openssl/aes.h>
+#include "types.h"
+
+/* This just glues us to OpenSSL's built-in AES-256 implementation */
+
+#define ANODE_AES_BLOCK_SIZE 16
+#define ANODE_AES_KEY_SIZE 32
+
+typedef AES_KEY AnodeAesExpandedKey;
+
+#define Anode_aes256_expand_key(k,ek) AES_set_encrypt_key((const unsigned char *)(k),256,(AES_KEY *)(ek))
+
+/* Note: in and out can be the same thing */
+#define Anode_aes256_encrypt(ek,in,out) AES_encrypt((const unsigned char *)(in),(unsigned char *)(out),(const AES_KEY *)(ek))
+
+/* Note: iv is modified */
+static inline void Anode_aes256_cfb_encrypt(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *in,
+ unsigned char *out,
+ unsigned char *iv,
+ unsigned long len)
+{
+ int tmp = 0;
+ AES_cfb128_encrypt(in,out,len,(const AES_KEY *)expkey,iv,&tmp,AES_ENCRYPT);
+}
+static inline void Anode_aes256_cfb_decrypt(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *in,
+ unsigned char *out,
+ unsigned char *iv,
+ unsigned long len)
+{
+ int tmp = 0;
+ AES_cfb128_encrypt(in,out,len,(const AES_KEY *)expkey,iv,&tmp,AES_DECRYPT);
+}
+
+/* CMAC message authentication code */
+void Anode_cmac_aes256(
+ const AnodeAesExpandedKey *expkey,
+ const unsigned char *restrict data,
+ unsigned long data_len,
+ unsigned char *restrict mac);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/dictionary.c b/attic/historic/anode/libanode/impl/dictionary.c
new file mode 100644
index 00000000..060c3815
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dictionary.c
@@ -0,0 +1,239 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "dictionary.h"
+
+static const char *EMPTY_STR = "";
+
+void AnodeDictionary_clear(struct AnodeDictionary *d)
+{
+ struct AnodeDictionaryEntry *e,*ne;
+ int oldcs;
+ unsigned int i;
+
+ oldcs = d->case_sensitive;
+
+ for(i=0;i<ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE;++i) {
+ e = d->ht[i];
+ while (e) {
+ ne = e->next;
+ if ((e->key)&&(e->key != EMPTY_STR)) free((void *)e->key);
+ if ((e->value)&&(e->value != EMPTY_STR)) free((void *)e->value);
+ free((void *)e);
+ e = ne;
+ }
+ }
+
+ Anode_zero((void *)d,sizeof(struct AnodeDictionary));
+
+ d->case_sensitive = oldcs;
+}
+
+void AnodeDictionary_put(struct AnodeDictionary *d,const char *key,const char *value)
+{
+ struct AnodeDictionaryEntry *e;
+ char *p1;
+ const char *p2;
+ unsigned int bucket = (d->case_sensitive) ? AnodeDictionary__get_bucket(key) : AnodeDictionary__get_bucket_ci(key);
+ unsigned int len,i;
+
+ e = d->ht[bucket];
+ while (e) {
+ if (((d->case_sensitive) ? Anode_streq(key,e->key) : Anode_strcaseeq(key,e->key))) {
+ if (!d->case_sensitive) {
+ p1 = e->key;
+ p2 = key;
+ while (*p2) *(p1++) = *(p2++);
+ }
+
+ len = 0;
+ while (value[len]) ++len;
+ if (len) {
+ if ((e->value)&&(e->value != EMPTY_STR))
+ e->value = (char *)realloc((void *)e->value,len + 1);
+ else e->value = (char *)malloc(len + 1);
+ for(i=0;i<len;++i) e->value[i] = value[i];
+ e->value[i] = (char)0;
+ } else {
+ if ((e->value)&&(e->value != EMPTY_STR)) free((void *)e->value);
+ e->value = (char *)EMPTY_STR;
+ }
+ return;
+ }
+ e = e->next;
+ }
+
+ e = (struct AnodeDictionaryEntry *)malloc(sizeof(struct AnodeDictionaryEntry));
+
+ len = 0;
+ while (key[len]) ++len;
+ if (len) {
+ e->key = (char *)malloc(len + 1);
+ for(i=0;i<len;++i) e->key[i] = key[i];
+ e->key[i] = (char)0;
+ } else e->key = (char *)EMPTY_STR;
+
+ len = 0;
+ while (value[len]) ++len;
+ if (len) {
+ e->value = (char *)malloc(len + 1);
+ for(i=0;i<len;++i) e->value[i] = value[i];
+ e->value[i] = (char)0;
+ } else e->value = (char *)EMPTY_STR;
+
+ e->next = d->ht[bucket];
+ d->ht[bucket] = e;
+
+ ++d->size;
+}
+
+void AnodeDictionary_read(
+ struct AnodeDictionary *d,
+ char *in,
+ const char *line_breaks,
+ const char *kv_breaks,
+ const char *comment_chars,
+ char escape_char,
+ int trim_whitespace_from_keys,
+ int trim_whitespace_from_values)
+{
+ char *line = in;
+ char *key;
+ char *value;
+ char *p1,*p2,*p3;
+ char last = ~escape_char;
+ int eof_state = 0;
+
+ for(;;) {
+ if ((!*in)||((Anode_strchr(line_breaks,*in))&&((last != escape_char)||(!escape_char)))) {
+ if (!*in)
+ eof_state = 1;
+ else *in = (char)0;
+
+ if ((*line)&&((comment_chars)&&(!Anode_strchr(comment_chars,*line)))) {
+ key = line;
+
+ while (*line) {
+ if ((Anode_strchr(kv_breaks,*line))&&((last != escape_char)||(!escape_char))) {
+ *(line++) = (char)0;
+ break;
+ } else last = *(line++);
+ }
+ while ((*line)&&(Anode_strchr(kv_breaks,*line))&&((last != escape_char)||(!escape_char)))
+ last = *(line++);
+ value = line;
+
+ if (escape_char) {
+ p1 = key;
+ while (*p1) {
+ if (*p1 == escape_char) {
+ p2 = p1;
+ p3 = p1 + 1;
+ while (*p3)
+ *(p2++) = *(p3++);
+ *p2 = (char)0;
+ }
+ ++p1;
+ }
+ p1 = value;
+ while (*p1) {
+ if (*p1 == escape_char) {
+ p2 = p1;
+ p3 = p1 + 1;
+ while (*p3)
+ *(p2++) = *(p3++);
+ *p2 = (char)0;
+ }
+ ++p1;
+ }
+ }
+
+ if (trim_whitespace_from_keys)
+ Anode_trim(key);
+ if (trim_whitespace_from_values)
+ Anode_trim(value);
+
+ AnodeDictionary_put(d,key,value);
+ }
+
+ if (eof_state)
+ break;
+ else line = in + 1;
+ }
+ last = *(in++);
+ }
+}
+
+long AnodeDictionary_write(
+ struct AnodeDictionary *d,
+ char *out,
+ long out_size,
+ const char *line_break,
+ const char *kv_break)
+{
+ struct AnodeDictionaryEntry *e;
+ const char *tmp;
+ long ptr = 0;
+ unsigned int bucket;
+
+ if (out_size <= 0)
+ return -1;
+
+ for(bucket=0;bucket<ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE;++bucket) {
+ e = d->ht[bucket];
+ while (e) {
+ tmp = e->key;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ tmp = kv_break;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ tmp = e->value;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ tmp = line_break;
+ if (tmp) {
+ while (*tmp) {
+ out[ptr++] = *tmp++;
+ if (ptr >= (out_size - 1)) return -1;
+ }
+ }
+
+ e = e->next;
+ }
+ }
+
+ out[ptr] = (char)0;
+
+ return ptr;
+}
diff --git a/attic/historic/anode/libanode/impl/dictionary.h b/attic/historic/anode/libanode/impl/dictionary.h
new file mode 100644
index 00000000..48e1642a
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dictionary.h
@@ -0,0 +1,126 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+/* This is a simple string hash table suitable for small tables such as zone
+ * files or HTTP header lists. */
+
+#ifndef _ANODE_DICTIONARY_H
+#define _ANODE_DICTIONARY_H
+
+#include "misc.h"
+
+/* This is a fixed hash table and is designed for relatively small numbers
+ * of keys for things like zone files. */
+#define ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE 16
+#define ANODE_DICTIONARY_FIXED_HASH_TABLE_MASK 15
+
+/* Computes a hash code for a string and returns the hash bucket */
+static inline unsigned int AnodeDictionary__get_bucket(const char *s)
+{
+ unsigned int hc = 3;
+ while (*s)
+ hc = ((hc << 4) + hc) + (unsigned int)*(s++);
+ return ((hc ^ (hc >> 4)) & ANODE_DICTIONARY_FIXED_HASH_TABLE_MASK);
+}
+/* Case insensitive version of get_bucket */
+static inline unsigned int AnodeDictionary__get_bucket_ci(const char *s)
+{
+ unsigned int hc = 3;
+ while (*s)
+ hc = ((hc << 4) + hc) + (unsigned int)Anode_tolower(*(s++));
+ return ((hc ^ (hc >> 4)) & ANODE_DICTIONARY_FIXED_HASH_TABLE_MASK);
+}
+
+struct AnodeDictionaryEntry
+{
+ char *key;
+ char *value;
+ struct AnodeDictionaryEntry *next;
+};
+
+struct AnodeDictionary
+{
+ struct AnodeDictionaryEntry *ht[ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE];
+ unsigned int size;
+ int case_sensitive;
+};
+
+static inline void AnodeDictionary_init(struct AnodeDictionary *d,int case_sensitive)
+{
+ Anode_zero((void *)d,sizeof(struct AnodeDictionary));
+ d->case_sensitive = case_sensitive;
+}
+
+void AnodeDictionary_clear(struct AnodeDictionary *d);
+
+static inline void AnodeDictionary_destroy(struct AnodeDictionary *d)
+{
+ AnodeDictionary_clear(d);
+}
+
+void AnodeDictionary_put(struct AnodeDictionary *d,const char *key,const char *value);
+
+static inline const char *AnodeDictionary_get(struct AnodeDictionary *d,const char *key)
+{
+ struct AnodeDictionaryEntry *e;
+ unsigned int bucket = (d->case_sensitive) ? AnodeDictionary__get_bucket(key) : AnodeDictionary__get_bucket_ci(key);
+
+ e = d->ht[bucket];
+ while (e) {
+ if ((d->case_sensitive ? Anode_streq(key,e->key) : Anode_strcaseeq(key,e->key)))
+ return e->value;
+ e = e->next;
+ }
+
+ return (const char *)0;
+}
+
+static inline void AnodeDictionary_iterate(
+ struct AnodeDictionary *d,
+ void *arg,
+ int (*func)(void *,const char *,const char *))
+{
+ struct AnodeDictionaryEntry *e;
+ unsigned int bucket;
+
+ for(bucket=0;bucket<ANODE_DICTIONARY_FIXED_HASH_TABLE_SIZE;++bucket) {
+ e = d->ht[bucket];
+ while (e) {
+ if (!func(arg,e->key,e->value))
+ return;
+ e = e->next;
+ }
+ }
+}
+
+void AnodeDictionary_read(
+ struct AnodeDictionary *d,
+ char *in,
+ const char *line_breaks,
+ const char *kv_breaks,
+ const char *comment_chars,
+ char escape_char,
+ int trim_whitespace_from_keys,
+ int trim_whitespace_from_values);
+
+long AnodeDictionary_write(
+ struct AnodeDictionary *d,
+ char *out,
+ long out_size,
+ const char *line_break,
+ const char *kv_break);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/dns_txt.c b/attic/historic/anode/libanode/impl/dns_txt.c
new file mode 100644
index 00000000..b5cf1318
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dns_txt.c
@@ -0,0 +1,93 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#include "dns_txt.h"
+
+#ifndef C_IN
+#define C_IN ns_c_in
+#endif
+#ifndef T_TXT
+#define T_TXT ns_t_txt
+#endif
+
+static volatile int Anode_resolver_initialized = 0;
+
+int Anode_sync_resolve_txt(const char *host,char *txt,unsigned int txt_len)
+{
+ unsigned char answer[16384],*pptr,*end;
+ char name[16384];
+ int len,explen,i;
+
+ if (!Anode_resolver_initialized) {
+ Anode_resolver_initialized = 1;
+ res_init();
+ }
+
+ /* Do not taunt happy fun ball. */
+
+ len = res_search(host,C_IN,T_TXT,answer,sizeof(answer));
+ if (len > 12) {
+ pptr = answer + 12;
+ end = answer + len;
+
+ explen = dn_expand(answer,end,pptr,name,sizeof(name));
+ if (explen > 0) {
+ pptr += explen;
+
+ if ((pptr + 2) >= end) return 2;
+ if (ntohs(*((uint16_t *)pptr)) == T_TXT) {
+ pptr += 4;
+ if (pptr >= end) return 2;
+
+ explen = dn_expand(answer,end,pptr,name,sizeof(name));
+ if (explen > 0) {
+ pptr += explen;
+
+ if ((pptr + 2) >= end) return 2;
+ if (ntohs(*((uint16_t *)pptr)) == T_TXT) {
+ pptr += 10;
+ if (pptr >= end) return 2;
+
+ len = *(pptr++);
+ if (len <= 0) return 2;
+ if ((pptr + len) > end) return 2;
+
+ if (txt_len < (len + 1))
+ return 4;
+ else {
+ for(i=0;i<len;++i)
+ txt[i] = pptr[i];
+ txt[len] = (char)0;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
diff --git a/attic/historic/anode/libanode/impl/dns_txt.h b/attic/historic/anode/libanode/impl/dns_txt.h
new file mode 100644
index 00000000..252865df
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/dns_txt.h
@@ -0,0 +1,37 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_DNS_TXT_H
+#define _ANODE_DNS_TXT_H
+
+/**
+ * Synchronous TXT resolver routine
+ *
+ * Error codes:
+ * 1 - I/O error
+ * 2 - Invalid response
+ * 3 - TXT record not found
+ * 4 - Destination buffer too small for result
+ *
+ * @param host Host name
+ * @param txt Buffer to store TXT result
+ * @param txt_len Size of buffer
+ * @return Zero on success, special error code on failure
+ */
+int Anode_sync_resolve_txt(const char *host,char *txt,unsigned int txt_len);
+
+#endif
+
diff --git a/attic/historic/anode/libanode/impl/ec.c b/attic/historic/anode/libanode/impl/ec.c
new file mode 100644
index 00000000..2604b4a9
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/ec.c
@@ -0,0 +1,219 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/bn.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rand.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include "types.h"
+#include "misc.h"
+#include "ec.h"
+
+static EC_GROUP *AnodeEC_group = (EC_GROUP *)0;
+
+static void *AnodeEC_KDF(const void *in,size_t inlen,void *out,size_t *outlen)
+{
+ unsigned long i,longest_length;
+
+ if (!*outlen)
+ return out;
+
+ for(i=0;i<(unsigned long)*outlen;++i)
+ ((unsigned char *)out)[i] = (unsigned char)0;
+
+ longest_length = inlen;
+ if (longest_length < *outlen)
+ longest_length = *outlen;
+ for(i=0;i<longest_length;++i)
+ ((unsigned char *)out)[i % (unsigned long)*outlen] ^= ((const unsigned char *)in)[i % (unsigned long)inlen];
+
+ return out;
+}
+
+int AnodeECKeyPair_generate(struct AnodeECKeyPair *pair)
+{
+ EC_KEY *key;
+ int len;
+
+#ifdef HAS_DEV_URANDOM
+ char buf[128];
+ FILE *f = fopen("/dev/urandom","r");
+ if (f) {
+ if (fread(buf,1,sizeof(buf),f) == sizeof(buf))
+ RAND_add(buf,sizeof(buf),sizeof(buf)/2);
+ fclose(f);
+ }
+#endif
+
+ if (!AnodeEC_group) {
+ AnodeEC_group = EC_GROUP_new_by_curve_name(ANODE_EC_GROUP);
+ if (!AnodeEC_group) return 0;
+ }
+
+ key = EC_KEY_new();
+ if (!key) return 0;
+
+ if (!EC_KEY_set_group(key,AnodeEC_group)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+
+ if (!EC_KEY_generate_key(key)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+
+ Anode_zero(pair,sizeof(struct AnodeECKeyPair));
+
+ /* Stuff the private key into priv.key */
+ len = BN_num_bytes(EC_KEY_get0_private_key(key));
+ if ((len > ANODE_EC_PRIME_BYTES)||(len < 0)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ BN_bn2bin(EC_KEY_get0_private_key(key),&(pair->priv.key[ANODE_EC_PRIME_BYTES - len]));
+ pair->priv.bytes = ANODE_EC_PRIME_BYTES;
+
+ len = EC_POINT_point2oct(AnodeEC_group,EC_KEY_get0_public_key(key),POINT_CONVERSION_COMPRESSED,pair->pub.key,sizeof(pair->pub.key),0);
+ if (len != ANODE_EC_PUBLIC_KEY_BYTES) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ pair->pub.bytes = ANODE_EC_PUBLIC_KEY_BYTES;
+
+ /* Keep a copy of OpenSSL's structure around so we don't have to re-init
+ * it every time we use our key pair structure. */
+ pair->internal_key = key;
+
+ return 1;
+}
+
+int AnodeECKeyPair_init(struct AnodeECKeyPair *pair,const struct AnodeECKey *pub,const struct AnodeECKey *priv)
+{
+ EC_KEY *key;
+ EC_POINT *kxy;
+ BIGNUM *pn;
+
+ if (!AnodeEC_group) {
+ AnodeEC_group = EC_GROUP_new_by_curve_name(ANODE_EC_GROUP);
+ if (!AnodeEC_group) return 0;
+ }
+
+ key = EC_KEY_new();
+ if (!key)
+ return 0;
+
+ if (!EC_KEY_set_group(key,AnodeEC_group)) {
+ EC_KEY_free(key);
+ return 0;
+ }
+
+ /* Grab the private key */
+ if (priv->bytes != ANODE_EC_PRIME_BYTES) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ pn = BN_new();
+ if (!pn) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ if (!BN_bin2bn(priv->key,ANODE_EC_PRIME_BYTES,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return 0;
+ }
+ if (!EC_KEY_set_private_key(key,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return 0;
+ }
+ BN_free(pn);
+
+ /* Set the public key */
+ if (pub->bytes != ANODE_EC_PUBLIC_KEY_BYTES) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ kxy = EC_POINT_new(AnodeEC_group);
+ if (!kxy) {
+ EC_KEY_free(key);
+ return 0;
+ }
+ EC_POINT_oct2point(AnodeEC_group,kxy,pub->key,ANODE_EC_PUBLIC_KEY_BYTES,0);
+ if (!EC_KEY_set_public_key(key,kxy)) {
+ EC_POINT_free(kxy);
+ EC_KEY_free(key);
+ return 0;
+ }
+ EC_POINT_free(kxy);
+
+ Anode_zero(pair,sizeof(struct AnodeECKeyPair));
+ Anode_memcpy((void *)&(pair->pub),(const void *)pub,sizeof(struct AnodeECKey));
+ Anode_memcpy((void *)&(pair->priv),(const void *)priv,sizeof(struct AnodeECKey));
+ pair->internal_key = key;
+
+ return 1;
+}
+
+void AnodeECKeyPair_destroy(struct AnodeECKeyPair *pair)
+{
+ if (pair) {
+ if (pair->internal_key)
+ EC_KEY_free((EC_KEY *)pair->internal_key);
+ }
+}
+
+int AnodeECKeyPair_agree(const struct AnodeECKeyPair *my_key_pair,const struct AnodeECKey *their_pub_key,unsigned char *key_buf,unsigned int key_len)
+{
+ EC_POINT *pub;
+ int i;
+
+ if (!AnodeEC_group) {
+ AnodeEC_group = EC_GROUP_new_by_curve_name(ANODE_EC_GROUP);
+ if (!AnodeEC_group) return 0;
+ }
+
+ if (!my_key_pair->internal_key)
+ return 0;
+
+ if (their_pub_key->bytes != ANODE_EC_PUBLIC_KEY_BYTES)
+ return 0;
+ pub = EC_POINT_new(AnodeEC_group);
+ if (!pub)
+ return 0;
+ EC_POINT_oct2point(AnodeEC_group,pub,their_pub_key->key,ANODE_EC_PUBLIC_KEY_BYTES,0);
+
+ i = ECDH_compute_key(key_buf,key_len,pub,(EC_KEY *)my_key_pair->internal_key,&AnodeEC_KDF);
+ if (i != (int)key_len) {
+ EC_POINT_free(pub);
+ return 0;
+ }
+
+ EC_POINT_free(pub);
+
+ return 1;
+}
+
+void AnodeEC_random(unsigned char *buf,unsigned int len)
+{
+ RAND_pseudo_bytes(buf,len);
+}
diff --git a/attic/historic/anode/libanode/impl/ec.h b/attic/historic/anode/libanode/impl/ec.h
new file mode 100644
index 00000000..f1262664
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/ec.h
@@ -0,0 +1,61 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+/* Elliptic curve glue -- hides OpenSSL code behind this source module */
+
+#ifndef _ANODE_EC_H
+#define _ANODE_EC_H
+
+#include "misc.h"
+
+/* Right now, only one mode is supported: NIST-P-256. This is the only mode
+ * supported in the spec as well, and should be good for quite some time.
+ * If other modes are needed this code will need to be refactored. */
+
+/* NIST-P-256 prime size in bytes */
+#define ANODE_EC_PRIME_BYTES 32
+
+/* Sizes of key fields */
+#define ANODE_EC_GROUP NID_X9_62_prime256v1
+#define ANODE_EC_PUBLIC_KEY_BYTES (ANODE_EC_PRIME_BYTES + 1)
+#define ANODE_EC_PRIVATE_KEY_BYTES ANODE_EC_PRIME_BYTES
+
+/* Larger of public or private key bytes, used for buffers */
+#define ANODE_EC_MAX_BYTES ANODE_EC_PUBLIC_KEY_BYTES
+
+struct AnodeECKey
+{
+ unsigned char key[ANODE_EC_MAX_BYTES];
+ unsigned int bytes;
+};
+
+struct AnodeECKeyPair
+{
+ struct AnodeECKey pub;
+ struct AnodeECKey priv;
+ void *internal_key;
+};
+
+/* Key management functions */
+int AnodeECKeyPair_generate(struct AnodeECKeyPair *pair);
+int AnodeECKeyPair_init(struct AnodeECKeyPair *pair,const struct AnodeECKey *pub,const struct AnodeECKey *priv);
+void AnodeECKeyPair_destroy(struct AnodeECKeyPair *pair);
+int AnodeECKeyPair_agree(const struct AnodeECKeyPair *my_key_pair,const struct AnodeECKey *their_pub_key,unsigned char *key_buf,unsigned int key_len);
+
+/* Provides access to the secure PRNG used to generate keys */
+void AnodeEC_random(unsigned char *buf,unsigned int len);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/environment.c b/attic/historic/anode/libanode/impl/environment.c
new file mode 100644
index 00000000..16e8ebe3
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/environment.c
@@ -0,0 +1,118 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "environment.h"
+
+#ifdef WINDOWS
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#include <string.h>
+#endif
+
+static char Anode_cache_base[1024] = { 0 };
+
+const char *Anode_get_cache()
+{
+ if (Anode_cache_base[0])
+ return Anode_cache_base;
+
+#ifdef WINDOWS
+#else
+ char tmp[1024];
+ char home[1024];
+ unsigned int i;
+ struct stat st;
+ const char *_home = getenv("HOME");
+
+ if (!_home)
+ return (const char *)0;
+ for(i=0;i<sizeof(home);++i) {
+ home[i] = _home[i];
+ if (!home[i]) {
+ if (i == 0)
+ return (const char *)0;
+ else if (home[i-1] == ANODE_PATH_SEPARATOR)
+ home[i-1] = (char)0;
+ break;
+ }
+ }
+ if (i == sizeof(home))
+ return (const char *)0;
+
+#ifdef __APPLE__
+ snprintf(tmp,sizeof(tmp),"%s%cLibrary",home,ANODE_PATH_SEPARATOR);
+ tmp[sizeof(tmp)-1] = (char)0;
+ if (!stat(tmp,&st)) {
+ sprintf(tmp,"%s%cLibrary%cCaches",home,ANODE_PATH_SEPARATOR,ANODE_PATH_SEPARATOR);
+ if (stat(tmp,&st)) {
+ if (mkdir(tmp,0700))
+ return (const char *)0;
+ }
+ snprintf(Anode_cache_base,sizeof(Anode_cache_base),"%s%ccom.zerotier.anode",tmp,ANODE_PATH_SEPARATOR);
+ Anode_cache_base[sizeof(Anode_cache_base)-1] = (char)0;
+ if (stat(Anode_cache_base,&st)) {
+ if (mkdir(Anode_cache_base,0700)) {
+ Anode_cache_base[0] = (char)0;
+ return (const char *)0;
+ }
+ }
+ return Anode_cache_base;
+ }
+#endif
+
+ snprintf(tmp,sizeof(tmp),"%s%c.anode",home,ANODE_PATH_SEPARATOR);
+ tmp[sizeof(tmp)-1] = (char)0;
+ if (stat(tmp,&st)) {
+ if (mkdir(tmp,0700)) {
+ Anode_cache_base[0] = (char)0;
+ return (const char *)0;
+ }
+ }
+ snprintf(Anode_cache_base,sizeof(Anode_cache_base),"%s%ccaches",tmp,ANODE_PATH_SEPARATOR);
+ Anode_cache_base[sizeof(Anode_cache_base)-1] = (char)0;
+ if (stat(Anode_cache_base,&st)) {
+ if (mkdir(Anode_cache_base,0700)) {
+ Anode_cache_base[0] = (char)0;
+ return (const char *)0;
+ }
+ }
+
+ return Anode_cache_base;
+#endif
+}
+
+char *Anode_get_cache_sub(const char *cache_subdir,char *buf,unsigned int len)
+{
+ struct stat st;
+ const char *cache_base = Anode_get_cache();
+
+ if (!len)
+ return (char *)0;
+ if (!cache_base)
+ return (char *)0;
+
+ snprintf(buf,len,"%s%c%s",cache_base,ANODE_PATH_SEPARATOR,cache_subdir);
+ buf[len-1] = (char)0;
+ if (stat(buf,&st)) {
+ if (mkdir(buf,0700))
+ return (char *)0;
+ }
+
+ return buf;
+}
diff --git a/attic/historic/anode/libanode/impl/environment.h b/attic/historic/anode/libanode/impl/environment.h
new file mode 100644
index 00000000..ecebdc11
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/environment.h
@@ -0,0 +1,30 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_ENVIRONMENT_H
+#define _ANODE_ENVIRONMENT_H
+
+#ifdef WINDOWS
+#define ANODE_PATH_SEPARATOR '\\'
+#else
+#define ANODE_PATH_SEPARATOR '/'
+#endif
+
+const char *Anode_get_cache();
+char *Anode_get_cache_sub(const char *cache_subdir,char *buf,unsigned int len);
+
+#endif
+
diff --git a/attic/historic/anode/libanode/impl/http_client.c b/attic/historic/anode/libanode/impl/http_client.c
new file mode 100644
index 00000000..a398a585
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/http_client.c
@@ -0,0 +1,558 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include "http_client.h"
+#include "misc.h"
+#include "types.h"
+
+/* How much to increment read buffer at each capacity top? */
+#define ANODE_HTTP_CAPACITY_INCREMENT 4096
+
+static void AnodeHttpClient_close_and_fail(struct AnodeHttpClient *client)
+{
+ if (client->impl.tcp_connection) {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ }
+
+ client->response.data_length = 0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+
+ if (client->handler)
+ client->handler(client);
+}
+
+static void AnodeHttpClient_do_initiate_client(struct AnodeHttpClient *client)
+{
+ const char *method = "";
+ long l,i;
+
+ switch(client->method) {
+ case ANODE_HTTP_GET: method = "GET"; break;
+ case ANODE_HTTP_HEAD: method = "HEAD"; break;
+ case ANODE_HTTP_POST: method = "POST"; break;
+ }
+ client->impl.outbuf_len = snprintf((char *)client->impl.outbuf,sizeof(client->impl.outbuf),
+ "%s %s%s%s HTTP/1.1\r\nHost: %s:%d\r\n%s",
+ method,
+ client->uri.path,
+ ((client->uri.query[0]) ? "?" : ""),
+ client->uri.query,
+ client->uri.host,
+ ((client->uri.port > 0) ? client->uri.port : 80),
+ ((client->keepalive) ? "" : "Connection: close\r\n")
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+
+ if (client->method == ANODE_HTTP_POST) {
+ if ((client->data)&&(client->data_length)) {
+ client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len,
+ "Content-Type: %s\r\n",
+ (client->data_content_type ? client->data_content_type : "application/x-www-form-urlencoded")
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len,
+ "Content-Length: %u\r\n",
+ client->data_length
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ } else {
+ client->impl.outbuf_len += snprintf((char *)client->impl.outbuf + client->impl.outbuf_len,sizeof(client->impl.outbuf) - client->impl.outbuf_len,
+ "Content-Length: 0\r\n"
+ );
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ }
+ }
+
+ l = AnodeDictionary_write(&(client->headers),(char *)client->impl.outbuf + client->impl.outbuf_len,(long)(sizeof(client->impl.outbuf) - client->impl.outbuf_len - 2),"\r\n",": ");
+ if (l < 0) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+
+ client->impl.outbuf_len += (unsigned int)l;
+ if (client->impl.outbuf_len >= (sizeof(client->impl.outbuf) - 2)) { /* sanity check */
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+
+ client->impl.outbuf[client->impl.outbuf_len++] = '\r';
+ client->impl.outbuf[client->impl.outbuf_len++] = '\n';
+
+ if ((client->method == ANODE_HTTP_POST)&&(client->data)&&(client->data_length)) {
+ i = sizeof(client->impl.outbuf) - client->impl.outbuf_len;
+ if (i > client->data_length)
+ i = client->data_length;
+ Anode_memcpy((client->impl.outbuf + client->impl.outbuf_len),client->data,i);
+ client->impl.request_data_ptr += i;
+ client->impl.outbuf_len += i;
+ }
+
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_SEND;
+ client->impl.transport_engine->tcp_start_writing(client->impl.transport_engine,client->impl.tcp_connection);
+}
+
+static void AnodeHttpClient_tcp_outgoing_connect_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection,
+ int error_code)
+{
+ struct AnodeHttpClient *client;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+
+ if ((client->impl.phase == ANODE_HTTP_REQUEST_PHASE_CONNECT)&&(!client->impl.freed)) {
+ if (error_code) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_CONNECT_FAILED;
+ AnodeHttpClient_close_and_fail(client);
+ } else {
+ client->impl.tcp_connection = connection;
+ AnodeHttpClient_do_initiate_client(client);
+ }
+ } else transport->tcp_close(transport,connection);
+}
+
+static void AnodeHttpClient_tcp_connection_terminated_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection,
+ int error_code)
+{
+ struct AnodeHttpClient *client;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+ if (client->impl.freed)
+ return;
+
+ client->response.data_length = 0;
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ if ((client->impl.phase != ANODE_HTTP_REQUEST_PHASE_KEEPALIVE)&&(client->impl.phase != ANODE_HTTP_REQUEST_PHASE_CLOSED)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ AnodeHttpClient_close_and_fail(client);
+ } else client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+}
+
+static void AnodeHttpClient_tcp_receive_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection,
+ void *data,
+ unsigned int data_length)
+{
+ struct AnodeHttpClient *client;
+ char *p1,*p2;
+ unsigned int i;
+ long l;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+ if (client->impl.freed) {
+ transport->tcp_close(transport,connection);
+ return;
+ }
+
+ if (!client->response.data)
+ client->response.data = malloc(client->impl.response_data_capacity = ANODE_HTTP_CAPACITY_INCREMENT);
+
+ i = 0;
+ while (i < data_length) {
+ switch(client->impl.read_mode) {
+ case ANODE_HTTP_READ_MODE_WAITING:
+ for(;i<data_length;++i) {
+ if (((const char *)data)[i] == '\n') {
+ ((char *)client->response.data)[client->response.data_length] = (char)0;
+ client->response.data_length = 0;
+
+ p1 = (char *)Anode_strchr((char *)client->response.data,' ');
+ if (!p1)
+ p1 = (char *)Anode_strchr((char *)client->response.data,'\t');
+ if (p1) {
+ while ((*p1 == ' ')||(*p1 == '\t')) ++p1;
+ if (!*p1) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ p2 = p1 + 1;
+ while (*p2) {
+ if ((*p2 == ' ')||(*p2 == '\t')||(*p2 == '\r')||(*p2 == '\n')) {
+ *p2 = (char)0;
+ break;
+ } else ++p2;
+ }
+ client->response.code = (int)strtol(p1,(char **)0,10);
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_HEADERS;
+ ++i; break; /* Exit inner for() */
+ }
+ } else {
+ ((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i];
+ if (client->response.data_length >= client->impl.response_data_capacity)
+ client->response.data = realloc(client->response.data,client->impl.response_data_capacity += ANODE_HTTP_CAPACITY_INCREMENT);
+ }
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_HEADERS:
+ case ANODE_HTTP_READ_MODE_CHUNKED_FOOTER:
+ for(;i<data_length;++i) {
+ if (((const char *)data)[i] == '\n') {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr] = (char)0;
+ client->impl.header_line_buf_ptr = 0;
+
+ if ((!client->impl.header_line_buf[0])||((client->impl.header_line_buf[0] == '\r')&&(!client->impl.header_line_buf[1]))) {
+ /* If the line is empty (or is empty with \r\n as the
+ * line terminator), we're at the end. */
+ if (client->impl.read_mode == ANODE_HTTP_READ_MODE_CHUNKED_FOOTER) {
+ /* If this is a chunked footer, we finally end the
+ * chunked response. */
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+ } else {
+ /* Otherwise, this is a regular header block */
+ if (client->response.code == 100) {
+ /* Ignore 100 Continue messages */
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ ++i; break; /* Exit inner for() */
+ } else if ((client->response.code == 200)&&(client->method != ANODE_HTTP_HEAD)) {
+ /* Other messages get their headers parsed to determine
+ * how to read them. */
+ p1 = (char *)AnodeDictionary_get(&(client->response.headers),"transfer-encoding");
+ if ((p1)&&(Anode_strcaseeq(p1,"chunked"))) {
+ /* Chunked encoding enters chunked mode */
+ client->impl.header_line_buf_ptr = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE;
+ ++i; break; /* Exit inner for() */
+ } else {
+ /* Else we must have a Content-Length header */
+ p1 = (char *)AnodeDictionary_get(&(client->response.headers),"content-length");
+ if (!p1) {
+ /* No chunked or content length is not supported */
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ } else {
+ /* Enter block read mode with content length */
+ l = strtol(p1,(char **)0,10);
+ if (l <= 0) {
+ /* Zero length data is all done... */
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+
+ ++i; break; /* Exit inner for() */
+ } else {
+ /* Else start reading... */
+ client->impl.expecting_response_length = (unsigned int)l;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_BLOCK;
+ ++i; break; /* Exit inner for() */
+ }
+ }
+ }
+ } else {
+ /* HEAD clients or non-200 codes get headers only */
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+
+ ++i; break; /* Exit inner for() */
+ }
+ }
+ } else {
+ /* Otherwise this is another header, add to dictionary */
+ AnodeDictionary_read(
+ &(client->response.headers),
+ client->impl.header_line_buf,
+ "\r\n",
+ ": \t",
+ "",
+ (char)0,
+ 1,
+ 1
+ );
+ }
+ } else {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr++] = ((const char *)data)[i];
+ if (client->impl.header_line_buf_ptr >= sizeof(client->impl.header_line_buf)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ }
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_BLOCK:
+ if ((client->response.data_length + client->impl.expecting_response_length) > client->impl.response_data_capacity)
+ client->response.data = realloc(client->response.data,client->impl.response_data_capacity = (client->response.data_length + client->impl.expecting_response_length));
+
+ for(;((i<data_length)&&(client->impl.expecting_response_length));++i) {
+ ((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i];
+ --client->impl.expecting_response_length;
+ }
+
+ if (!client->impl.expecting_response_length) {
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ if (client->keepalive)
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_KEEPALIVE;
+ else {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CLOSED;
+ }
+
+ if (client->handler)
+ client->handler(client);
+ if (client->impl.freed)
+ return;
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE:
+ for(;i<data_length;++i) {
+ if (((const char *)data)[i] == '\n') {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr] = (char)0;
+ client->impl.header_line_buf_ptr = 0;
+
+ p1 = client->impl.header_line_buf;
+ while (*p1) {
+ if ((*p1 == ';')||(*p1 == ' ')||(*p1 == '\r')||(*p1 == '\n')||(*p1 == '\t')) {
+ *p1 = (char)0;
+ break;
+ } else ++p1;
+ }
+
+ if (client->impl.header_line_buf[0]) {
+ l = strtol(client->impl.header_line_buf,(char **)0,16);
+ if (l <= 0) {
+ /* Zero length ends chunked and enters footer mode */
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_FOOTER;
+ } else {
+ /* Otherwise the next chunk is to be read */
+ client->impl.expecting_response_length = (unsigned int)l;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_DATA;
+ }
+ ++i; break; /* Exit inner for() */
+ }
+ } else {
+ client->impl.header_line_buf[client->impl.header_line_buf_ptr++] = ((const char *)data)[i];
+ if (client->impl.header_line_buf_ptr >= sizeof(client->impl.header_line_buf)) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE;
+ AnodeHttpClient_close_and_fail(client);
+ return;
+ }
+ }
+ }
+ break;
+ case ANODE_HTTP_READ_MODE_CHUNKED_DATA:
+ if ((client->response.data_length + client->impl.expecting_response_length) > client->impl.response_data_capacity)
+ client->response.data = realloc(client->response.data,client->impl.response_data_capacity = (client->response.data_length + client->impl.expecting_response_length));
+
+ for(;((i<data_length)&&(client->impl.expecting_response_length));++i) {
+ ((char *)client->response.data)[client->response.data_length++] = ((const char *)data)[i];
+ --client->impl.expecting_response_length;
+ }
+
+ if (!client->impl.expecting_response_length)
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE;
+ break;
+ }
+ }
+}
+
+static void AnodeHttpClient_tcp_available_for_write_handler(
+ AnodeTransportEngine *transport,
+ AnodeTransportTcpConnection *connection)
+{
+ struct AnodeHttpClient *client;
+ unsigned int i,j;
+ int n;
+
+ if (!(client = (struct AnodeHttpClient *)(connection->ptr)))
+ return;
+ if (client->impl.freed) {
+ transport->tcp_close(transport,connection);
+ return;
+ }
+
+ if (client->impl.phase == ANODE_HTTP_REQUEST_PHASE_SEND) {
+ n = client->impl.transport_engine->tcp_send(client->impl.transport_engine,client->impl.tcp_connection,(const void *)client->impl.outbuf,(int)client->impl.outbuf_len);
+ if (n < 0) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION;
+ AnodeHttpClient_close_and_fail(client);
+ } else if (n > 0) {
+ for(i=0,j=(client->impl.outbuf_len - (unsigned int)n);i<j;++i)
+ client->impl.outbuf[i] = client->impl.outbuf[i + (unsigned int)n];
+ client->impl.outbuf_len -= (unsigned int)n;
+
+ if ((client->method == ANODE_HTTP_POST)&&(client->data)&&(client->data_length)) {
+ i = sizeof(client->impl.outbuf) - client->impl.outbuf_len;
+ j = client->data_length - client->impl.request_data_ptr;
+ if (i > j)
+ i = j;
+ Anode_memcpy((client->impl.outbuf + client->impl.outbuf_len),client->data,i);
+ client->impl.request_data_ptr += i;
+ client->impl.outbuf_len += i;
+ }
+
+ if (!client->impl.outbuf_len) {
+ client->impl.transport_engine->tcp_stop_writing(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_RECEIVE;
+ }
+ }
+ } else client->impl.transport_engine->tcp_stop_writing(client->impl.transport_engine,client->impl.tcp_connection);
+}
+
+static void AnodeHttpClient_dns_result_handler(
+ AnodeTransportEngine *transport,
+ void *ptr,
+ int error_code,
+ const char *name,
+ const AnodeTransportIpAddress *ip_addresses,
+ unsigned int ip_address_count,
+ const AnodeAddress *anode_address)
+{
+ struct AnodeHttpClient *client;
+ AnodeTransportIpEndpoint to_endpoint;
+
+ if (!(client = (struct AnodeHttpClient *)ptr))
+ return;
+ if (client->impl.freed)
+ return;
+
+ if ((error_code)||(!ip_address_count)) {
+ if (client->impl.phase == ANODE_HTTP_REQUEST_PHASE_RESOLVE) {
+ client->response.code = ANODE_HTTP_SPECIAL_RESPONSE_DNS_RESOLVE_FAILED;
+ AnodeHttpClient_close_and_fail(client);
+ }
+ } else {
+ client->impl.phase = ANODE_HTTP_REQUEST_PHASE_CONNECT;
+ Anode_memcpy(&to_endpoint.address,ip_addresses,sizeof(AnodeTransportIpAddress));
+ to_endpoint.port = (client->uri.port > 0) ? client->uri.port : 80;
+ client->impl.transport_engine->tcp_connect(
+ client->impl.transport_engine,
+ client,
+ &AnodeHttpClient_tcp_outgoing_connect_handler,
+ &AnodeHttpClient_tcp_connection_terminated_handler,
+ &AnodeHttpClient_tcp_receive_handler,
+ &AnodeHttpClient_tcp_available_for_write_handler,
+ &to_endpoint);
+ }
+}
+
+struct AnodeHttpClient *AnodeHttpClient_new(AnodeTransportEngine *transport_engine)
+{
+ struct AnodeHttpClient *req = malloc(sizeof(struct AnodeHttpClient));
+ Anode_zero(req,sizeof(struct AnodeHttpClient));
+
+ AnodeDictionary_init(&(req->headers),0);
+ AnodeDictionary_init(&(req->response.headers),0);
+
+ req->impl.transport_engine = transport_engine;
+
+ return req;
+}
+
+void AnodeHttpClient_send(struct AnodeHttpClient *client)
+{
+ client->response.code = 0;
+ client->response.data_length = 0;
+ AnodeDictionary_clear(&(client->response.headers));
+
+ client->impl.request_data_ptr = 0;
+ client->impl.expecting_response_length = 0;
+ client->impl.read_mode = ANODE_HTTP_READ_MODE_WAITING;
+ client->impl.outbuf_len = 0;
+
+ if (!client->impl.tcp_connection) {
+ client->impl.transport_engine->dns_resolve(
+ client->impl.transport_engine,
+ &AnodeHttpClient_dns_result_handler,
+ client,
+ client->uri.host,
+ ANODE_TRANSPORT_DNS_QUERY_ALWAYS,
+ ANODE_TRANSPORT_DNS_QUERY_IF_NO_PREVIOUS,
+ ANODE_TRANSPORT_DNS_QUERY_NEVER);
+ } else AnodeHttpClient_do_initiate_client(client);
+}
+
+void AnodeHttpClient_free(struct AnodeHttpClient *client)
+{
+ AnodeDictionary_destroy(&(client->headers));
+ AnodeDictionary_destroy(&(client->response.headers));
+
+ if (client->impl.tcp_connection) {
+ client->impl.transport_engine->tcp_close(client->impl.transport_engine,client->impl.tcp_connection);
+ client->impl.tcp_connection = (AnodeTransportTcpConnection *)0;
+ }
+
+ if (client->response.data)
+ free(client->response.data);
+
+ client->impl.freed = 1;
+ client->impl.transport_engine->run_later(client->impl.transport_engine,client,&free);
+}
diff --git a/attic/historic/anode/libanode/impl/http_client.h b/attic/historic/anode/libanode/impl/http_client.h
new file mode 100644
index 00000000..f1673097
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/http_client.h
@@ -0,0 +1,200 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_HTTP_CLIENT_H
+#define _ANODE_HTTP_CLIENT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "dictionary.h"
+#include "../anode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * HTTP request type
+ */
+enum AnodeHttpClientRequestMethod
+{
+ ANODE_HTTP_GET = 0,
+ ANODE_HTTP_HEAD = 1,
+ ANODE_HTTP_POST = 2
+};
+
+/*
+ * Special response codes to indicate I/O errors
+ */
+#define ANODE_HTTP_SPECIAL_RESPONSE_DNS_RESOLVE_FAILED -1
+#define ANODE_HTTP_SPECIAL_RESPONSE_CONNECT_FAILED -2
+#define ANODE_HTTP_SPECIAL_RESPONSE_HEADERS_TOO_LARGE -3
+#define ANODE_HTTP_SPECIAL_RESPONSE_SERVER_CLOSED_CONNECTION -4
+#define ANODE_HTTP_SPECIAL_RESPONSE_INVALID_RESPONSE -5
+
+/**
+ * Simple HTTP client
+ */
+struct AnodeHttpClient
+{
+ /**
+ * Request URI
+ */
+ AnodeURI uri;
+
+ /**
+ * Request method: GET, PUT, HEAD, or POST
+ */
+ enum AnodeHttpClientRequestMethod method;
+
+ /**
+ * Data for POST requests
+ *
+ * It is your responsibility to manage and/or free this pointer. The HTTP
+ * client only reads from it.
+ */
+ const void *data;
+ unsigned int data_length;
+
+ /**
+ * Content type for data, or null for application/x-www-form-urlencoded
+ */
+ const char *data_content_type;
+
+ /**
+ * Set to non-zero to use HTTP connection keepalive
+ *
+ * If keepalive is enabled, this request can be modified and re-used and
+ * its associated connection will stay open (being reopened if needed)
+ * until it is freed.
+ *
+ * Note that this client is too dumb to pool connections and pick them on
+ * the basis of host. Keepalive mode should only be set if the next request
+ * will be from the same host and port, otherwise you will get a '404'.
+ */
+ int keepalive;
+
+ /**
+ * Function pointer to be called when request is complete (or fails)
+ */
+ void (*handler)(struct AnodeHttpClient *);
+
+ /**
+ * Two arbitrary pointers that can be stored here for use by the handler.
+ * These are not accessed or modified by the client.
+ */
+ void *ptr[2];
+
+ /**
+ * Request headers
+ */
+ struct AnodeDictionary headers;
+
+ struct {
+ /**
+ * Response code, set on completion or failure before handler is called
+ *
+ * Also check for the special response codes defined in http_client.h as
+ * these negative codes indicate network or other errors.
+ */
+ int code;
+
+ /**
+ * Response data, for GET and POST requests
+ */
+ void *data;
+
+ /**
+ * Length of response data
+ */
+ unsigned int data_length;
+
+ /**
+ * Response headers
+ */
+ struct AnodeDictionary headers;
+ } response;
+
+ /**
+ * Internal fields used by implementation
+ */
+ struct {
+ /* Transport engine being used by request */
+ AnodeTransportEngine *transport_engine;
+
+ /* Connection to which request has been sent, or null if none */
+ struct AnodeHttpConnection *connection;
+
+ /* Buffer for reading chunked mode chunk lines (can't use data buf) */
+ char header_line_buf[256];
+ unsigned int header_line_buf_ptr;
+
+ /* Where are we in sending request data? */
+ unsigned int request_data_ptr;
+
+ /* Capacity of response_data buffer */
+ unsigned int response_data_capacity;
+
+ /* How much response data are we currently expecting? */
+ /* This is content-length in block mode or chunk length in chunked mode */
+ unsigned int expecting_response_length;
+
+ /* Read mode */
+ enum {
+ ANODE_HTTP_READ_MODE_WAITING = 0,
+ ANODE_HTTP_READ_MODE_HEADERS = 1,
+ ANODE_HTTP_READ_MODE_BLOCK = 2,
+ ANODE_HTTP_READ_MODE_CHUNKED_CHUNK_SIZE = 3,
+ ANODE_HTTP_READ_MODE_CHUNKED_DATA = 4,
+ ANODE_HTTP_READ_MODE_CHUNKED_FOOTER = 5
+ } read_mode;
+
+ /* Connection from transport engine */
+ AnodeTransportTcpConnection *tcp_connection;
+
+ /* Write buffer */
+ unsigned char outbuf[16384];
+ unsigned int outbuf_len;
+
+ /* Phase of request state machine */
+ enum {
+ ANODE_HTTP_REQUEST_PHASE_RESOLVE = 0,
+ ANODE_HTTP_REQUEST_PHASE_CONNECT = 1,
+ ANODE_HTTP_REQUEST_PHASE_SEND = 2,
+ ANODE_HTTP_REQUEST_PHASE_RECEIVE = 3,
+ ANODE_HTTP_REQUEST_PHASE_KEEPALIVE = 4,
+ ANODE_HTTP_REQUEST_PHASE_CLOSED = 5
+ } phase;
+
+ /* Has request object been freed? */
+ int freed;
+
+ /**
+ * Pointer used internally for putting requests into linked lists
+ */
+ struct AnodeHttpClient *next;
+ } impl;
+};
+
+struct AnodeHttpClient *AnodeHttpClient_new(AnodeTransportEngine *transport_engine);
+void AnodeHttpClient_send(struct AnodeHttpClient *client);
+void AnodeHttpClient_free(struct AnodeHttpClient *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/misc.c b/attic/historic/anode/libanode/impl/misc.c
new file mode 100644
index 00000000..edc73978
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/misc.c
@@ -0,0 +1,190 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "misc.h"
+#include "types.h"
+
+static const char Anode_hex_chars[16] = {
+ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
+};
+
+static const char Anode_base32_chars[32] = {
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
+ 'r','s','t','u','v','w','x','y','z','2','3','4','5','6','7'
+};
+static const unsigned char Anode_base32_bits[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,
+ 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,0,1,2,
+ 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+/* Table for converting ASCII chars to lower case */
+const unsigned char Anode_ascii_tolower_table[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+void Anode_trim(char *s)
+{
+ char *dest = s;
+ char *last;
+ while ((*s)&&((*s == ' ')||(*s == '\t')||(*s == '\r')||(*s == '\n')))
+ ++s;
+ last = s;
+ while ((*dest = *s)) {
+ if ((*dest != ' ')&&(*dest != '\t')&&(*dest != '\r')&&(*dest != '\n'))
+ last = dest;
+ ++dest;
+ ++s;
+ }
+ if (*last)
+ *(++last) = (char)0;
+}
+
+unsigned int Anode_rand()
+{
+ static volatile int need_seed = 1;
+
+ if (need_seed) {
+ need_seed = 0;
+ srandom((unsigned long)Anode_time64());
+ }
+
+ return (unsigned int)random();
+}
+
+void Anode_to_hex(const unsigned char *b,unsigned int len,char *h,unsigned int hlen)
+{
+ unsigned int i;
+
+ if ((len * 2) >= hlen)
+ len = (hlen - 1) / 2;
+
+ for(i=0;i<len;++i) {
+ *(h++) = Anode_hex_chars[b[i] >> 4];
+ *(h++) = Anode_hex_chars[b[i] & 0xf];
+ }
+ *h = (char)0;
+}
+
+void Anode_from_hex(const char *h,unsigned char *b,unsigned int blen)
+{
+ unsigned char *end = b + blen;
+ unsigned char v = (unsigned char)0;
+
+ while (b != end) {
+ switch(*(h++)) {
+ case '0': v = 0x00; break;
+ case '1': v = 0x10; break;
+ case '2': v = 0x20; break;
+ case '3': v = 0x30; break;
+ case '4': v = 0x40; break;
+ case '5': v = 0x50; break;
+ case '6': v = 0x60; break;
+ case '7': v = 0x70; break;
+ case '8': v = 0x80; break;
+ case '9': v = 0x90; break;
+ case 'a': v = 0xa0; break;
+ case 'b': v = 0xb0; break;
+ case 'c': v = 0xc0; break;
+ case 'd': v = 0xd0; break;
+ case 'e': v = 0xe0; break;
+ case 'f': v = 0xf0; break;
+ default: return;
+ }
+
+ switch(*(h++)) {
+ case '0': v |= 0x00; break;
+ case '1': v |= 0x01; break;
+ case '2': v |= 0x02; break;
+ case '3': v |= 0x03; break;
+ case '4': v |= 0x04; break;
+ case '5': v |= 0x05; break;
+ case '6': v |= 0x06; break;
+ case '7': v |= 0x07; break;
+ case '8': v |= 0x08; break;
+ case '9': v |= 0x09; break;
+ case 'a': v |= 0x0a; break;
+ case 'b': v |= 0x0b; break;
+ case 'c': v |= 0x0c; break;
+ case 'd': v |= 0x0d; break;
+ case 'e': v |= 0x0e; break;
+ case 'f': v |= 0x0f; break;
+ default: return;
+ }
+
+ *(b++) = v;
+ }
+}
+
+void Anode_base32_5_to_8(const unsigned char *in,char *out)
+{
+ out[0] = Anode_base32_chars[(in[0]) >> 3];
+ out[1] = Anode_base32_chars[(in[0] & 0x07) << 2 | (in[1] & 0xc0) >> 6];
+ out[2] = Anode_base32_chars[(in[1] & 0x3e) >> 1];
+ out[3] = Anode_base32_chars[(in[1] & 0x01) << 4 | (in[2] & 0xf0) >> 4];
+ out[4] = Anode_base32_chars[(in[2] & 0x0f) << 1 | (in[3] & 0x80) >> 7];
+ out[5] = Anode_base32_chars[(in[3] & 0x7c) >> 2];
+ out[6] = Anode_base32_chars[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5];
+ out[7] = Anode_base32_chars[(in[4] & 0x1f)];
+}
+
+void Anode_base32_8_to_5(const char *in,unsigned char *out)
+{
+ out[0] = ((Anode_base32_bits[(unsigned int)in[0]]) << 3) | (Anode_base32_bits[(unsigned int)in[1]] & 0x1C) >> 2;
+ out[1] = ((Anode_base32_bits[(unsigned int)in[1]] & 0x03) << 6) | (Anode_base32_bits[(unsigned int)in[2]]) << 1 | (Anode_base32_bits[(unsigned int)in[3]] & 0x10) >> 4;
+ out[2] = ((Anode_base32_bits[(unsigned int)in[3]] & 0x0F) << 4) | (Anode_base32_bits[(unsigned int)in[4]] & 0x1E) >> 1;
+ out[3] = ((Anode_base32_bits[(unsigned int)in[4]] & 0x01) << 7) | (Anode_base32_bits[(unsigned int)in[5]]) << 2 | (Anode_base32_bits[(unsigned int)in[6]] & 0x18) >> 3;
+ out[4] = ((Anode_base32_bits[(unsigned int)in[6]] & 0x07) << 5) | (Anode_base32_bits[(unsigned int)in[7]]);
+}
diff --git a/attic/historic/anode/libanode/impl/misc.h b/attic/historic/anode/libanode/impl/misc.h
new file mode 100644
index 00000000..38ddea7c
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/misc.h
@@ -0,0 +1,193 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+/* This contains miscellaneous functions, including some re-implementations
+ * of some functions from string.h. This is to help us port to some platforms
+ * (cough Windows Mobile cough) that lack a lot of the basic C library. */
+
+#ifndef _ANODE_MISC_H
+#define _ANODE_MISC_H
+
+#include <time.h>
+#include <sys/time.h>
+#include "types.h"
+
+#ifndef ANODE_NO_STRING_H
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+/* Table mapping ASCII characters to themselves or their lower case */
+extern const unsigned char Anode_ascii_tolower_table[256];
+
+/* Get the lower case version of an ASCII char */
+#define Anode_tolower(c) ((char)Anode_ascii_tolower_table[((unsigned long)((unsigned char)(c)))])
+
+/* Test strings for equality, return nonzero if equal */
+static inline unsigned int Anode_streq(const char *restrict a,const char *restrict b)
+{
+ if ((!a)||(!b))
+ return 0;
+ while (*a == *(b++)) {
+ if (!*(a++))
+ return 1;
+ }
+ return 0;
+}
+
+/* Equality test ignoring (ASCII) case */
+static inline unsigned int Anode_strcaseeq(const char *restrict a,const char *restrict b)
+{
+ if ((!a)||(!b))
+ return 0;
+ while (Anode_tolower(*a) == Anode_tolower(*(b++))) {
+ if (!*(a++))
+ return 1;
+ }
+ return 0;
+}
+
+/* Safe c-string copy, ensuring that dest[] always ends with zero */
+static inline void Anode_str_copy(char *restrict dest,const char *restrict src,unsigned int dest_size)
+{
+ char *restrict dest_end = dest + (dest_size - 1);
+ while ((*src)&&(dest != dest_end))
+ *(dest++) = *(src++);
+ *dest = (char)0;
+}
+
+/* Simple memcpy() */
+#ifdef ANODE_NO_STRING_H
+static inline void Anode_memcpy(void *restrict dest,const void *restrict src,unsigned int len)
+{
+ unsigned int i;
+ for(i=0;i<len;++i)
+ ((unsigned char *restrict)dest)[i] = ((const unsigned char *restrict)src)[i];
+}
+#else
+#define Anode_memcpy(d,s,l) memcpy((d),(s),(l))
+#endif
+
+/* Memory test for equality */
+#ifdef ANODE_NO_STRING_H
+static inline unsigned int Anode_mem_eq(const void *restrict a,const void *restrict b,unsigned int len)
+{
+ unsigned int i;
+ for(i=0;i<len;++i) {
+ if (((const unsigned char *restrict)a)[i] != ((const unsigned char *restrict)b)[i])
+ return 0;
+ }
+ return 1;
+}
+#else
+#define Anode_mem_eq(a,b,l) (!memcmp((a),(b),(l)))
+#endif
+
+/* Zero memory */
+#ifdef ANODE_NO_STRING_H
+static inline void Anode_zero(void *restrict ptr,unsigned int len)
+{
+ unsigned int i;
+ for(i=0;i<len;++i)
+ ((unsigned char *restrict)ptr)[i] = (unsigned char)0;
+}
+#else
+#define Anode_zero(p,l) memset((p),0,(l))
+#endif
+
+/* Get a pointer to the first occurrance of a character in a string */
+#ifdef ANODE_NO_STRING_H
+static inline const char *Anode_strchr(const char *s,char c)
+{
+ while (*s) {
+ if (*s == c)
+ return s;
+ ++s;
+ }
+ return (char *)0;
+}
+#else
+#define Anode_strchr(s,c) strchr((s),(c))
+#endif
+
+static inline unsigned int Anode_count_char(const char *s,char c)
+{
+ unsigned int cnt = 0;
+ while (s) {
+ if (*s == c)
+ ++cnt;
+ ++s;
+ }
+ return cnt;
+}
+
+/* Strip all of a given set of characters from a string */
+static inline void Anode_strip_all(char *s,const char *restrict schars)
+{
+ char *d = s;
+
+ while (*s) {
+ if (!Anode_strchr(schars,*s))
+ *(d++) = *s;
+ ++s;
+ }
+ *d = (char)0;
+}
+
+/* Trim whitespace from beginning and end of string */
+void Anode_trim(char *s);
+
+/* Get the length of a string */
+#ifdef ANODE_NO_STRING_H
+static inline unsigned int Anode_strlen(const char *s)
+{
+ const char *ptr = s;
+ while (*ptr) ++ptr;
+ return (unsigned int)(ptr - s);
+}
+#else
+#define Anode_strlen(s) strlen((s))
+#endif
+
+/* Returns number of milliseconds since the epoch (Java-style) */
+static inline uint64_t Anode_time64()
+{
+ struct timeval tv;
+ gettimeofday(&tv,(void *)0);
+ return ( (((uint64_t)tv.tv_sec) / 1000ULL) + ((uint64_t)(tv.tv_usec / 1000ULL)) );
+}
+
+/* Returns number of seconds since the epoch (*nix style) */
+static inline unsigned long Anode_time()
+{
+ struct timeval tv;
+ gettimeofday(&tv,(void *)0);
+ return (unsigned long)tv.tv_sec;
+}
+
+/* Simple random function, not cryptographically safe */
+unsigned int Anode_rand();
+
+/* Fast hex/ascii conversion */
+void Anode_to_hex(const unsigned char *b,unsigned int len,char *h,unsigned int hlen);
+void Anode_from_hex(const char *h,unsigned char *b,unsigned int blen);
+
+/* Convert back and forth from base32 encoding */
+/* 5 bytes -> 8 base32 characters and vice versa */
+void Anode_base32_5_to_8(const unsigned char *in,char *out);
+void Anode_base32_8_to_5(const char *in,unsigned char *out);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/mutex.h b/attic/historic/anode/libanode/impl/mutex.h
new file mode 100644
index 00000000..b20eb82b
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/mutex.h
@@ -0,0 +1,34 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_MUTEX_H
+#define _ANODE_MUTEX_H
+
+#ifdef WINDOWS
+
+#else /* WINDOWS */
+
+#include <pthread.h>
+
+#define AnodeMutex pthread_mutex_t
+#define AnodeMutex_init(m) pthread_mutex_init((m),(const pthread_mutexattr_t *)0)
+#define AnodeMutex_destroy(m) pthread_mutex_destroy((m))
+#define AnodeMutex_lock(m) pthread_mutex_lock((m))
+#define AnodeMutex_unlock(m) pthread_mutex_unlock((m))
+
+#endif /* WINDOWS */
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/thread.c b/attic/historic/anode/libanode/impl/thread.c
new file mode 100644
index 00000000..c2070462
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/thread.c
@@ -0,0 +1,58 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include "thread.h"
+#include <stdlib.h>
+
+#ifdef WINDOWS
+
+#else /* not WINDOWS */
+
+struct _AnodeThread
+{
+ void (*func)(void *);
+ void *arg;
+ int wait_for_join;
+ pthread_t thread;
+};
+
+static void *_AnodeThread_main(void *arg)
+{
+ ((struct _AnodeThread *)arg)->func(((struct _AnodeThread *)arg)->arg);
+ if (!((struct _AnodeThread *)arg)->wait_for_join)
+ free(arg);
+ return (void *)0;
+}
+
+AnodeThread *AnodeThread_create(void (*func)(void *),void *arg,int wait_for_join)
+{
+ struct _AnodeThread *t = malloc(sizeof(struct _AnodeThread));
+ t->func = func;
+ t->arg = arg;
+ t->wait_for_join = wait_for_join;
+ pthread_create(&t->thread,(const pthread_attr_t *)0,&_AnodeThread_main,(void *)t);
+ if (!wait_for_join)
+ pthread_detach(t->thread);
+ return (AnodeThread *)t;
+}
+
+void AnodeThread_join(AnodeThread *thread)
+{
+ pthread_join(((struct _AnodeThread *)thread)->thread,(void **)0);
+ free((void *)thread);
+}
+
+#endif /* WINDOWS / not WINDOWS */
diff --git a/attic/historic/anode/libanode/impl/thread.h b/attic/historic/anode/libanode/impl/thread.h
new file mode 100644
index 00000000..accf173a
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/thread.h
@@ -0,0 +1,65 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_THREAD_H
+#define _ANODE_THREAD_H
+
+#ifdef WINDOWS
+
+#include <windows.h>
+#include <thread.h>
+typedef DWORD AnodeThreadId;
+
+#else /* not WINDOWS */
+
+#include <pthread.h>
+typedef pthread_t AnodeThreadId;
+
+#define AnodeThread_self() pthread_self()
+#define AnodeThreadId_equal(a,b) pthread_equal((pthread_t)(a),(pthread_t)(b))
+
+#endif
+
+typedef void AnodeThread;
+
+/**
+ * Create and launch a new thread
+ *
+ * If wait_for_join is true (nonzero), the thread can and must be joined. The
+ * thread object won't be freed until join is called and returns. If
+ * wait_for_join is false, the thread object frees itself automatically on
+ * termination.
+ *
+ * If wait_for_join is false (zero), there is really no need to keep track of
+ * the thread object.
+ *
+ * @param func Function to call as thread main
+ * @param arg Argument to pass to function
+ * @param wait_for_join If false, thread deletes itself when it terminates
+ */
+AnodeThread *AnodeThread_create(void (*func)(void *),void *arg,int wait_for_join);
+
+/**
+ * Wait for a thread to terminate and delete thread object
+ *
+ * This can only be used for threads created with wait_for_join set to true.
+ * The thread object is no longer valid after this call.
+ *
+ * @param thread Thread to wait for termination and delete
+ */
+void AnodeThread_join(AnodeThread *thread);
+
+#endif
diff --git a/attic/historic/anode/libanode/impl/types.h b/attic/historic/anode/libanode/impl/types.h
new file mode 100644
index 00000000..5f070e5a
--- /dev/null
+++ b/attic/historic/anode/libanode/impl/types.h
@@ -0,0 +1,25 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _ANODE_TYPES_H
+#define _ANODE_TYPES_H
+
+#ifdef WINDOWS
+#else
+#include <stdint.h>
+#endif
+
+#endif
diff --git a/attic/historic/anode/libanode/network_address.c b/attic/historic/anode/libanode/network_address.c
new file mode 100644
index 00000000..86ec054f
--- /dev/null
+++ b/attic/historic/anode/libanode/network_address.c
@@ -0,0 +1,136 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "impl/misc.h"
+#include "impl/types.h"
+#include "anode.h"
+
+const AnodeNetworkAddress AnodeNetworkAddress_ANY4 = {
+ ANODE_NETWORK_ADDRESS_IPV4,
+ { 0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+const AnodeNetworkAddress AnodeNetworkAddress_ANY6 = {
+ ANODE_NETWORK_ADDRESS_IPV6,
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+const AnodeNetworkAddress AnodeNetworkAddress_LOCAL4 = {
+ ANODE_NETWORK_ADDRESS_IPV4,
+ { 127,0,0,1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+const AnodeNetworkAddress AnodeNetworkAddress_LOCAL6 = {
+ ANODE_NETWORK_ADDRESS_IPV6,
+ { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+
+int AnodeNetworkAddress_to_string(const AnodeNetworkAddress *address,char *buf,int len)
+{
+ const char *s;
+
+ switch(address->type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ s = inet_ntop(AF_INET,(const void *)address->bits,buf,len);
+ if (s)
+ return Anode_strlen(s);
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ s = inet_ntop(AF_INET6,address->bits,buf,len);
+ if (s)
+ return Anode_strlen(s);
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ /*
+ case ANODE_NETWORK_ADDRESS_ETHERNET:
+ break;
+ case ANODE_NETWORK_ADDRESS_USB:
+ break;
+ case ANODE_NETWORK_ADDRESS_BLUETOOTH:
+ break;
+ case ANODE_NETWORK_ADDRESS_IPC:
+ break;
+ case ANODE_NETWORK_ADDRESS_80211S:
+ break;
+ case ANODE_NETWORK_ADDRESS_SERIAL:
+ break;
+ */
+ case ANODE_NETWORK_ADDRESS_ANODE_256_40:
+ return AnodeAddress_to_string((const AnodeAddress *)address->bits,buf,len);
+ default:
+ return ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ }
+}
+
+int AnodeNetworkAddress_from_string(const char *str,AnodeNetworkAddress *address)
+{
+ unsigned int dots = Anode_count_char(str,'.');
+ unsigned int colons = Anode_count_char(str,':');
+
+ if ((dots == 3)&&(!colons)) {
+ address->type = ANODE_NETWORK_ADDRESS_IPV4;
+ if (inet_pton(AF_INET,str,address->bits) > 0)
+ return 0;
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ } else if ((colons)&&(!dots)) {
+ address->type = ANODE_NETWORK_ADDRESS_IPV6;
+ if (inet_pton(AF_INET6,str,address->bits) > 0)
+ return 0;
+ else return ANODE_ERR_INVALID_ARGUMENT;
+ } else {
+ address->type = ANODE_NETWORK_ADDRESS_ANODE_256_40;
+ return AnodeAddress_from_string(str,(AnodeAddress *)address->bits);
+ }
+}
+
+int AnodeNetworkEndpoint_from_sockaddr(const void *sockaddr,AnodeNetworkEndpoint *endpoint)
+{
+ switch(((struct sockaddr_storage *)sockaddr)->ss_family) {
+ case AF_INET:
+ *((uint32_t *)endpoint->address.bits) = (uint32_t)(((struct sockaddr_in *)sockaddr)->sin_addr.s_addr);
+ endpoint->port = (int)ntohs(((struct sockaddr_in *)sockaddr)->sin_port);
+ return 0;
+ case AF_INET6:
+ Anode_memcpy(endpoint->address.bits,((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr,16);
+ endpoint->port = (int)ntohs(((struct sockaddr_in6 *)sockaddr)->sin6_port);
+ return 0;
+ default:
+ return ANODE_ERR_INVALID_ARGUMENT;
+ }
+}
+
+int AnodeNetworkEndpoint_to_sockaddr(const AnodeNetworkEndpoint *endpoint,void *sockaddr,int sockaddr_len)
+{
+ switch(endpoint->address.type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ if (sockaddr_len < (int)sizeof(struct sockaddr_in))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ Anode_zero(sockaddr,sizeof(struct sockaddr_in));
+ ((struct sockaddr_in *)sockaddr)->sin_family = AF_INET;
+ ((struct sockaddr_in *)sockaddr)->sin_port = htons((uint16_t)endpoint->port);
+ ((struct sockaddr_in *)sockaddr)->sin_addr.s_addr = *((uint32_t *)endpoint->address.bits);
+ return 0;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ if (sockaddr_len < (int)sizeof(struct sockaddr_in6))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ Anode_zero(sockaddr,sizeof(struct sockaddr_in6));
+ ((struct sockaddr_in6 *)sockaddr)->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *)sockaddr)->sin6_port = htons((uint16_t)endpoint->port);
+ Anode_memcpy(((struct sockaddr_in6 *)sockaddr)->sin6_addr.s6_addr,endpoint->address.bits,16);
+ return 0;
+ default:
+ return ANODE_ERR_INVALID_ARGUMENT;
+ }
+}
diff --git a/attic/historic/anode/libanode/secure_random.c b/attic/historic/anode/libanode/secure_random.c
new file mode 100644
index 00000000..4322d7de
--- /dev/null
+++ b/attic/historic/anode/libanode/secure_random.c
@@ -0,0 +1,88 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "impl/aes.h"
+#include "impl/misc.h"
+#include "anode.h"
+
+#ifdef WINDOWS
+#include <windows.h>
+#include <wincrypt.h>
+#endif
+
+struct AnodeSecureRandomImpl
+{
+ AnodeAesExpandedKey key;
+ unsigned char state[ANODE_AES_BLOCK_SIZE];
+ unsigned char block[ANODE_AES_BLOCK_SIZE];
+ unsigned int ptr;
+};
+
+AnodeSecureRandom *AnodeSecureRandom_new()
+{
+ unsigned char keybuf[ANODE_AES_KEY_SIZE + ANODE_AES_BLOCK_SIZE + ANODE_AES_BLOCK_SIZE];
+ unsigned int i;
+ struct AnodeSecureRandomImpl *srng;
+
+#ifdef WINDOWS
+ HCRYPTPROV hProv;
+ if (CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+ CryptGenRandom(hProv,sizeof(keybuf),keybuf);
+ CryptReleaseContext(hProv,0);
+ }
+#else
+ FILE *urandf = fopen("/dev/urandom","rb");
+ if (urandf) {
+ fread((void *)keybuf,sizeof(keybuf),1,urandf);
+ fclose(urandf);
+ }
+#endif
+
+ for(i=0;i<sizeof(keybuf);++i)
+ keybuf[i] ^= (unsigned char)(Anode_rand() >> 5);
+
+ srng = malloc(sizeof(struct AnodeSecureRandomImpl));
+ Anode_aes256_expand_key(keybuf,&srng->key);
+ for(i=0;i<ANODE_AES_BLOCK_SIZE;++i)
+ srng->state[i] = keybuf[ANODE_AES_KEY_SIZE + i];
+ for(i=0;i<ANODE_AES_BLOCK_SIZE;++i)
+ srng->block[i] = keybuf[ANODE_AES_KEY_SIZE + ANODE_AES_KEY_SIZE + i];
+ srng->ptr = ANODE_AES_BLOCK_SIZE;
+
+ return (AnodeSecureRandom *)srng;
+}
+
+void AnodeSecureRandom_gen_bytes(AnodeSecureRandom *srng,void *buf,long count)
+{
+ long i,j;
+
+ for(i=0;i<count;++i) {
+ if (((struct AnodeSecureRandomImpl *)srng)->ptr == ANODE_AES_BLOCK_SIZE) {
+ Anode_aes256_encrypt(&((struct AnodeSecureRandomImpl *)srng)->key,((struct AnodeSecureRandomImpl *)srng)->state,((struct AnodeSecureRandomImpl *)srng)->state);
+ for(j=0;j<ANODE_AES_KEY_SIZE;++j)
+ ((struct AnodeSecureRandomImpl *)srng)->block[j] ^= ((struct AnodeSecureRandomImpl *)srng)->state[j];
+ ((struct AnodeSecureRandomImpl *)srng)->ptr = 0;
+ }
+ ((unsigned char *)buf)[i] = ((struct AnodeSecureRandomImpl *)srng)->block[((struct AnodeSecureRandomImpl *)srng)->ptr++];
+ }
+}
+
+void AnodeSecureRandom_delete(AnodeSecureRandom *srng)
+{
+ free(srng);
+}
diff --git a/attic/historic/anode/libanode/system_transport.c b/attic/historic/anode/libanode/system_transport.c
new file mode 100644
index 00000000..4bfb143e
--- /dev/null
+++ b/attic/historic/anode/libanode/system_transport.c
@@ -0,0 +1,948 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include "anode.h"
+#include "impl/mutex.h"
+#include "impl/thread.h"
+#include "impl/misc.h"
+#include "impl/dns_txt.h"
+
+#ifdef WINDOWS
+#include <windows.h>
+#include <winsock2.h>
+#define AnodeSystemTransport__close_socket(s) closesocket((s))
+#define ANODE_USE_SELECT 1
+#else
+#include <poll.h>
+#include <unistd.h>
+#define AnodeSystemTransport__close_socket(s) close((s))
+#endif
+
+static const char *AnodeSystemTransport_CLASS = "SystemTransport";
+
+/* ======================================================================== */
+
+struct AnodeSystemTransport;
+
+struct AnodeSystemTransport_AnodeSocket
+{
+ AnodeSocket base; /* must be first */
+ unsigned int entry_idx;
+};
+
+#define ANODE_SYSTEM_TRANSPORT_DNS_MAX_RESULTS 16
+struct AnodeSystemTransport__dns_request
+{
+ struct AnodeSystemTransport__dns_request *next;
+
+ AnodeThread *thread;
+ struct AnodeSystemTransport *owner;
+
+ void (*event_handler)(const AnodeEvent *event);
+
+ char name[256];
+ enum AnodeTransportDnsIncludeMode ipv4_include_mode;
+ enum AnodeTransportDnsIncludeMode ipv6_include_mode;
+ enum AnodeTransportDnsIncludeMode anode_include_mode;
+
+ AnodeNetworkAddress addresses[ANODE_SYSTEM_TRANSPORT_DNS_MAX_RESULTS];
+ unsigned int address_count;
+
+ int error_code;
+};
+
+#ifdef ANODE_USE_SELECT
+typedef int AnodeSystemTransport__poll_fd; /* for select() */
+#else
+typedef struct pollfd AnodeSystemTransport__poll_fd; /* for poll() */
+#endif
+
+struct AnodeSystemTransport
+{
+ AnodeTransport interface; /* must be first */
+
+ AnodeTransport *base;
+
+#ifdef ANODE_USE_SELECT
+ FD_SET readfds;
+ FD_SET writefds;
+#endif
+
+ void (*default_event_handler)(const AnodeEvent *event);
+
+ AnodeSystemTransport__poll_fd *fds;
+ struct AnodeSystemTransport_AnodeSocket *sockets;
+ unsigned int fd_count;
+ unsigned int fd_capacity;
+
+ struct AnodeSystemTransport__dns_request *pending_dns_requests;
+
+ int invoke_pipe[2];
+ AnodeMutex invoke_pipe_m;
+ void *invoke_pipe_buf[2];
+ unsigned int invoke_pipe_buf_ptr;
+};
+
+/* ======================================================================== */
+/* Internal helper methods */
+
+static unsigned int AnodeSystemTransport__add_entry(struct AnodeSystemTransport *transport)
+{
+ if ((transport->fd_count + 1) > transport->fd_capacity) {
+ transport->fd_capacity += 8;
+ transport->fds = realloc(transport->fds,sizeof(AnodeSystemTransport__poll_fd) * transport->fd_capacity);
+ transport->sockets = realloc(transport->sockets,sizeof(struct AnodeSystemTransport_AnodeSocket) * transport->fd_capacity);
+ }
+ return transport->fd_count++;
+}
+
+static void AnodeSystemTransport__remove_entry(struct AnodeSystemTransport *transport,const unsigned int idx)
+{
+ unsigned int i;
+
+ --transport->fd_count;
+ for(i=idx;i<transport->fd_count;++i) {
+ Anode_memcpy(&transport->fds[i],&transport->fds[i+1],sizeof(AnodeSystemTransport__poll_fd));
+ Anode_memcpy(&transport->sockets[i],&transport->sockets[i+1],sizeof(struct AnodeSystemTransport_AnodeSocket));
+ }
+
+ if ((transport->fd_capacity - transport->fd_count) > 16) {
+ transport->fd_capacity -= 16;
+ transport->fds = realloc(transport->fds,sizeof(AnodeSystemTransport__poll_fd) * transport->fd_capacity);
+ transport->sockets = realloc(transport->sockets,sizeof(struct AnodeSystemTransport_AnodeSocket) * transport->fd_capacity);
+ }
+}
+
+static void AnodeSystemTransport__dns_invoke_on_completion(void *_dreq)
+{
+ struct AnodeSystemTransport__dns_request *dreq = (struct AnodeSystemTransport__dns_request *)_dreq;
+ struct AnodeSystemTransport__dns_request *ptr,**lastnext;
+
+ AnodeThread_join(dreq->thread);
+
+ ptr = dreq->owner->pending_dns_requests;
+ lastnext = &dreq->owner->pending_dns_requests;
+ while (ptr) {
+ if (ptr == dreq) {
+ *lastnext = ptr->next;
+ break;
+ } else {
+ lastnext = &ptr->next;
+ ptr = ptr->next;
+ }
+ }
+
+ free(dreq);
+}
+
+static void AnodeSystemTransport__dns_thread_main(void *_dreq)
+{
+ struct AnodeSystemTransport__dns_request *dreq = (struct AnodeSystemTransport__dns_request *)_dreq;
+
+ dreq->owner->interface.invoke((AnodeTransport *)dreq->owner,dreq,&AnodeSystemTransport__dns_invoke_on_completion);
+}
+
+static void AnodeSystemTransport__do_close(struct AnodeSystemTransport *transport,struct AnodeSystemTransport_AnodeSocket *sock,const int error_code,const int generate_event)
+{
+ AnodeEvent evbuf;
+ int fd;
+
+ if (sock->base.class_name == AnodeSystemTransport_CLASS) {
+#ifdef ANODE_USE_SELECT
+ fd = (int)(transport->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]);
+#else
+ fd = transport->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].fd;
+#endif
+
+ if ((sock->base.type == ANODE_SOCKET_STREAM_CONNECTION)&&(sock->base.state != ANODE_SOCKET_CLOSED)) {
+ sock->base.state = ANODE_SOCKET_CLOSED;
+
+ if (generate_event) {
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_CLOSED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = error_code;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ }
+ }
+
+ AnodeSystemTransport__close_socket(fd);
+ AnodeSystemTransport__remove_entry(transport,((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx);
+
+#ifdef ANODE_USE_SELECT
+ FD_CLR(sock,&THIS->readfds);
+ FD_CLR(sock,&THIS->writefds);
+#endif
+ } else transport->base->close(transport->base,(AnodeSocket *)sock);
+}
+
+static int AnodeSystemTransport__populate_network_endpoint(const struct sockaddr_storage *saddr,AnodeNetworkEndpoint *ep)
+{
+ switch(saddr->ss_family) {
+ case AF_INET:
+ ep->address.type = ANODE_NETWORK_ADDRESS_IPV4;
+ *((uint32_t *)ep->address.bits) = ((struct sockaddr_in *)saddr)->sin_addr.s_addr;
+ ep->port = ntohs(((struct sockaddr_in *)saddr)->sin_port);
+ return 1;
+ case AF_INET6:
+ ep->address.type = ANODE_NETWORK_ADDRESS_IPV6;
+ Anode_memcpy(ep->address.bits,((struct sockaddr_in6 *)saddr)->sin6_addr.s6_addr,16);
+ ep->port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
+ return 1;
+ }
+ return 0;
+}
+
+/* ======================================================================== */
+
+#ifdef THIS
+#undef THIS
+#endif
+#define THIS ((struct AnodeSystemTransport *)transport)
+
+static void AnodeSystemTransport_invoke(AnodeTransport *transport,
+ void *ptr,
+ void (*func)(void *))
+{
+ void *invoke_msg[2];
+
+ invoke_msg[0] = ptr;
+ invoke_msg[1] = (void *)func;
+
+ AnodeMutex_lock(&THIS->invoke_pipe_m);
+ write(THIS->invoke_pipe[1],(void *)(&invoke_msg),sizeof(invoke_msg));
+ AnodeMutex_unlock(&THIS->invoke_pipe_m);
+}
+
+static void AnodeSystemTransport_dns_resolve(AnodeTransport *transport,
+ const char *name,
+ void (*event_handler)(const AnodeEvent *),
+ enum AnodeTransportDnsIncludeMode ipv4_include_mode,
+ enum AnodeTransportDnsIncludeMode ipv6_include_mode,
+ enum AnodeTransportDnsIncludeMode anode_include_mode)
+{
+ struct AnodeSystemTransport__dns_request *dreq = malloc(sizeof(struct AnodeSystemTransport__dns_request));
+
+ dreq->owner = THIS;
+ dreq->event_handler = event_handler;
+ Anode_str_copy(dreq->name,name,sizeof(dreq->name));
+ dreq->ipv4_include_mode = ipv4_include_mode;
+ dreq->ipv6_include_mode = ipv6_include_mode;
+ dreq->anode_include_mode = anode_include_mode;
+
+ dreq->address_count = 0;
+ dreq->error_code = 0;
+
+ dreq->next = THIS->pending_dns_requests;
+ THIS->pending_dns_requests = dreq;
+
+ dreq->thread = AnodeThread_create(&AnodeSystemTransport__dns_thread_main,dreq,0);
+}
+
+static AnodeSocket *AnodeSystemTransport_datagram_listen(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+ struct AnodeSystemTransport_AnodeSocket *sock;
+ unsigned int entry_idx;
+ int fd;
+ int tmp;
+
+ switch(local_address->type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ fd = socket(AF_INET,SOCK_DGRAM,0);
+ if (fd <= 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ tmp = 1;
+ setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp));
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons(local_port);
+ sin4.sin_addr.s_addr = *((uint32_t *)local_address->bits);
+
+ if (bind(fd,(const struct sockaddr *)&sin4,sizeof(sin4))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ fd = socket(AF_INET6,SOCK_DGRAM,0);
+ if (fd <= 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ tmp = 1; setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp));
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+#ifdef IPV6_V6ONLY
+ tmp = 1; setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY,&tmp,sizeof(tmp));
+#endif
+
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(local_port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,local_address->bits,16);
+
+ if (bind(fd,(const struct sockaddr *)&sin6,sizeof(sin6))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ default:
+ if (THIS->base)
+ return THIS->base->datagram_listen(THIS->base,local_address,local_port,error_code);
+ else {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ }
+
+ entry_idx = AnodeSystemTransport__add_entry(THIS);
+ sock = &(THIS->sockets[entry_idx]);
+
+ sock->base.type = ANODE_SOCKET_DATAGRAM;
+ sock->base.state = ANODE_SOCKET_OPEN;
+ Anode_memcpy(&sock->base.endpoint.address,local_address,sizeof(AnodeNetworkAddress));
+ sock->base.endpoint.port = local_port;
+ sock->base.class_name = AnodeSystemTransport_CLASS;
+ sock->base.user_ptr[0] = NULL;
+ sock->base.user_ptr[1] = NULL;
+ sock->base.event_handler = NULL;
+ sock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = fd;
+ THIS->fds[entry_idx].events = POLLIN;
+ THIS->fds[entry_idx].revents = 0;
+
+ *error_code = 0;
+ return (AnodeSocket *)sock;
+}
+
+static AnodeSocket *AnodeSystemTransport_stream_listen(AnodeTransport *transport,
+ const AnodeNetworkAddress *local_address,
+ int local_port,
+ int *error_code)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+ struct AnodeSystemTransport_AnodeSocket *sock;
+ unsigned int entry_idx;
+ int fd;
+ int tmp;
+
+ switch(local_address->type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ fd = socket(AF_INET,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons(local_port);
+ sin4.sin_addr.s_addr = *((uint32_t *)local_address->bits);
+
+ if (bind(fd,(const struct sockaddr *)&sin4,sizeof(sin4))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ if (listen(fd,8)) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ fd = socket(AF_INET6,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+#ifdef IPV6_V6ONLY
+ tmp = 1; setsockopt(fd,IPPROTO_IPV6,IPV6_V6ONLY,&tmp,sizeof(tmp));
+#endif
+
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(local_port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,local_address->bits,16);
+
+ if (bind(fd,(const struct sockaddr *)&sin6,sizeof(sin6))) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ if (listen(fd,8)) {
+ AnodeSystemTransport__close_socket(fd);
+ *error_code = ANODE_ERR_UNABLE_TO_BIND;
+ return (AnodeSocket *)0;
+ }
+ break;
+ default:
+ if (THIS->base)
+ return THIS->base->stream_listen(THIS->base,local_address,local_port,error_code);
+ else {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ }
+
+ entry_idx = AnodeSystemTransport__add_entry(THIS);
+ sock = &(THIS->sockets[entry_idx]);
+
+ sock->base.type = ANODE_SOCKET_STREAM_LISTEN;
+ sock->base.state = ANODE_SOCKET_OPEN;
+ Anode_memcpy(&sock->base.endpoint.address,local_address,sizeof(AnodeNetworkAddress));
+ sock->base.endpoint.port = local_port;
+ sock->base.class_name = AnodeSystemTransport_CLASS;
+ sock->base.user_ptr[0] = NULL;
+ sock->base.user_ptr[1] = NULL;
+ sock->base.event_handler = NULL;
+ sock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = fd;
+ THIS->fds[entry_idx].events = POLLIN;
+ THIS->fds[entry_idx].revents = 0;
+
+ *error_code = 0;
+ return (AnodeSocket *)sock;
+}
+
+static int AnodeSystemTransport_datagram_send(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len,
+ const AnodeNetworkEndpoint *to_endpoint)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+
+#ifdef ANODE_USE_SELECT
+ const int fd = (int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]);
+#else
+ const int fd = THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].fd;
+#endif
+
+ switch(to_endpoint->address.type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons((uint16_t)to_endpoint->port);
+ sin4.sin_addr.s_addr = *((uint32_t *)to_endpoint->address.bits);
+ sendto(fd,data,data_len,0,(struct sockaddr *)&sin4,sizeof(sin4));
+ return 0;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons((uint16_t)to_endpoint->port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,to_endpoint->address.bits,16);
+ sendto(fd,data,data_len,0,(struct sockaddr *)&sin6,sizeof(sin6));
+ return 0;
+ default:
+ if (THIS->base)
+ return THIS->base->datagram_send(THIS->base,sock,data,data_len,to_endpoint);
+ else return ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ }
+}
+
+static AnodeSocket *AnodeSystemTransport_stream_connect(AnodeTransport *transport,
+ const AnodeNetworkEndpoint *to_endpoint,
+ int *error_code)
+{
+ struct sockaddr_in sin4;
+ struct sockaddr_in6 sin6;
+ struct AnodeSystemTransport_AnodeSocket *sock;
+ unsigned int entry_idx;
+ int fd;
+
+ switch(to_endpoint->address.type) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ Anode_zero(&sin4,sizeof(struct sockaddr_in));
+ sin4.sin_family = AF_INET;
+ sin4.sin_port = htons(to_endpoint->port);
+ sin4.sin_addr.s_addr = *((uint32_t *)to_endpoint->address.bits);
+
+ fd = socket(AF_INET,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ if (connect(fd,(struct sockaddr *)&sin4,sizeof(sin4))) {
+ if (errno != EINPROGRESS) {
+ *error_code = ANODE_ERR_CONNECT_FAILED;
+ AnodeSystemTransport__close_socket(fd);
+ return (AnodeSocket *)0;
+ }
+ }
+ break;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ Anode_zero(&sin6,sizeof(struct sockaddr_in6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(to_endpoint->port);
+ Anode_memcpy(sin6.sin6_addr.s6_addr,to_endpoint->address.bits,16);
+
+ fd = socket(AF_INET6,SOCK_STREAM,0);
+ if (fd < 0) {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ fcntl(fd,F_SETFL,O_NONBLOCK);
+
+ if (connect(fd,(struct sockaddr *)&sin6,sizeof(sin6))) {
+ if (errno == EINPROGRESS) {
+ *error_code = ANODE_ERR_CONNECT_FAILED;
+ AnodeSystemTransport__close_socket(fd);
+ return (AnodeSocket *)0;
+ }
+ }
+ break;
+ default:
+ if (THIS->base)
+ return THIS->base->stream_connect(THIS->base,to_endpoint,error_code);
+ else {
+ *error_code = ANODE_ERR_ADDRESS_TYPE_NOT_SUPPORTED;
+ return (AnodeSocket *)0;
+ }
+ }
+
+ entry_idx = AnodeSystemTransport__add_entry(THIS);
+ sock = &(THIS->sockets[entry_idx]);
+
+ sock->base.type = ANODE_SOCKET_STREAM_CONNECTION;
+ sock->base.state = ANODE_SOCKET_CONNECTING;
+ Anode_memcpy(&sock->base.endpoint,to_endpoint,sizeof(AnodeNetworkEndpoint));
+ sock->base.class_name = AnodeSystemTransport_CLASS;
+ sock->base.user_ptr[0] = NULL;
+ sock->base.user_ptr[1] = NULL;
+ sock->base.event_handler = NULL;
+ sock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = fd;
+ THIS->fds[entry_idx].events = POLLIN|POLLOUT;
+ THIS->fds[entry_idx].revents = 0;
+
+ return (AnodeSocket *)sock;
+}
+
+static void AnodeSystemTransport_stream_start_writing(AnodeTransport *transport,
+ AnodeSocket *sock)
+{
+ if ((sock->type == ANODE_SOCKET_STREAM_CONNECTION)&&(((struct AnodeSystemTransport_AnodeSocket *)sock)->base.state == ANODE_SOCKET_OPEN)) {
+ if (sock->class_name == AnodeSystemTransport_CLASS) {
+#ifdef ANODE_USE_SELECT
+ FD_SET((int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]),&THIS->writefds);
+#else
+ THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].events = (POLLIN|POLLOUT);
+#endif
+ } else THIS->base->stream_start_writing(THIS->base,sock);
+ }
+}
+
+static void AnodeSystemTransport_stream_stop_writing(AnodeTransport *transport,
+ AnodeSocket *sock)
+{
+ if ((sock->type == ANODE_SOCKET_STREAM_CONNECTION)&&(((struct AnodeSystemTransport_AnodeSocket *)sock)->base.state == ANODE_SOCKET_OPEN)) {
+ if (sock->class_name == AnodeSystemTransport_CLASS) {
+#ifdef ANODE_USE_SELECT
+ FD_CLR((int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]),&THIS->writefds);
+#else
+ THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].events = POLLIN;
+#endif
+ } else THIS->base->stream_stop_writing(THIS->base,sock);
+ }
+}
+
+static int AnodeSystemTransport_stream_send(AnodeTransport *transport,
+ AnodeSocket *sock,
+ const void *data,
+ int data_len)
+{
+ int result;
+
+ if (sock->type == ANODE_SOCKET_STREAM_CONNECTION) {
+ if (sock->class_name == AnodeSystemTransport_CLASS) {
+ if (((struct AnodeSystemTransport_AnodeSocket *)sock)->base.state != ANODE_SOCKET_OPEN)
+ return ANODE_ERR_CONNECTION_CLOSED;
+
+#ifdef ANODE_USE_SELECT
+ result = send((int)(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx]),data,data_len,0);
+#else
+ result = send(THIS->fds[((struct AnodeSystemTransport_AnodeSocket *)sock)->entry_idx].fd,data,data_len,0);
+#endif
+
+ if (result >= 0)
+ return result;
+ else {
+ AnodeSystemTransport__do_close(THIS,(struct AnodeSystemTransport_AnodeSocket *)sock,ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE,1);
+ return ANODE_ERR_CONNECTION_CLOSED;
+ }
+ } else return THIS->base->stream_send(THIS->base,sock,data,data_len);
+ } else return ANODE_ERR_INVALID_ARGUMENT;
+}
+
+static void AnodeSystemTransport_close(AnodeTransport *transport,
+ AnodeSocket *sock)
+{
+ AnodeSystemTransport__do_close(THIS,(struct AnodeSystemTransport_AnodeSocket *)sock,0,1);
+}
+
+static void AnodeSystemTransport__poll_do_read_datagram(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ char buf[16384];
+ struct sockaddr_storage fromaddr;
+ AnodeNetworkEndpoint tmp_ep;
+ AnodeEvent evbuf;
+ socklen_t addrlen;
+ int n;
+
+ addrlen = sizeof(struct sockaddr_storage);
+ n = recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr *)&fromaddr,&addrlen);
+ if ((n >= 0)&&(AnodeSystemTransport__populate_network_endpoint(&fromaddr,&tmp_ep))) {
+ evbuf.type = ANODE_TRANSPORT_EVENT_DATAGRAM_RECEIVED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = &tmp_ep;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = n;
+ evbuf.data = buf;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ }
+}
+
+static void AnodeSystemTransport__poll_do_accept_incoming_connection(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ struct sockaddr_storage fromaddr;
+ AnodeNetworkEndpoint tmp_ep;
+ AnodeEvent evbuf;
+ struct AnodeSystemTransport_AnodeSocket *newsock;
+ socklen_t addrlen;
+ int n;
+ unsigned int entry_idx;
+
+ addrlen = sizeof(struct sockaddr_storage);
+ n = accept(fd,(struct sockaddr *)&fromaddr,&addrlen);
+ if ((n >= 0)&&(AnodeSystemTransport__populate_network_endpoint(&fromaddr,&tmp_ep))) {
+ entry_idx = AnodeSystemTransport__add_entry(transport);
+ newsock = &(transport->sockets[entry_idx]);
+
+ newsock->base.type = ANODE_SOCKET_STREAM_CONNECTION;
+ newsock->base.state = ANODE_SOCKET_OPEN;
+ Anode_memcpy(&newsock->base.endpoint,&tmp_ep,sizeof(AnodeNetworkEndpoint));
+ newsock->base.class_name = AnodeSystemTransport_CLASS;
+ newsock->base.user_ptr[0] = NULL;
+ newsock->base.user_ptr[1] = NULL;
+ newsock->base.event_handler = NULL;
+ newsock->entry_idx = entry_idx;
+
+ THIS->fds[entry_idx].fd = n;
+ THIS->fds[entry_idx].events = POLLIN;
+ THIS->fds[entry_idx].revents = 0;
+
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_INCOMING_CONNECT;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)newsock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ }
+}
+
+static void AnodeSystemTransport__poll_do_read_stream(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ char buf[65536];
+ AnodeEvent evbuf;
+ int n;
+
+ n = recv(fd,buf,sizeof(buf),0);
+ if (n > 0) {
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_DATA_RECEIVED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = n;
+ evbuf.data = buf;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+ } else AnodeSystemTransport__do_close(transport,sock,ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE,1);
+}
+
+static void AnodeSystemTransport__poll_do_stream_available_for_write(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ AnodeEvent evbuf;
+
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_DATA_RECEIVED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+}
+
+static void AnodeSystemTransport__poll_do_outgoing_connect(struct AnodeSystemTransport *transport,int fd,struct AnodeSystemTransport_AnodeSocket *sock)
+{
+ AnodeEvent evbuf;
+ int err_code;
+ socklen_t optlen;
+
+ optlen = sizeof(err_code);
+ if (getsockopt(fd,SOL_SOCKET,SO_ERROR,(void *)&err_code,&optlen)) {
+ /* Error getting result, so we assume a failure */
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_FAILED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = ANODE_ERR_CONNECT_FAILED;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ AnodeSystemTransport__do_close(transport,sock,0,0);
+ } else if (err_code) {
+ /* Error code is nonzero, so connect failed */
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_FAILED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = ANODE_ERR_CONNECT_FAILED;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+
+ AnodeSystemTransport__do_close(transport,sock,0,0);
+ } else {
+ /* Connect succeeded */
+ evbuf.type = ANODE_TRANSPORT_EVENT_STREAM_OUTGOING_CONNECT_ESTABLISHED;
+ evbuf.transport = (AnodeTransport *)transport;
+ evbuf.sock = (AnodeSocket *)sock;
+ evbuf.datagram_from = NULL;
+ evbuf.dns_name = NULL;
+ evbuf.dns_addresses = NULL;
+ evbuf.dns_address_count = 0;
+ evbuf.error_code = 0;
+ evbuf.data_length = 0;
+ evbuf.data = NULL;
+ }
+
+ if (sock->base.event_handler)
+ sock->base.event_handler(&evbuf);
+ else if (transport->default_event_handler)
+ transport->default_event_handler(&evbuf);
+}
+
+static int AnodeSystemTransport_poll(AnodeTransport *transport)
+{
+ int timeout = -1;
+ unsigned int fd_idx;
+ int event_count = 0;
+ int n;
+
+ if (poll((struct pollfd *)THIS->fds,THIS->fd_count,timeout) > 0) {
+ for(fd_idx=0;fd_idx<THIS->fd_count;++fd_idx) {
+ if ((THIS->fds[fd_idx].revents & (POLLERR|POLLHUP|POLLNVAL))) {
+ if (THIS->sockets[fd_idx].base.type == ANODE_SOCKET_STREAM_CONNECTION) {
+ if (THIS->sockets[fd_idx].base.state == ANODE_SOCKET_CONNECTING)
+ AnodeSystemTransport__poll_do_outgoing_connect(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ else AnodeSystemTransport__do_close(THIS,&THIS->sockets[fd_idx],ANODE_ERR_CONNECTION_CLOSED_BY_REMOTE,1);
+ ++event_count;
+ }
+ } else {
+ if ((THIS->fds[fd_idx].revents & POLLIN)) {
+ if (THIS->fds[fd_idx].fd == THIS->invoke_pipe[0]) {
+ n = read(THIS->invoke_pipe[0],&(((unsigned char *)(&(THIS->invoke_pipe_buf)))[THIS->invoke_pipe_buf_ptr]),sizeof(THIS->invoke_pipe_buf) - THIS->invoke_pipe_buf_ptr);
+ if (n > 0) {
+ THIS->invoke_pipe_buf_ptr += (unsigned int)n;
+ if (THIS->invoke_pipe_buf_ptr >= sizeof(THIS->invoke_pipe_buf)) {
+ THIS->invoke_pipe_buf_ptr -= sizeof(THIS->invoke_pipe_buf);
+ ((void (*)(void *))(THIS->invoke_pipe_buf[1]))(THIS->invoke_pipe_buf[0]);
+ }
+ }
+ } else {
+ switch(THIS->sockets[fd_idx].base.type) {
+ case ANODE_SOCKET_DATAGRAM:
+ AnodeSystemTransport__poll_do_read_datagram(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ break;
+ case ANODE_SOCKET_STREAM_LISTEN:
+ AnodeSystemTransport__poll_do_accept_incoming_connection(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ break;
+ case ANODE_SOCKET_STREAM_CONNECTION:
+ if (THIS->sockets[fd_idx].base.state == ANODE_SOCKET_CONNECTING)
+ AnodeSystemTransport__poll_do_outgoing_connect(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ else AnodeSystemTransport__poll_do_read_stream(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ break;
+ }
+ ++event_count;
+ }
+ }
+
+ if ((THIS->fds[fd_idx].revents & POLLOUT)) {
+ if (THIS->sockets[fd_idx].base.state == ANODE_SOCKET_CONNECTING)
+ AnodeSystemTransport__poll_do_outgoing_connect(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ else AnodeSystemTransport__poll_do_stream_available_for_write(THIS,THIS->fds[fd_idx].fd,&THIS->sockets[fd_idx]);
+ ++event_count;
+ }
+ }
+ }
+ }
+
+ return event_count;
+}
+
+static int AnodeSystemTransport_supports_address_type(const AnodeTransport *transport,
+ enum AnodeNetworkAddressType at)
+{
+ switch(at) {
+ case ANODE_NETWORK_ADDRESS_IPV4:
+ return 1;
+ case ANODE_NETWORK_ADDRESS_IPV6:
+ return 1;
+ default:
+ if (THIS->base)
+ return THIS->base->supports_address_type(THIS->base,at);
+ return 0;
+ }
+}
+
+static AnodeTransport *AnodeSystemTransport_base_instance(const AnodeTransport *transport)
+{
+ return THIS->base;
+}
+
+static const char *AnodeSystemTransport_class_name(AnodeTransport *transport)
+{
+ return AnodeSystemTransport_CLASS;
+}
+
+static void AnodeSystemTransport_delete(AnodeTransport *transport)
+{
+ close(THIS->invoke_pipe[0]);
+ close(THIS->invoke_pipe[1]);
+
+ AnodeMutex_destroy(&THIS->invoke_pipe_m);
+
+ if (THIS->fds) free(THIS->fds);
+ if (THIS->sockets) free(THIS->sockets);
+
+ if (THIS->base) THIS->base->delete(THIS->base);
+
+ free(transport);
+}
+
+/* ======================================================================== */
+
+AnodeTransport *AnodeSystemTransport_new(AnodeTransport *base)
+{
+ struct AnodeSystemTransport *t;
+ unsigned int entry_idx;
+
+ t = malloc(sizeof(struct AnodeSystemTransport));
+ if (!t) return (AnodeTransport *)0;
+ Anode_zero(t,sizeof(struct AnodeSystemTransport));
+
+ t->interface.invoke = &AnodeSystemTransport_invoke;
+ t->interface.dns_resolve = &AnodeSystemTransport_dns_resolve;
+ t->interface.datagram_listen = &AnodeSystemTransport_datagram_listen;
+ t->interface.stream_listen = &AnodeSystemTransport_stream_listen;
+ t->interface.datagram_send = &AnodeSystemTransport_datagram_send;
+ t->interface.stream_connect = &AnodeSystemTransport_stream_connect;
+ t->interface.stream_start_writing = &AnodeSystemTransport_stream_start_writing;
+ t->interface.stream_stop_writing = &AnodeSystemTransport_stream_stop_writing;
+ t->interface.stream_send = &AnodeSystemTransport_stream_send;
+ t->interface.close = &AnodeSystemTransport_close;
+ t->interface.poll = &AnodeSystemTransport_poll;
+ t->interface.supports_address_type = &AnodeSystemTransport_supports_address_type;
+ t->interface.base_instance = &AnodeSystemTransport_base_instance;
+ t->interface.class_name = &AnodeSystemTransport_class_name;
+ t->interface.delete = &AnodeSystemTransport_delete;
+
+ t->base = base;
+
+ pipe(t->invoke_pipe);
+ fcntl(t->invoke_pipe[0],F_SETFL,O_NONBLOCK);
+ entry_idx = AnodeSystemTransport__add_entry(t);
+ t->fds[entry_idx].fd = t->invoke_pipe[0];
+ t->fds[entry_idx].events = POLLIN;
+ t->fds[entry_idx].revents = 0;
+ AnodeMutex_init(&t->invoke_pipe_m);
+
+ return (AnodeTransport *)t;
+}
diff --git a/attic/historic/anode/libanode/tests/Makefile b/attic/historic/anode/libanode/tests/Makefile
new file mode 100644
index 00000000..a479092c
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/Makefile
@@ -0,0 +1,25 @@
+all: force clean anode-utils-test anode-zone-test aes-test ec-test
+
+aes-test:
+ gcc -Wall -O6 -ftree-vectorize -std=c99 -o aes-test aes-test.c ../aes_digest.c -lcrypto
+
+http_client-test:
+ gcc -O0 -g -std=c99 -o http_client-test http_client-test.c ../anode-utils.c ../misc.c ../http_client.c ../dictionary.c ../iptransport.c ../anode-transport.c -lcrypto
+
+anode-utils-test:
+ gcc -O0 -g -std=c99 -o anode-utils-test anode-utils-test.c ../anode-utils.c ../misc.c
+
+ec-test:
+ gcc -O0 -g -std=c99 -o ec-test ec-test.c ../impl/ec.c ../impl/misc.c -lcrypto
+
+anode-zone-test:
+ gcc -O0 -g -std=c99 -o anode-zone-test anode-zone-test.c ../anode-zone.c ../http_client.c ../dictionary.c ../misc.c ../anode-transport.c ../iptransport.c ../environment.c
+
+system_transport-test:
+ gcc -O0 -g -std=c99 -o system_transport-test system_transport-test.c ../system_transport.c ../network_address.c ../address.c ../aes_digest.c ../impl/misc.c ../impl/thread.c ../impl/dns_txt.c ../impl/aes.c -lresolv -lcrypto
+
+clean: force
+ rm -rf *.dSYM
+ rm -f http_client-test anode-utils-test anode-zone-test ec-test aes-test system_transport-test
+
+force: ;
diff --git a/attic/historic/anode/libanode/tests/aes-test.c b/attic/historic/anode/libanode/tests/aes-test.c
new file mode 100644
index 00000000..bca63b89
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/aes-test.c
@@ -0,0 +1,191 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../impl/aes.h"
+#include "../anode.h"
+
+static const unsigned char AES_TEST_KEY[32] = {
+ 0x08,0x09,0x0A,0x0B,0x0D,0x0E,0x0F,0x10,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1A,
+ 0x1C,0x1D,0x1E,0x1F,0x21,0x22,0x23,0x24,0x26,0x27,0x28,0x29,0x2B,0x2C,0x2D,0x2E
+};
+static const unsigned char AES_TEST_IN[16] = {
+ 0x06,0x9A,0x00,0x7F,0xC7,0x6A,0x45,0x9F,0x98,0xBA,0xF9,0x17,0xFE,0xDF,0x95,0x21
+};
+static const unsigned char AES_TEST_OUT[16] = {
+ 0x08,0x0e,0x95,0x17,0xeb,0x16,0x77,0x71,0x9a,0xcf,0x72,0x80,0x86,0x04,0x0a,0xe3
+};
+
+static const unsigned char CMAC_TEST_KEY[32] = {
+ 0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,
+ 0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4
+};
+
+static const unsigned char CMAC_TEST1_OUT[16] = {
+ 0x02,0x89,0x62,0xf6,0x1b,0x7b,0xf8,0x9e,0xfc,0x6b,0x55,0x1f,0x46,0x67,0xd9,0x83
+};
+
+static const unsigned char CMAC_TEST2_IN[16] = {
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a
+};
+static const unsigned char CMAC_TEST2_OUT[16] = {
+ 0x28,0xa7,0x02,0x3f,0x45,0x2e,0x8f,0x82,0xbd,0x4b,0xf2,0x8d,0x8c,0x37,0xc3,0x5c
+};
+
+static const unsigned char CMAC_TEST3_IN[40] = {
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,
+ 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51,
+ 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11
+};
+static const unsigned char CMAC_TEST3_OUT[16] = {
+ 0xaa,0xf3,0xd8,0xf1,0xde,0x56,0x40,0xc2,0x32,0xf5,0xb1,0x69,0xb9,0xc9,0x11,0xe6
+};
+
+static const unsigned char CMAC_TEST4_IN[64] = {
+ 0x6b,0xc1,0xbe,0xe2,0x2e,0x40,0x9f,0x96,0xe9,0x3d,0x7e,0x11,0x73,0x93,0x17,0x2a,
+ 0xae,0x2d,0x8a,0x57,0x1e,0x03,0xac,0x9c,0x9e,0xb7,0x6f,0xac,0x45,0xaf,0x8e,0x51,
+ 0x30,0xc8,0x1c,0x46,0xa3,0x5c,0xe4,0x11,0xe5,0xfb,0xc1,0x19,0x1a,0x0a,0x52,0xef,
+ 0xf6,0x9f,0x24,0x45,0xdf,0x4f,0x9b,0x17,0xad,0x2b,0x41,0x7b,0xe6,0x6c,0x37,0x10
+};
+static const unsigned char CMAC_TEST4_OUT[16] = {
+ 0xe1,0x99,0x21,0x90,0x54,0x9f,0x6e,0xd5,0x69,0x6a,0x2c,0x05,0x6c,0x31,0x54,0x10
+};
+
+static void test_cmac(const AnodeAesExpandedKey *expkey,const unsigned char *in,unsigned int inlen,const unsigned char *expected)
+{
+ unsigned int i;
+ unsigned char out[16];
+
+ printf("Testing CMAC with %u byte input:\n",inlen);
+ printf(" IN: ");
+ for(i=0;i<inlen;++i)
+ printf("%.2x",(int)in[i]);
+ printf("\n");
+ printf(" EXP: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)expected[i]);
+ printf("\n");
+ Anode_cmac_aes256(expkey,in,inlen,out);
+ printf(" OUT: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)out[i]);
+ printf("\n");
+ if (memcmp(expected,out,16)) {
+ printf("FAILED!\n");
+ exit(1);
+ } else printf("Passed.\n");
+}
+
+static void test_cfb(const AnodeAesExpandedKey *expkey,const unsigned char *in,unsigned int inlen,unsigned char *iv,const unsigned char *expected)
+{
+ unsigned char tmp[131072];
+ unsigned char tmp2[131072];
+ unsigned char tmpiv[16];
+
+ printf("Testing AES-256 CFB mode with %u bytes: ",inlen);
+ fflush(stdout);
+
+ memcpy(tmpiv,iv,16);
+ Anode_aes256_cfb_encrypt(expkey,in,tmp,tmpiv,inlen);
+ if (!memcmp(tmp,expected,inlen)) {
+ printf("FAILED (didn't encrypt)!\n");
+ exit(1);
+ }
+ memcpy(tmpiv,iv,16);
+ Anode_aes256_cfb_decrypt(expkey,tmp,tmp2,tmpiv,inlen);
+ if (memcmp(tmp2,expected,inlen)) {
+ printf("FAILED (didn't encrypt)!\n");
+ exit(1);
+ } else printf("Passed.\n");
+}
+
+static const char *AES_DIGEST_TEST_1 = "test";
+static const char *AES_DIGEST_TEST_2 = "supercalifragilisticexpealidocious";
+static const char *AES_DIGEST_TEST_3 = "12345678";
+static const char *AES_DIGEST_TEST_4 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+
+int main(int argc,char **argv)
+{
+ AnodeAesExpandedKey expkey;
+ unsigned int i;
+ unsigned char aestestbuf[16];
+ unsigned char cfbin[131072];
+ unsigned char iv[16];
+
+ printf("Testing AES-256:");
+ Anode_aes256_expand_key(AES_TEST_KEY,&expkey);
+ printf(" IN: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)AES_TEST_IN[i]);
+ printf("\n");
+ printf(" EXP: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)AES_TEST_OUT[i]);
+ printf("\n");
+ Anode_aes256_encrypt(&expkey,AES_TEST_IN,aestestbuf);
+ printf(" OUT: ");
+ for(i=0;i<16;++i)
+ printf("%.2x",(int)aestestbuf[i]);
+ printf("\n");
+ if (memcmp(AES_TEST_OUT,aestestbuf,16)) {
+ printf("FAILED!\n");
+ return 1;
+ } else printf("Passed.\n");
+ printf("\n");
+
+ Anode_aes256_expand_key(CMAC_TEST_KEY,&expkey);
+ test_cmac(&expkey,(unsigned char *)0,0,CMAC_TEST1_OUT);
+ test_cmac(&expkey,CMAC_TEST2_IN,16,CMAC_TEST2_OUT);
+ test_cmac(&expkey,CMAC_TEST3_IN,40,CMAC_TEST3_OUT);
+ test_cmac(&expkey,CMAC_TEST4_IN,64,CMAC_TEST4_OUT);
+ printf("\n");
+
+ for(i=0;i<131072;++i)
+ cfbin[i] = (unsigned char)(i & 0xff);
+ for(i=0;i<16;++i)
+ iv[i] = (unsigned char)(i & 0xff);
+ for(i=12345;i<131072;i+=7777)
+ test_cfb(&expkey,cfbin,i,iv,cfbin);
+
+ printf("\nTesting AES-DIGEST...\n");
+ printf("0 bytes: ");
+ Anode_aes_digest(cfbin,0,iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_1));
+ Anode_aes_digest(AES_DIGEST_TEST_1,strlen(AES_DIGEST_TEST_1),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_2));
+ Anode_aes_digest(AES_DIGEST_TEST_2,strlen(AES_DIGEST_TEST_2),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_3));
+ Anode_aes_digest(AES_DIGEST_TEST_3,strlen(AES_DIGEST_TEST_3),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+ printf("%d bytes: ",(int)strlen(AES_DIGEST_TEST_4));
+ Anode_aes_digest(AES_DIGEST_TEST_4,strlen(AES_DIGEST_TEST_4),iv);
+ for(i=0;i<16;++i) printf("%.2x",(unsigned int)iv[i]);
+ printf("\n");
+
+ return 0;
+}
+
diff --git a/attic/historic/anode/libanode/tests/anode-secure_random-test.c b/attic/historic/anode/libanode/tests/anode-secure_random-test.c
new file mode 100644
index 00000000..a6983653
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/anode-secure_random-test.c
@@ -0,0 +1,38 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../anode.h"
+#include "../misc.h"
+
+int main(int argc,char **argv)
+{
+ unsigned char test[10005];
+ unsigned int i;
+ AnodeSecureRandom srng;
+
+ AnodeSecureRandom_init(&srng);
+
+ AnodeSecureRandom_gen_bytes(&srng,test,sizeof(test));
+
+ for(i=0;i<sizeof(test);++i) {
+ printf("%.2x",(unsigned int)test[i]);
+ if ((i % 20) == 19)
+ printf("\n");
+ }
+ printf("\n");
+}
diff --git a/attic/historic/anode/libanode/tests/anode-utils-test.c b/attic/historic/anode/libanode/tests/anode-utils-test.c
new file mode 100644
index 00000000..85bfe324
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/anode-utils-test.c
@@ -0,0 +1,75 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../anode.h"
+#include "../misc.h"
+
+static const char *testuris[22] = {
+ "http://www.test.com",
+ "http://www.test.com/",
+ "http://www.test.com/path/to/something",
+ "http://user@www.test.com",
+ "http://user@www.test.com/path/to/something",
+ "http://user:password@www.test.com/path/to/something",
+ "http://www.test.com/path/to/something?query=foo&bar=baz",
+ "http://www.test.com/path/to/something#fragment",
+ "http://www.test.com/path/to/something?query=foo&bar=baz#fragment",
+ "http://user:password@www.test.com/path/to/something#fragment",
+ "http://user:password@www.test.com/path/to/something?query=foo&bar=baz#fragment",
+ "http://@www.test.com/",
+ "http://:@www.test.com/",
+ "http://www.test.com:8080/path/to/something",
+ "http://user:password@www.test.com:8080/path/to/something?query=foo#fragment",
+ "http://",
+ "http://www.test.com/path/to/something?#",
+ "http://www.test.com/path/to/something?#fragment",
+ "http:",
+ "http",
+ "mailto:this_is_a_urn@somedomain.com",
+ ""
+};
+
+int main(int argc,char **argv)
+{
+ int i,r;
+ char reconstbuf[2048];
+ char *reconst;
+ AnodeURI uri;
+
+ for(i=0;i<22;++i) {
+ printf("\"%s\":\n",testuris[i]);
+ r = AnodeURI_parse(&uri,testuris[i]);
+ if (r) {
+ printf(" error: %d\n",r);
+ } else {
+ printf(" scheme: %s\n",uri.scheme);
+ printf(" username: %s\n",uri.username);
+ printf(" password: %s\n",uri.password);
+ printf(" host: %s\n",uri.host);
+ printf(" port: %d\n",uri.port);
+ printf(" path: %s\n",uri.path);
+ printf(" query: %s\n",uri.query);
+ printf(" fragment: %s\n",uri.fragment);
+ }
+ reconst = AnodeURI_to_string(&uri,reconstbuf,sizeof(reconstbuf));
+ printf("Reconstituted URI: %s\n",reconst ? reconst : "(null)");
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/anode-zone-test.c b/attic/historic/anode/libanode/tests/anode-zone-test.c
new file mode 100644
index 00000000..08396716
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/anode-zone-test.c
@@ -0,0 +1,47 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../anode.h"
+#include "../dictionary.h"
+
+static int got_it = 0;
+
+static void zone_lookup_handler(void *ptr,long zone_id,AnodeZone *zone)
+{
+ if (zone)
+ printf("got %.8lx: %d entries\n",(unsigned long)zone_id & 0xffffffff,((struct AnodeDictionary *)zone)->size);
+ else printf("failed.\n");
+ got_it = 1;
+}
+
+int main(int argc,char **argv)
+{
+ AnodeTransportEngine transport;
+
+ Anode_init_ip_transport_engine(&transport);
+
+ AnodeZone_lookup(&transport,0,0,&zone_lookup_handler);
+
+ while (!got_it)
+ transport.poll(&transport);
+
+ transport.destroy(&transport);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/dictionary-test.c b/attic/historic/anode/libanode/tests/dictionary-test.c
new file mode 100644
index 00000000..12a5fb2f
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/dictionary-test.c
@@ -0,0 +1,149 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include "../dictionary.h"
+
+static const char *HASH_TESTS[16] = {
+ "test",
+ "testt",
+ "",
+ "foo",
+ "fooo",
+ "1",
+ "2",
+ "3",
+ "4",
+ "11",
+ "22",
+ "33",
+ "44",
+ "adklfjklejrer",
+ "erngnetbekjrq",
+ "erklerqqqqre"
+};
+
+int diterate(void *arg,const char *key,const char *value)
+{
+ printf(" %s: %s\n",key ? key : "(null)",value ? value : "(null)");
+ return 1;
+}
+
+int main(int argc,char **argv)
+{
+ char tmp[1024];
+ char fuzzparam1[16],fuzzparam2[16],fuzzparam3[16];
+ struct AnodeDictionary d;
+ unsigned int i,j,k,cs;
+
+ srandom(time(0));
+
+ printf("Trying out hash function a little...\n");
+ for(i=0;i<16;++i)
+ printf(" %s: %u\n",HASH_TESTS[i],(unsigned int)AnodeDictionary__get_bucket(HASH_TESTS[i]));
+
+ for(cs=0;cs<2;++cs) {
+ printf("\nTesting with case sensitivity = %d\n",cs);
+ AnodeDictionary_init(&d,cs);
+
+ printf("\nTesting dictionary by adding and retrieving some keys...\n");
+ AnodeDictionary_put(&d,"test1","This is the first test");
+ AnodeDictionary_put(&d,"test2","This is the second test");
+ AnodeDictionary_put(&d,"test3","This is the third test (lower case)");
+ AnodeDictionary_put(&d,"TEST3","This is the third test (UPPER CASE)");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ if (d.size != (cs ? 4 : 3)) {
+ printf("Failed (size).\n");
+ return 1;
+ }
+
+ AnodeDictionary_clear(&d);
+ if (d.size||(AnodeDictionary_get(&d,"test1"))) {
+ printf("Failed (clear).\n");
+ return 1;
+ }
+
+ printf("\nTesting read, trial 1: simple key=value with unterminated line\n");
+ strcpy(tmp,"foo=bar\nbar=baz\ntest1=Happy happy joyjoy!\ntest2=foobarbaz\nlinewithnocr=thisworked");
+ AnodeDictionary_read(&d,tmp,"\r\n","=","",'\\',0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nTesting read, trial 2: key=value with escape chars, escaped CRs\n");
+ strcpy(tmp,"foo=bar\r\nbar==baz\nte\\=st1=\\=Happy happy joyjoy!\ntest2=foobarbaz\\\nfoobarbaz on next line\r\n");
+ AnodeDictionary_read(&d,tmp,"\r\n","=","",'\\',0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nTesting read, trial 3: HTTP header-like dictionary\n");
+ strcpy(tmp,"Host: some.host.net\r\nX-Some-Header: foo bar\r\nX-Some-Other-Header: y0y0y0y0y0\r\n");
+ AnodeDictionary_read(&d,tmp,"\r\n",": ","",0,0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nTesting read, trial 4: single line key/value\n");
+ strcpy(tmp,"Header: one line only");
+ AnodeDictionary_read(&d,tmp,"\r\n",": ","",0,0,0);
+ printf("Results:\n");
+ AnodeDictionary_iterate(&d,(void *)0,&diterate);
+ AnodeDictionary_clear(&d);
+
+ printf("\nFuzzing dictionary reader...\n"); fflush(stdout);
+ for(i=0;i<200000;++i) {
+ j = random() % (sizeof(tmp) - 1);
+ for(k=0;k<j;++k) {
+ tmp[k] = (char)((unsigned int)random() >> 3);
+ if (!tmp[k]) tmp[k] = 1;
+ }
+ tmp[j] = (char)0;
+
+ j = random() % (sizeof(fuzzparam1) - 1);
+ for(k=0;k<j;++k) {
+ fuzzparam1[k] = (char)((unsigned int)random() >> 3);
+ if (!fuzzparam1[k]) fuzzparam1[k] = 1;
+ }
+ fuzzparam1[j] = (char)0;
+
+ j = random() % (sizeof(fuzzparam2) - 1);
+ for(k=0;k<j;++k) {
+ fuzzparam1[k] = (char)((unsigned int)random() >> 3);
+ if (!fuzzparam2[k]) fuzzparam2[k] = 1;
+ }
+ fuzzparam2[j] = (char)0;
+
+ j = random() % (sizeof(fuzzparam3) - 1);
+ for(k=0;k<j;++k) {
+ fuzzparam3[k] = (char)((unsigned int)random() >> 3);
+ if (!fuzzparam3[k]) fuzzparam3[k] = 1;
+ }
+ fuzzparam3[j] = (char)0;
+
+ AnodeDictionary_read(&d,tmp,fuzzparam1,fuzzparam2,fuzzparam3,random() & 3,random() & 1,random() & 1);
+ AnodeDictionary_clear(&d);
+ }
+
+ AnodeDictionary_destroy(&d);
+ }
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/ec-test.c b/attic/historic/anode/libanode/tests/ec-test.c
new file mode 100644
index 00000000..49f04265
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/ec-test.c
@@ -0,0 +1,97 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../impl/ec.h"
+#include "../impl/misc.h"
+
+#define TEST_KEY_LEN 128
+#define AnodeEC_key_to_hex(k,b,l) Anode_to_hex((k)->key,(k)->bytes,(b),l)
+
+int main(int argc,char **argv)
+{
+ struct AnodeECKeyPair pair1;
+ struct AnodeECKeyPair pair2;
+ struct AnodeECKeyPair pair3;
+ unsigned char key[TEST_KEY_LEN];
+ char str[16384];
+
+ printf("Creating key pair #1...\n");
+ if (!AnodeECKeyPair_generate(&pair1)) {
+ printf("Could not create key pair.\n");
+ return 1;
+ }
+ AnodeEC_key_to_hex(&pair1.pub,str,sizeof(str));
+ printf("Public: %s\n",str);
+ AnodeEC_key_to_hex(&pair1.priv,str,sizeof(str));
+ printf("Private: %s\n\n",str);
+
+ printf("Creating key pair #2...\n");
+ if (!AnodeECKeyPair_generate(&pair2)) {
+ printf("Could not create key pair.\n");
+ return 1;
+ }
+ AnodeEC_key_to_hex(&pair2.pub,str,sizeof(str));
+ printf("Public: %s\n",str);
+ AnodeEC_key_to_hex(&pair2.priv,str,sizeof(str));
+ printf("Private: %s\n\n",str);
+
+ printf("Key agreement between public #2 and private #1...\n");
+ if (!AnodeECKeyPair_agree(&pair1,&pair2.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret: %s\n\n",str);
+
+ printf("Key agreement between public #1 and private #2...\n");
+ if (!AnodeECKeyPair_agree(&pair2,&pair1.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret: %s\n\n",str);
+
+ printf("Testing key pair init function (init #3 from #2's parts)...\n");
+ if (!AnodeECKeyPair_init(&pair3,&(pair2.pub),&(pair2.priv))) {
+ printf("Init failed.\n");
+ return 1;
+ }
+
+ printf("Key agreement between public #1 and private #3...\n");
+ if (!AnodeECKeyPair_agree(&pair3,&pair1.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret: %s\n\n",str);
+
+ printf("Key agreement between public #1 and private #1...\n");
+ if (!AnodeECKeyPair_agree(&pair1,&pair1.pub,key,TEST_KEY_LEN)) {
+ printf("Agreement failed.\n");
+ return 1;
+ }
+ Anode_to_hex(key,TEST_KEY_LEN,str,sizeof(str));
+ printf("Agreed secret (should not match): %s\n\n",str);
+
+ AnodeECKeyPair_destroy(&pair1);
+ AnodeECKeyPair_destroy(&pair2);
+ AnodeECKeyPair_destroy(&pair3);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/environment-test.c b/attic/historic/anode/libanode/tests/environment-test.c
new file mode 100644
index 00000000..c481a129
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/environment-test.c
@@ -0,0 +1,28 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "../environment.h"
+
+int main(int argc,char **argv)
+{
+ const char *cache = Anode_get_cache();
+
+ printf("Cache folder: %s\n",cache ? cache : "(null)");
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/http_client-test.c b/attic/historic/anode/libanode/tests/http_client-test.c
new file mode 100644
index 00000000..e1f93967
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/http_client-test.c
@@ -0,0 +1,233 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <openssl/sha.h>
+#include "../anode.h"
+#include "../misc.h"
+#include "../http_client.h"
+#include "../dictionary.h"
+
+struct TestCase
+{
+ int method;
+ AnodeURI uri;
+ const void *client_data;
+ unsigned int client_data_len;
+ const char *expected_sha1;
+ char actual_sha1[64];
+ int got_it;
+ int keepalive;
+ struct TestCase *next;
+};
+
+#define NUM_TEST_CASES 7
+static struct TestCase test_cases[NUM_TEST_CASES];
+
+static void init_test_cases(int keepalive)
+{
+ AnodeURI_parse(&(test_cases[0].uri),"http://zerotier.com/for_unit_tests/test1.txt");
+ test_cases[0].method = ANODE_HTTP_GET;
+ test_cases[0].client_data_len = 0;
+ test_cases[0].expected_sha1 = "0828324174b10cc867b7255a84a8155cf89e1b8b";
+ test_cases[0].actual_sha1[0] = (char)0;
+ test_cases[0].got_it = 0;
+ test_cases[0].keepalive = keepalive;
+ test_cases[0].next = &(test_cases[1]);
+
+ AnodeURI_parse(&(test_cases[1].uri),"http://zerotier.com/for_unit_tests/test2.bin");
+ test_cases[1].method = ANODE_HTTP_GET;
+ test_cases[1].client_data_len = 0;
+ test_cases[1].expected_sha1 = "6b67c635786ab52666211d02412c0d0f0372980d";
+ test_cases[1].actual_sha1[0] = (char)0;
+ test_cases[1].got_it = 0;
+ test_cases[1].keepalive = keepalive;
+ test_cases[1].next = &(test_cases[2]);
+
+ AnodeURI_parse(&(test_cases[2].uri),"http://zerotier.com/for_unit_tests/test3.bin");
+ test_cases[2].method = ANODE_HTTP_GET;
+ test_cases[2].client_data_len = 0;
+ test_cases[2].expected_sha1 = "efa7722029fdbb6abd0e3ed32a0b44bfb982cff0";
+ test_cases[2].actual_sha1[0] = (char)0;
+ test_cases[2].got_it = 0;
+ test_cases[2].keepalive = keepalive;
+ test_cases[2].next = &(test_cases[3]);
+
+ AnodeURI_parse(&(test_cases[3].uri),"http://zerotier.com/for_unit_tests/test4.bin");
+ test_cases[3].method = ANODE_HTTP_GET;
+ test_cases[3].client_data_len = 0;
+ test_cases[3].expected_sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
+ test_cases[3].actual_sha1[0] = (char)0;
+ test_cases[3].got_it = 0;
+ test_cases[3].keepalive = keepalive;
+ test_cases[3].next = &(test_cases[4]);
+
+ AnodeURI_parse(&(test_cases[4].uri),"http://zerotier.com/for_unit_tests/echo.php?echo=foobar");
+ test_cases[4].method = ANODE_HTTP_GET;
+ test_cases[4].client_data_len = 0;
+ test_cases[4].expected_sha1 = "8843d7f92416211de9ebb963ff4ce28125932878";
+ test_cases[4].actual_sha1[0] = (char)0;
+ test_cases[4].got_it = 0;
+ test_cases[4].keepalive = keepalive;
+ test_cases[4].next = &(test_cases[5]);
+
+ AnodeURI_parse(&(test_cases[5].uri),"http://zerotier.com/for_unit_tests/echo.php");
+ test_cases[5].method = ANODE_HTTP_POST;
+ test_cases[5].client_data = "echo=foobar";
+ test_cases[5].client_data_len = strlen((char *)test_cases[5].client_data);
+ test_cases[5].expected_sha1 = "8843d7f92416211de9ebb963ff4ce28125932878";
+ test_cases[5].actual_sha1[0] = (char)0;
+ test_cases[5].got_it = 0;
+ test_cases[5].keepalive = keepalive;
+ test_cases[5].next = &(test_cases[6]);
+
+ AnodeURI_parse(&(test_cases[6].uri),"http://zerotier.com/for_unit_tests/test3.bin");
+ test_cases[6].method = ANODE_HTTP_HEAD;
+ test_cases[6].client_data_len = 0;
+ test_cases[6].expected_sha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
+ test_cases[6].actual_sha1[0] = (char)0;
+ test_cases[6].got_it = 0;
+ test_cases[6].keepalive = keepalive;
+ test_cases[6].next = 0;
+}
+
+static int http_handler_dump_headers(void *arg,const char *key,const char *value)
+{
+ printf(" H %s: %s\n",key,value);
+ return 1;
+}
+
+static void http_handler(struct AnodeHttpClient *client)
+{
+ const char *method = "???";
+ char buf[1024];
+ unsigned char sha[20];
+ struct TestCase *test = (struct TestCase *)client->ptr[0];
+
+ switch(client->method) {
+ case ANODE_HTTP_GET:
+ method = "GET";
+ break;
+ case ANODE_HTTP_HEAD:
+ method = "HEAD";
+ break;
+ case ANODE_HTTP_POST:
+ method = "POST";
+ break;
+ }
+
+ if (client->response.code == 200) {
+ SHA1((unsigned char *)client->response.data,client->response.data_length,sha);
+ Anode_to_hex(sha,20,test->actual_sha1,sizeof(test->actual_sha1));
+ printf("%s %s\n * SHA1: %s exp: %s\n",method,AnodeURI_to_string(&(test->uri),buf,sizeof(buf)),test->actual_sha1,test->expected_sha1);
+ if (strcmp(test->actual_sha1,test->expected_sha1))
+ printf(" ! SHA1 MISMATCH!\n");
+ AnodeDictionary_iterate(&(client->response.headers),0,&http_handler_dump_headers);
+ } else printf("%s %s: ERROR: %d\n",method,AnodeURI_to_string(&(test->uri),buf,sizeof(buf)),client->response.code);
+
+ test->got_it = 1;
+
+ if (!test->keepalive)
+ AnodeHttpClient_free(client);
+ else {
+ test = test->next;
+ if (test) {
+ memcpy((void *)&(client->uri),(const void *)&(test->uri),sizeof(AnodeURI));
+
+ client->data = test->client_data;
+ client->data_length = test->client_data_len;
+ client->ptr[0] = test;
+ client->keepalive = test->keepalive;
+ client->method = test->method;
+ client->handler = &http_handler;
+
+ AnodeHttpClient_send(client);
+ } else {
+ AnodeHttpClient_free(client);
+ }
+ }
+}
+
+int main(int argc,char **argv)
+{
+ struct AnodeHttpClient *client;
+ AnodeTransportEngine transport_engine;
+ int i;
+
+ if (Anode_init_ip_transport_engine(&transport_engine)) {
+ printf("Failed (transport engine init)\n");
+ return 1;
+ }
+
+ printf("Testing without keepalive...\n\n");
+ init_test_cases(0);
+ for(i=0;i<NUM_TEST_CASES;++i) {
+ client = AnodeHttpClient_new(&transport_engine);
+
+ memcpy((void *)&(client->uri),(const void *)&(test_cases[i].uri),sizeof(AnodeURI));
+ client->data = test_cases[i].client_data;
+ client->data_length = test_cases[i].client_data_len;
+ client->ptr[0] = &test_cases[i];
+ client->keepalive = test_cases[i].keepalive;
+ client->method = test_cases[i].method;
+ client->handler = &http_handler;
+
+ AnodeHttpClient_send(client);
+ }
+
+ for(;;) {
+ for(i=0;i<NUM_TEST_CASES;++i) {
+ if (!test_cases[i].got_it)
+ break;
+ }
+ if (i == NUM_TEST_CASES)
+ break;
+ transport_engine.poll(&transport_engine);
+ }
+ printf("\n\n");
+
+ printf("Testing with keepalive...\n\n");
+ init_test_cases(1);
+
+ client = AnodeHttpClient_new(&transport_engine);
+
+ i = 0;
+ memcpy((void *)&(client->uri),(const void *)&(test_cases[i].uri),sizeof(AnodeURI));
+ client->data = test_cases[i].client_data;
+ client->data_length = test_cases[i].client_data_len;
+ client->ptr[0] = &test_cases[i];
+ client->keepalive = test_cases[i].keepalive;
+ client->method = test_cases[i].method;
+ client->handler = &http_handler;
+
+ AnodeHttpClient_send(client);
+
+ for(;;) {
+ for(i=0;i<NUM_TEST_CASES;++i) {
+ if (!test_cases[i].got_it)
+ break;
+ }
+ if (i == NUM_TEST_CASES)
+ break;
+ transport_engine.poll(&transport_engine);
+ }
+
+ transport_engine.destroy(&transport_engine);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/misc-test.c b/attic/historic/anode/libanode/tests/misc-test.c
new file mode 100644
index 00000000..e5b9085f
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/misc-test.c
@@ -0,0 +1,137 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include "../misc.h"
+
+int main(int argc,char **argv)
+{
+ const char *base32TestStr = "asdf";
+ char *fields[16];
+ char buf[1024];
+ char buf2[1024];
+ char buf3[4096];
+ unsigned int i;
+ unsigned long tmpl,tmpl2;
+ unsigned long long tmp64;
+
+ srand(time(0));
+
+ Anode_base32_5_to_8((const unsigned char *)base32TestStr,buf);
+ printf("Base32 from test string: %s\n",buf);
+ Anode_base32_8_to_5("MFZWIZQA",(unsigned char *)buf2);
+ printf("Test string from Base32 (upper case): %s\n",buf2);
+ Anode_base32_8_to_5("mfzwizqa",(unsigned char *)buf2);
+ printf("Test string from Base32 (lower case): %s\n",buf2);
+ printf("Testing variable length encoding/decoded with pad5 functions...\n");
+ for(i=0;i<1024;++i) {
+ tmpl = rand() % (sizeof(buf) - 8);
+ if (!tmpl)
+ tmpl = 1;
+ for(tmpl2=0;tmpl2<tmpl;++tmpl2)
+ buf[tmpl2] = (buf2[tmpl2] = (char)(rand() >> 3));
+ if (!Anode_base32_encode_pad5(buf2,tmpl,buf3,sizeof(buf3))) {
+ printf("Failed (encode failed).\n");
+ return 1;
+ }
+ memset(buf2,0,sizeof(buf2));
+ if (!Anode_base32_decode_pad5(buf3,buf2,sizeof(buf2))) {
+ printf("Failed (decode failed).\n");
+ return 1;
+ }
+ if (memcmp(buf,buf2,tmpl)) {
+ printf("Failed (compare failed).\n");
+ return 1;
+ }
+ }
+
+ printf("Anode_htonll(0x0102030405060708) == 0x%.16llx\n",tmp64 = Anode_htonll(0x0102030405060708ULL));
+ printf("Anode_ntohll(0x%.16llx) == 0x%.16llx\n",tmp64,Anode_ntohll(tmp64));
+ if (Anode_ntohll(tmp64) != 0x0102030405060708ULL) {
+ printf("Failed.\n");
+ return 1;
+ }
+
+ strcpy(buf,"foo bar baz");
+ Anode_trim(buf);
+ printf("Testing string trim: 'foo bar baz' -> '%s'\n",buf);
+ strcpy(buf,"foo bar baz ");
+ Anode_trim(buf);
+ printf("Testing string trim: 'foo bar baz ' -> '%s'\n",buf);
+ strcpy(buf," foo bar baz");
+ Anode_trim(buf);
+ printf("Testing string trim: ' foo bar baz' -> '%s'\n",buf);
+ strcpy(buf," foo bar baz ");
+ Anode_trim(buf);
+ printf("Testing string trim: ' foo bar baz ' -> '%s'\n",buf);
+ strcpy(buf,"");
+ Anode_trim(buf);
+ printf("Testing string trim: '' -> '%s'\n",buf);
+ strcpy(buf," ");
+ Anode_trim(buf);
+ printf("Testing string trim: ' ' -> '%s'\n",buf);
+
+ printf("Testing string split.\n");
+ strcpy(buf,"66.246.138.121,5323,0");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 1) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s\n",fields[0]);
+ strcpy(buf,"a;b;c");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,";;");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,"a;b;");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,"a;;c");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 3) {
+ printf("Failed.\n");
+ return 1;
+ } else printf("Fields: %s %s %s\n",fields[0],fields[1],fields[2]);
+ strcpy(buf,";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 16) {
+ printf("Failed.\n");
+ return 1;
+ }
+ strcpy(buf,"");
+ i = Anode_split(buf,';',fields,16);
+ if (i != 0) {
+ printf("Failed.\n");
+ return 1;
+ }
+ printf("Passed.\n");
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/tests/system_transport-test.c b/attic/historic/anode/libanode/tests/system_transport-test.c
new file mode 100644
index 00000000..bda575ed
--- /dev/null
+++ b/attic/historic/anode/libanode/tests/system_transport-test.c
@@ -0,0 +1,70 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include "../anode.h"
+#include "../impl/thread.h"
+
+static int do_client()
+{
+ AnodeTransport *st;
+ AnodeSocket *udp_sock;
+ int run = 1;
+
+ st = AnodeSystemTransport_new(NULL);
+ if (!st) {
+ printf("FAILED: unable to construct AnodeSystemTransport.\n");
+ return -1;
+ }
+ printf("Created AnodeSystemTransport.\n");
+
+ while (run)
+ st->poll(st);
+}
+
+static int do_server()
+{
+ AnodeTransport *st;
+ AnodeSocket *udp_sock;
+ AnodeSocket *tcp_sock;
+ int run = 1;
+
+ st = AnodeSystemTransport_new(NULL);
+ if (!st) {
+ printf("FAILED: unable to construct AnodeSystemTransport.\n");
+ return -1;
+ }
+ printf("Created AnodeSystemTransport.\n");
+
+ while (run)
+ st->poll(st);
+}
+
+int main(int argc,char **argv)
+{
+ if (argc == 2) {
+ if (!strcmp(argv[1],"client"))
+ return do_client();
+ else if (!strcmp(argv[1],"server"))
+ return do_server();
+ }
+
+ printf("Usage: system_transport-test <client / server>\n");
+ return -1;
+}
diff --git a/attic/historic/anode/libanode/uri.c b/attic/historic/anode/libanode/uri.c
new file mode 100644
index 00000000..ca644b6a
--- /dev/null
+++ b/attic/historic/anode/libanode/uri.c
@@ -0,0 +1,185 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "impl/misc.h"
+#include "anode.h"
+
+int AnodeURI_parse(AnodeURI *parsed_uri,const char *uri_string)
+{
+ char buf[sizeof(AnodeURI)];
+ unsigned long ptr = 0;
+ char c;
+ char *p1,*p2;
+
+ Anode_zero((void *)parsed_uri,sizeof(AnodeURI));
+
+ /* Get the scheme */
+ for(;;) {
+ c = *(uri_string++);
+ if (!c) {
+ parsed_uri->scheme[ptr] = (char)0;
+ return ANODE_ERR_INVALID_URI;
+ } else if (c == ':') {
+ parsed_uri->scheme[ptr] = (char)0;
+ break;
+ } else {
+ parsed_uri->scheme[ptr++] = c;
+ if (ptr == sizeof(parsed_uri->scheme))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+ }
+
+ if (*uri_string == '/') {
+ /* If it starts with /, it's a URL */
+
+ /* Skip double slash */
+ if (!(*(++uri_string)))
+ return 0; /* Scheme with no path */
+ if (*uri_string == '/') {
+ if (!(*(++uri_string)))
+ return 0; /* Scheme with no path */
+ }
+
+ /* Get the host section and put it in buf[] */
+ ptr = 0;
+ while ((*uri_string)&&(*uri_string != '/')) {
+ buf[ptr++] = *(uri_string++);
+ if (ptr == sizeof(buf))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+ buf[ptr] = (char)0;
+
+ /* Parse host section for host, username, password, and port */
+ if (buf[0]) {
+ p1 = (char *)Anode_strchr(buf,'@');
+ if (p1) {
+ *(p1++) = (char)0;
+ if (*p1) {
+ p2 = (char *)Anode_strchr(buf,':');
+ if (p2) {
+ *(p2++) = (char)0;
+ Anode_str_copy(parsed_uri->password,p2,sizeof(parsed_uri->password));
+ }
+ Anode_str_copy(parsed_uri->username,buf,sizeof(parsed_uri->username));
+ } else return ANODE_ERR_INVALID_URI;
+ } else p1 = buf;
+
+ p2 = (char *)Anode_strchr(p1,':');
+ if (p2) {
+ *(p2++) = (char)0;
+ if (*p2)
+ parsed_uri->port = (int)strtoul(p2,(char **)0,10);
+ }
+ Anode_str_copy(parsed_uri->host,p1,sizeof(parsed_uri->host));
+ }
+
+ /* Get the path, query, and fragment section and put it in buf[] */
+ ptr = 0;
+ while ((buf[ptr++] = *(uri_string++))) {
+ if (ptr == sizeof(buf))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+
+ /* Parse path section for path, query, and fragment */
+ if (buf[0]) {
+ p1 = (char *)Anode_strchr(buf,'?');
+ if (p1) {
+ *(p1++) = (char)0;
+ p2 = (char *)Anode_strchr(p1,'#');
+ if (p2) {
+ *(p2++) = (char)0;
+ Anode_str_copy(parsed_uri->fragment,p2,sizeof(parsed_uri->fragment));
+ }
+ Anode_str_copy(parsed_uri->query,p1,sizeof(parsed_uri->query));
+ } else {
+ p2 = (char *)Anode_strchr(buf,'#');
+ if (p2) {
+ *(p2++) = (char)0;
+ Anode_str_copy(parsed_uri->fragment,p2,sizeof(parsed_uri->fragment));
+ }
+ }
+ Anode_str_copy(parsed_uri->path,buf,sizeof(parsed_uri->path));
+ }
+ } else {
+ /* Otherwise, it's a URN and what remains is all path */
+ ptr = 0;
+ while ((parsed_uri->path[ptr++] = *(uri_string++))) {
+ if (ptr == sizeof(parsed_uri->path))
+ return ANODE_ERR_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return 0;
+}
+
+char *AnodeURI_to_string(const AnodeURI *uri,char *buf,int len)
+{
+ int i = 0;
+ char portbuf[16];
+ const char *p;
+
+ p = uri->scheme;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+
+ buf[i++] = ':'; if (i >= len) return (char *)0;
+
+ if (uri->host[0]) {
+ buf[i++] = '/'; if (i >= len) return (char *)0;
+ buf[i++] = '/'; if (i >= len) return (char *)0;
+
+ if (uri->username[0]) {
+ p = uri->username;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ if (uri->password[0]) {
+ buf[i++] = ':'; if (i >= len) return (char *)0;
+ p = uri->password;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+ buf[i++] = '@'; if (i >= len) return (char *)0;
+ }
+
+ p = uri->host;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+
+ if ((uri->port > 0)&&(uri->port <= 0xffff)) {
+ buf[i++] = ':'; if (i >= len) return (char *)0;
+ snprintf(portbuf,sizeof(portbuf),"%d",uri->port);
+ p = portbuf;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+ }
+
+ p = uri->path;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+
+ if (uri->query[0]) {
+ buf[i++] = '?'; if (i >= len) return (char *)0;
+ p = uri->query;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+
+ if (uri->fragment[0]) {
+ buf[i++] = '#'; if (i >= len) return (char *)0;
+ p = uri->fragment;
+ while (*p) { buf[i++] = *(p++); if (i >= len) return (char *)0; }
+ }
+
+ buf[i] = (char)0;
+
+ return buf;
+}
diff --git a/attic/historic/anode/libanode/utils/anode-make-identity.c b/attic/historic/anode/libanode/utils/anode-make-identity.c
new file mode 100644
index 00000000..99a3897a
--- /dev/null
+++ b/attic/historic/anode/libanode/utils/anode-make-identity.c
@@ -0,0 +1,50 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "../anode.h"
+#include "../impl/misc.h"
+#include "../impl/types.h"
+
+int main(int argc,char **argv)
+{
+ char str[1024];
+ AnodeZone zone;
+ AnodeIdentity identity;
+
+ if (argc < 2) {
+ printf("Usage: anode-make-identity <32-bit zone ID hex>\n");
+ return 0;
+ }
+
+ *((uint32_t *)zone.bits) = htonl((uint32_t)strtoul(argv[1],(char **)0,16));
+
+ if (AnodeIdentity_generate(&identity,&zone,ANODE_ADDRESS_ANODE_256_40)) {
+ fprintf(stderr,"Error: identity key pair generation failed (check build settings).\n");
+ return 1;
+ }
+ if (AnodeIdentity_to_string(&identity,str,sizeof(str)) <= 0) {
+ fprintf(stderr,"Error: internal error converting identity to string.\n");
+ return -1;
+ }
+
+ printf("%s\n",str);
+
+ return 0;
+}
diff --git a/attic/historic/anode/libanode/zone.c b/attic/historic/anode/libanode/zone.c
new file mode 100644
index 00000000..a6e397ae
--- /dev/null
+++ b/attic/historic/anode/libanode/zone.c
@@ -0,0 +1,184 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009-2010 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "impl/types.h"
+#include "impl/misc.h"
+#include "impl/dictionary.h"
+#include "impl/environment.h"
+#include "impl/http_client.h"
+#include "anode.h"
+
+static const char *_MONTHS[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
+static const char *_DAYS_OF_WEEK[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
+static inline unsigned long get_file_time_for_http(const char *path,char *buf,unsigned int len)
+{
+ struct stat st;
+ struct tm *gmt;
+
+ if (!stat(path,(struct stat *)&st)) {
+ gmt = gmtime(&st.st_mtime);
+ if (gmt) {
+ snprintf(buf,len,"%s, %d %s %d %d:%d:%d GMT",
+ _DAYS_OF_WEEK[gmt->tm_wday],
+ gmt->tm_mday,
+ _MONTHS[gmt->tm_mon],
+ (1900 + gmt->tm_year),
+ gmt->tm_hour,
+ gmt->tm_min,
+ gmt->tm_sec);
+ buf[len - 1] = (char)0;
+ return (unsigned long)st.st_size;
+ }
+ }
+
+ return 0;
+}
+
+struct AnodeZoneLookupJob
+{
+ char cached_zone_file[2048];
+ struct AnodeDictionary *zone_dict;
+ AnodeZone zone;
+ void *ptr;
+ void (*zone_lookup_handler)(void *,const AnodeZone *,AnodeZoneFile *);
+ int had_cached_zone;
+};
+
+static void AnodeZone_lookup_http_handler(struct AnodeHttpClient *client)
+{
+ char *data_tmp;
+ struct AnodeZoneLookupJob *job = (struct AnodeZoneLookupJob *)client->ptr[0];
+ FILE *zf;
+
+ if ((client->response.code == 200)&&(client->response.data_length > 0)) {
+ zf = fopen(job->cached_zone_file,"w");
+ if (zf) {
+ fwrite(client->response.data,1,client->response.data_length,zf);
+ fclose(zf);
+ }
+
+ data_tmp = (char *)malloc(client->response.data_length + 1);
+ Anode_memcpy((void *)data_tmp,client->response.data,client->response.data_length);
+ data_tmp[client->response.data_length] = (char)0;
+
+ AnodeDictionary_clear(job->zone_dict);
+ AnodeDictionary_read(
+ job->zone_dict,
+ data_tmp,
+ "\r\n",
+ "=",
+ ";",
+ '\\',
+ 1,1);
+
+ free((void *)data_tmp);
+
+ job->zone_lookup_handler(job->ptr,&job->zone,(AnodeZoneFile *)job->zone_dict);
+ } else if (job->had_cached_zone)
+ job->zone_lookup_handler(job->ptr,&job->zone,(AnodeZoneFile *)job->zone_dict);
+ else {
+ AnodeDictionary_destroy(job->zone_dict);
+ free((void *)job->zone_dict);
+ job->zone_lookup_handler(job->ptr,&job->zone,(AnodeZoneFile *)0);
+ }
+
+ free((void *)job);
+ AnodeHttpClient_free(client);
+}
+
+void AnodeZone_lookup(
+ AnodeTransportEngine *transport,
+ const AnodeZone *zone,
+ void *ptr,
+ void (*zone_lookup_handler)(void *,const AnodeZone *,AnodeZone *))
+{
+ char cached_zones_folder[2048];
+ char cached_zone_file[2048];
+ char if_modified_since[256];
+ unsigned long file_size;
+ struct AnodeZoneLookupJob *job;
+ struct AnodeHttpClient *client;
+ char *file_data;
+ FILE *zf;
+
+ if (Anode_get_cache_sub("zones",cached_zones_folder,sizeof(cached_zones_folder))) {
+ snprintf(cached_zone_file,sizeof(cached_zone_file),"%s%c%.2x%.2x%.2x%.2x.z",cached_zones_folder,ANODE_PATH_SEPARATOR,(unsigned int)zone->bits[0],(unsigned int)zone->bits[1],(unsigned int)zone->bits[2],(unsigned int)zone->bits[3]);
+ cached_zone_file[sizeof(cached_zone_file)-1] = (char)0;
+
+ job = (struct AnodeZoneLookupJob *)malloc(sizeof(struct AnodeZoneLookupJob));
+ Anode_str_copy(job->cached_zone_file,cached_zone_file,sizeof(job->cached_zone_file));
+ job->zone_dict = (struct AnodeDictionary *)malloc(sizeof(struct AnodeDictionary));
+ AnodeDictionary_init(job->zone_dict,0);
+ job->zone.bits[0] = zone->bits[0];
+ job->zone.bits[1] = zone->bits[1];
+ job->zone.bits[2] = zone->bits[2];
+ job->zone.bits[3] = zone->bits[3];
+ job->ptr = ptr;
+ job->zone_lookup_handler = zone_lookup_handler;
+ job->had_cached_zone = 0;
+
+ client = AnodeHttpClient_new(transport);
+
+ Anode_str_copy(client->uri.scheme,"http",sizeof(client->uri.scheme));
+ snprintf(client->uri.host,sizeof(client->uri.host),"a--%.2x%.2x%.2x%.2x.net",(unsigned int)zone->bits[0],(unsigned int)zone->bits[1],(unsigned int)zone->bits[2],(unsigned int)zone->bits[3]);
+ client->uri.host[sizeof(client->uri.host)-1] = (char)0;
+ Anode_str_copy(client->uri.path,"/z",sizeof(client->uri.path));
+
+ client->handler = &AnodeZone_lookup_http_handler;
+ client->ptr[0] = job;
+
+ if ((file_size = get_file_time_for_http(cached_zone_file,if_modified_since,sizeof(if_modified_since)))) {
+ zf = fopen(cached_zone_file,"r");
+ if (zf) {
+ AnodeDictionary_put(&client->headers,"If-Modified-Since",if_modified_since);
+ file_data = (char *)malloc(file_size + 1);
+ if (fread((void *)file_data,1,file_size,zf)) {
+ file_data[file_size] = (char)0;
+ AnodeDictionary_read(
+ job->zone_dict,
+ file_data,
+ "\r\n",
+ "=",
+ ";",
+ '\\',
+ 1,1);
+ job->had_cached_zone = 1;
+ }
+ free((void *)file_data);
+ fclose(zf);
+ }
+ }
+
+ AnodeHttpClient_send(client);
+ } else zone_lookup_handler(ptr,zone,(AnodeZone *)0);
+}
+
+const char *AnodeZoneFile_get(AnodeZoneFile *zone,const char *key)
+{
+ return AnodeDictionary_get((struct AnodeDictionary *)zone,key);
+}
+
+void AnodeZoneFile_free(AnodeZoneFile *zone)
+{
+ AnodeDictionary_destroy((struct AnodeDictionary *)zone);
+ free((void *)zone);
+}
diff --git a/attic/historic/anode/libspark/Makefile b/attic/historic/anode/libspark/Makefile
new file mode 100644
index 00000000..0d3fedd8
--- /dev/null
+++ b/attic/historic/anode/libspark/Makefile
@@ -0,0 +1,16 @@
+SYSNAME:=${shell uname}
+SYSNAME!=uname
+include ../config.mk.${SYSNAME}
+
+LIBSPARK_OBJS=
+
+all: libspark
+
+libspark: $(LIBSPARK_OBJS)
+ ar rcs libspark.a $(LIBSPARK_OBJS)
+ ranlib libspark.a
+
+clean: force
+ rm -f *.a *.so *.dylib *.dll *.lib *.exe *.o
+
+force: ;
diff --git a/attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp b/attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp
new file mode 100644
index 00000000..9b1ecaa1
--- /dev/null
+++ b/attic/historic/anode/libspark/experiments/FindGoodSegmentDelimiters.cpp
@@ -0,0 +1,161 @@
+// Searches for good delimiters to cut streams into relatively well sized
+// segments.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <boost/cstdint.hpp>
+#include <boost/array.hpp>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <iostream>
+#include <vector>
+#include <map>
+
+// Desired size range
+#define MIN_DESIRED_SIZE 4096
+#define MAX_DESIRED_SIZE 131072
+
+#define DELIMITER_SET_SIZE 1
+typedef boost::array<boost::uint16_t,DELIMITER_SET_SIZE> DelimArray;
+
+struct BestEntry
+{
+ DelimArray best;
+ double bestScore;
+ std::vector<unsigned char> data;
+};
+
+boost::mutex bestLock;
+boost::mutex outLock;
+std::map<std::string,BestEntry> best;
+
+static void runThread(const std::string &fileName)
+{
+ char tmp[4096];
+
+ boost::mt19937 prng;
+ {
+ boost::uint32_t seed;
+ FILE *ur = fopen("/dev/urandom","r");
+ fread((void *)&seed,1,sizeof(seed),ur);
+ fclose(ur);
+ prng.seed(seed);
+ }
+
+ BestEntry *myEntry;
+ {
+ boost::mutex::scoped_lock l(bestLock);
+ myEntry = &(best[fileName]);
+ myEntry->bestScore = 99999999.0;
+ }
+
+ {
+ boost::mutex::scoped_lock l(outLock);
+
+ std::cout << "*** Reading test data from: " << fileName << std::endl;
+ FILE *f = fopen(fileName.c_str(),"r");
+ if (f) {
+ int n;
+ while ((n = fread((void *)tmp,1,sizeof(tmp),f)) > 0) {
+ for(int i=0;i<n;++i)
+ myEntry->data.push_back((unsigned char)tmp[i]);
+ }
+ fclose(f);
+ }
+
+ if (myEntry->data.size() <= 0) {
+ std::cout << "Error: no data read." << std::endl;
+ exit(1);
+ } else std::cout << "*** Read " << myEntry->data.size() << " bytes of test data." << std::endl;
+
+ std::cout.flush();
+ }
+
+ DelimArray current;
+ for(unsigned int i=0;i<DELIMITER_SET_SIZE;++i)
+ current[i] = (boost::uint16_t)prng();
+
+ for(;;) {
+ unsigned long numTooShort = 0;
+ unsigned long numTooLong = 0;
+ unsigned long numGood = 0;
+
+ boost::uint32_t shiftRegister = 0;
+ unsigned long segSize = 0;
+ for(std::vector<unsigned char>::iterator i=myEntry->data.begin();i!=myEntry->data.end();++i) {
+ shiftRegister <<= 1;
+ shiftRegister |= (((boost::uint32_t)*i) & 1);
+
+ ++segSize;
+
+ boost::uint16_t transformedShiftRegister = (boost::uint16_t)(shiftRegister);
+
+ for(DelimArray::iterator d=current.begin();d!=current.end();++d) {
+ if (transformedShiftRegister == *d) {
+ if (segSize < MIN_DESIRED_SIZE)
+ ++numTooShort;
+ else if (segSize > MAX_DESIRED_SIZE)
+ ++numTooLong;
+ else ++numGood;
+ segSize = 0;
+ break;
+ }
+ }
+ }
+ if (segSize) {
+ if (segSize < MIN_DESIRED_SIZE)
+ ++numTooShort;
+ else if (segSize > MAX_DESIRED_SIZE)
+ ++numTooLong;
+ else ++numGood;
+ }
+
+ if (numGood) {
+ double score = ((double)(numTooShort + numTooLong)) / ((double)numGood);
+
+ if (score < myEntry->bestScore) {
+ myEntry->best = current;
+ myEntry->bestScore = score;
+
+ boost::mutex::scoped_lock l(outLock);
+
+ std::cout << fileName << ": ";
+
+ for(DelimArray::iterator d=current.begin();d!=current.end();++d) {
+ sprintf(tmp,"0x%.4x",(unsigned int)*d);
+ if (d != current.begin())
+ std::cout << ',';
+ std::cout << tmp;
+ }
+
+ std::cout << ": " << numTooShort << " / " << numGood << " / " << numTooLong << " (" << score << ")" << std::endl;
+ std::cout.flush();
+
+ if ((numTooShort == 0)&&(numTooLong == 0))
+ break;
+ }
+ }
+
+ for(DelimArray::iterator i=current.begin();i!=current.end();++i)
+ *i = (boost::uint16_t)prng();
+ }
+}
+
+int main(int argc,char **argv)
+{
+ std::vector< boost::shared_ptr<boost::thread> > threads;
+
+ for(int i=1;i<argc;++i) {
+ boost::shared_ptr<boost::thread> t(new boost::thread(boost::bind(&runThread,std::string(argv[i]))));
+ threads.push_back(t);
+ }
+
+ for(std::vector< boost::shared_ptr<boost::thread> >::iterator i=threads.begin();i!=threads.end();++i)
+ (*i)->join();
+
+ return 0;
+}
diff --git a/attic/historic/anode/libspark/experiments/Makefile b/attic/historic/anode/libspark/experiments/Makefile
new file mode 100644
index 00000000..83aa4f67
--- /dev/null
+++ b/attic/historic/anode/libspark/experiments/Makefile
@@ -0,0 +1,5 @@
+all:
+ g++ -O6 -ftree-vectorize -o FindGoodSegmentDelimiters FindGoodSegmentDelimiters.cpp -lboost_thread -lpthread
+
+clean:
+ rm FindGoodSegmentDelimiters
diff --git a/attic/historic/anode/libspark/streamencoder.h b/attic/historic/anode/libspark/streamencoder.h
new file mode 100644
index 00000000..b487ca40
--- /dev/null
+++ b/attic/historic/anode/libspark/streamencoder.h
@@ -0,0 +1,108 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _SPARK_STREAMENCODER_H
+#define _SPARK_STREAMENCODER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ unsigned char *input_buf;
+ unsigned long input_buf_capacity;
+ unsigned long input_length;
+
+ unsigned char *stream_out_buf;
+ unsigned long stream_out_buf_capacity;
+ unsigned long stream_out_length;
+
+ void (*data_segment_add_func)(const void *data,unsigned long len,const void *global_hash,unsigned long global_hash_len);
+} SparkStreamEncoder;
+
+/**
+ * Initialize a spark stream encoder
+ *
+ * @param enc Encoder structure to initialize
+ * @param data_segment_add_func Function to call to store or cache data
+ */
+void SparkStreamEncoder_init(
+ SparkStreamEncoder *enc,
+ void (*data_segment_add_func)(
+ const void *data,
+ unsigned long len,
+ const void *global_hash,
+ unsigned long global_hash_len));
+
+/**
+ * Clean up a spark stream encoder structure
+ *
+ * @param enc Structure to clear
+ */
+void SparkStreamEncoder_destroy(SparkStreamEncoder *enc);
+
+/**
+ * Add data to encode
+ *
+ * @param enc Encoder structure
+ * @param data Data to encode
+ * @param len Length of data in bytes
+ * @return Number of bytes of result stream now available
+ */
+unsigned long SparkStreamEncoder_put(
+ SparkStreamEncoder *enc,
+ const void *data,
+ unsigned long len);
+
+/**
+ * Flush all data currently in input buffer
+ *
+ * @param enc Encoder structure to flush
+ */
+void SparkStreamEncoder_flush(SparkStreamEncoder *enc);
+
+/**
+ * @return Number of bytes of output stream available
+ */
+static inline unsigned long SparkStreamEncoder_available(SparkStreamEncoder *enc)
+{
+ return enc->stream_out_length;
+}
+
+/**
+ * @return Pointer to result stream bytes (may return null if none available)
+ */
+static inline const void *SparkStreamEncoder_get(SparkStreamEncoder *enc)
+{
+ return (const void *)(enc->stream_out_buf);
+}
+
+/**
+ * @return "Consume" result stream bytes after they're read or sent
+ */
+static inline void SparkStreamEncoder_consume(SparkStreamEncoder *enc,unsigned long len)
+{
+ unsigned long i;
+ for(i=len;i<enc->stream_out_length;++i)
+ enc->stream_out_buf[i - len] = enc->stream_out_buf[i];
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/historic/anode/libspark/wrapper.h b/attic/historic/anode/libspark/wrapper.h
new file mode 100644
index 00000000..eb8c593d
--- /dev/null
+++ b/attic/historic/anode/libspark/wrapper.h
@@ -0,0 +1,66 @@
+/* libanode: the Anode C reference implementation
+ * Copyright (C) 2009 Adam Ierymenko <adam.ierymenko@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef _SPARK_WRAPPER_H
+#define _SPARK_WRAPPER_H
+
+#include <openssl/sha.h>
+#include "../libanode/aes128.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Spark uses SHA-256 with hash length 32 */
+#define SPARK_HASH_LENGTH 32
+
+// Wrap a segment for forward propagation
+static inline void Spark_wrap(void *data,unsigned long len,void *plaintext_hash_buf,void *global_hash_buf)
+{
+ unsigned char expkey[ANODE_AES128_EXP_KEY_SIZE];
+
+ SHA256((const unsigned char *)data,len,(unsigned char *)plaintext_hash_buf);
+
+ Anode_aes128_expand_key(expkey,(const unsigned char *)plaintext_hash_buf);
+ Anode_aes128_cfb_encrypt(expkey,((const unsigned char *)plaintext_hash_buf) + 16,(unsigned char *)data,len);
+
+ SHA256((const unsigned char *)data,len,(unsigned char *)global_hash_buf);
+}
+
+// Unwrap a segment and check its integrity
+static inline int Spark_unwrap(void *data,unsigned long len,const void *plaintext_hash)
+{
+ unsigned char expkey[ANODE_AES128_EXP_KEY_SIZE];
+ unsigned char check_hash[32];
+ unsigned long i;
+
+ Anode_aes128_expand_key(expkey,(const unsigned char *)plaintext_hash);
+ Anode_aes128_cfb_decrypt(expkey,((const unsigned char *)plaintext_hash) + 16,(unsigned char *)data,len);
+
+ SHA256((const unsigned char *)data,len,check_hash);
+
+ for(i=0;i<32;++i) {
+ if (check_hash[i] != ((const unsigned char *)plaintext_hash)[i])
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/attic/kubernetes_docs/.zerotierCliSettings b/attic/kubernetes_docs/.zerotierCliSettings
new file mode 100644
index 00000000..0e7df9b6
--- /dev/null
+++ b/attic/kubernetes_docs/.zerotierCliSettings
@@ -0,0 +1,18 @@
+{
+ "configVersion": 1,
+ "defaultCentral": "@my.zerotier.com",
+ "defaultController": "@my.zerotier.com",
+ "defaultOne": "@local",
+ "things": {
+ "local": {
+ "auth": "local_service_auth_token_replaced_automatically",
+ "type": "one",
+ "url": "http://127.0.0.1:9993/"
+ },
+ "my.zerotier.com": {
+ "auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "type": "central",
+ "url": "https://my.zerotier.com/"
+ }
+ }
+}
diff --git a/attic/kubernetes_docs/Dockerfile b/attic/kubernetes_docs/Dockerfile
new file mode 100644
index 00000000..6437a2bb
--- /dev/null
+++ b/attic/kubernetes_docs/Dockerfile
@@ -0,0 +1,19 @@
+FROM node:4.4
+EXPOSE 8080/tcp 9993/udp
+
+# Install ZT network conf files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD zerotier-one /
+ADD zerotier-cli /
+ADD .zerotierCliSettings /
+
+# Install App
+ADD server.js /
+
+# script which will start/auth VM on ZT network
+ADD entrypoint.sh /
+RUN chmod -v +x /entrypoint.sh
+
+CMD ["./entrypoint.sh"] \ No newline at end of file
diff --git a/attic/kubernetes_docs/README.md b/attic/kubernetes_docs/README.md
new file mode 100644
index 00000000..482e77e5
--- /dev/null
+++ b/attic/kubernetes_docs/README.md
@@ -0,0 +1,150 @@
+Kubernetes + ZeroTier
+====
+
+A self-authorizing Kubernetes cluster deployment over a private ZeroTier network.
+
+This is a quick tutorial for setting up a Kubernetes deployment which can self-authorize each new replica onto your private ZeroTier network with no additional configuration needed when you scale. The Kubernetes-specific instructions and content is based on the [hellonode](http://kubernetes.io/docs/hellonode/) tutorial. All of the files discussed below can be found [here]();
+
+
+
+## Preliminary tasks
+
+**Step 1: Go to [my.zerotier.com](https://my.zerotier.com) and generate a network controller API key. This key will be used by ZeroTier to automatically authorize new instances of your VMs to join your secure deployment network during replication.**
+
+**Step 2: Create a new `private` network. Take note of the network ID, henceforth: `nwid`**
+
+**Step 3: Follow the instructions from the [hellonode](ttp://kubernetes.io/docs/hellonode/) tutorial to set up your development system.**
+
+***
+## Construct docker image
+
+**Step 4: Create necessary files for inclusion into image, your resultant directory should contain:**
+
+ - `ztkube/<nwid>.conf`
+ - `ztkube/Dockerfile`
+ - `ztkube/entrypoint.sh`
+ - `ztkube/server.js`
+ - `ztkube/zerotier-cli`
+ - `ztkube/zerotier-one`
+
+Start by creating a build directory to copy all required files into `mkdir ztkube`. Then build the following:
+ - `make one`
+ - `make cli`
+
+Add the following files to the `ztkube` directory. These files will be compiled into the Docker image.
+
+ - Create an empty `<nwid>.conf` file to specify the private deployment network you created in *Step 2*:
+
+ - Create a CLI tool config file `.zerotierCliSettings` which should only contain your network controller API key to authorize new devices on your network (the local service API key will be filled in automatically). In this example the default controller is hosted by us at [my.zerotier.com](https://my.zerotier.com). Alternatively, you can host your own network controller but you'll need to modify the CLI config file accordingly.
+
+```
+{
+ "configVersion": 1,
+ "defaultCentral": "@my.zerotier.com",
+ "defaultController": "@my.zerotier.com",
+ "defaultOne": "@local",
+ "things": {
+ "local": {
+ "auth": "local_service_auth_token_replaced_automatically",
+ "type": "one",
+ "url": "http://127.0.0.1:9993/"
+ },
+ "my.zerotier.com": {
+ "auth": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+ "type": "central",
+ "url": "https://my.zerotier.com/"
+ }
+ }
+}
+```
+
+
+ - Create a `Dockerfile` which will copy the ZeroTier service as well as the ZeroTier CLI to the image:
+
+```
+FROM node:4.4
+EXPOSE 8080/tcp 9993/udp
+
+# Install ZT network conf files
+RUN mkdir -p /var/lib/zerotier-one/networks.d
+ADD *.conf /var/lib/zerotier-one/networks.d/
+ADD *.conf /
+ADD zerotier-one /
+ADD zerotier-cli /
+ADD .zerotierCliSettings /
+
+# Install App
+ADD server.js /
+
+# script which will start/auth VM on ZT network
+ADD entrypoint.sh /
+RUN chmod -v +x /entrypoint.sh
+
+CMD ["./entrypoint.sh"]
+```
+
+ - Create the `entrypoint.sh` script which will start the ZeroTier service in the VM, attempt to join your deployment network and automatically authorize the new VM if your network is set to private:
+
+```
+#!/bin/bash
+
+echo '*** ZeroTier-Kubernetes self-auth test script'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+dev=""
+nwconf=$(ls *.conf)
+nwid="${nwconf%.*}"
+
+sleep 10
+dev=$(cat /var/lib/zerotier-one/identity.public| cut -d ':' -f 1)
+
+echo '*** Joining'
+./zerotier-cli join "$nwid".conf
+# Fill out local service auth token
+AUTHTOKEN=$(cat /var/lib/zerotier-one/authtoken.secret)
+sed "s|\local_service_auth_token_replaced_automatically|${AUTHTOKEN}|" .zerotierCliSettings > /root/.zerotierCliSettings
+echo '*** Authorizing'
+./zerotier-cli net-auth @my.zerotier.com "$nwid" "$dev"
+echo '*** Cleaning up' # Remove controller auth token
+rm -rf .zerotierCliSettings /root/.zerotierCliSettings
+node server.js
+```
+
+**Step 5: Build the image:**
+
+ - `docker build -t gcr.io/$PROJECT_ID/hello-node .`
+
+
+
+**Step 6: Push the docker image to your *Container Registry***
+
+ - `gcloud docker push gcr.io/$PROJECT_ID/hello-node:v1`
+
+***
+## Deploy!
+
+**Step 7: Create Kubernetes Cluster**
+
+ - `gcloud config set compute/zone us-central1-a`
+
+ - `gcloud container clusters create hello-world`
+
+ - `gcloud container clusters get-credentials hello-world`
+
+
+
+**Step 8: Create your pod**
+
+ - `kubectl run hello-node --image=gcr.io/$PROJECT_ID/hello-node:v1 --port=8080`
+
+
+
+**Step 9: Scale**
+
+ - `kubectl scale deployment hello-node --replicas=4`
+
+***
+## Verify
+
+Now, after a minute or so you can use `zerotier-cli net-members <nwid>` to show all of your VM instances on your ZeroTier deployment network. If you haven't [configured your local CLI](https://github.com/zerotier/ZeroTierOne/tree/dev/cli), you can simply log into [my.zerotier.com](https://my.zerotier.com), go to *Networks -> nwid* to check that your VMs are indeed members of your private network. You should also note that the `entrypoint.sh` script will automatically delete your network controller API key once it has authorized your VM. This is merely a security measure and can be removed if needed.
diff --git a/attic/kubernetes_docs/entrypoint.sh b/attic/kubernetes_docs/entrypoint.sh
new file mode 100644
index 00000000..80cd278e
--- /dev/null
+++ b/attic/kubernetes_docs/entrypoint.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+echo '*** ZeroTier-Kubernetes self-auth test script'
+chown -R daemon /var/lib/zerotier-one
+chgrp -R daemon /var/lib/zerotier-one
+su daemon -s /bin/bash -c '/zerotier-one -d -U -p9993 >>/tmp/zerotier-one.out 2>&1'
+dev=""
+nwconf=$(ls *.conf)
+nwid="${nwconf%.*}"
+
+sleep 10
+dev=$(cat /var/lib/zerotier-one/identity.public| cut -d ':' -f 1)
+
+echo '*** Joining'
+./zerotier-cli join "$nwid".conf
+# Fill out local service auth token
+AUTHTOKEN=$(cat /var/lib/zerotier-one/authtoken.secret)
+sed "s|\local_service_auth_token_replaced_automatically|${AUTHTOKEN}|" .zerotierCliSettings > /root/.zerotierCliSettings
+echo '*** Authorizing'
+./zerotier-cli net-auth @my.zerotier.com "$nwid" "$dev"
+echo '*** Cleaning up' # Remove controller auth token
+rm -rf .zerotierCliSettings /root/.zerotierCliSettings
+node server.js \ No newline at end of file
diff --git a/attic/kubernetes_docs/server.js b/attic/kubernetes_docs/server.js
new file mode 100644
index 00000000..a4b08bb8
--- /dev/null
+++ b/attic/kubernetes_docs/server.js
@@ -0,0 +1,8 @@
+var http = require('http');
+var handleRequest = function(request, response) {
+ console.log('Received request for URL: ' + request.url);
+ response.writeHead(200);
+ response.end('Hello World!');
+};
+var www = http.createServer(handleRequest);
+www.listen(8080);
diff --git a/attic/lat_lon_to_xyz.js b/attic/lat_lon_to_xyz.js
new file mode 100644
index 00000000..692a3687
--- /dev/null
+++ b/attic/lat_lon_to_xyz.js
@@ -0,0 +1,25 @@
+'use strict'
+
+/* This is a utility to convert latitude/longitude into X,Y,Z coordinates as used by clustering. */
+
+if (process.argv.length !== 4) {
+ console.log('Usage: node lat_lon_to_xyz.js <latitude> <longitude');
+ process.exit(1);
+}
+
+var lat = parseFloat(process.argv[2])||0.0;
+var lon = parseFloat(process.argv[3])||0.0;
+
+var latRadians = lat * 0.01745329251994; // PI / 180
+var lonRadians = lon * 0.01745329251994; // PI / 180
+var cosLat = Math.cos(latRadians);
+
+console.log({
+ lat: lat,
+ lon: lon,
+ x: Math.round((-6371.0) * cosLat * Math.cos(lonRadians)),
+ y: Math.round(6371.0 * Math.sin(latRadians)),
+ z: Math.round(6371.0 * cosLat * Math.sin(lonRadians))
+});
+
+process.exit(0);
diff --git a/attic/old-linux-installer/buildinstaller.sh b/attic/old-linux-installer/buildinstaller.sh
deleted file mode 100755
index 21f2f73e..00000000
--- a/attic/old-linux-installer/buildinstaller.sh
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/bin/bash
-
-# This script builds the installer for *nix systems. Windows must do everything
-# completely differently, as usual.
-
-export PATH=/bin:/usr/bin:/sbin:/usr/sbin
-
-if [ ! -f zerotier-one ]; then
- echo "Could not find 'zerotier-one' binary, please build before running this script."
- exit 2
-fi
-
-machine=`uname -m`
-system=`uname -s`
-
-vmajor=`cat version.h | grep -F ZEROTIER_ONE_VERSION_MAJOR | cut -d ' ' -f 3`
-vminor=`cat version.h | grep -F ZEROTIER_ONE_VERSION_MINOR | cut -d ' ' -f 3`
-revision=`cat version.h | grep -F ZEROTIER_ONE_VERSION_REVISION | cut -d ' ' -f 3`
-
-if [ -z "$vmajor" -o -z "$vminor" -o -z "$revision" ]; then
- echo "Unable to extract version info from version.h, aborting installer build."
- exit 2
-fi
-
-rm -rf build-installer
-mkdir build-installer
-
-case "$system" in
-
- Linux)
- # Canonicalize $machine for some architectures... we use x86
- # and x64 for Intel stuff. ARM and others should be fine if
- # we ever ship officially for those.
- debian_arch=$machine
- case "$machine" in
- i386|i486|i586|i686)
- machine="x86"
- debian_arch="i386"
- ;;
- x86_64|amd64|x64)
- machine="x64"
- debian_arch="amd64"
- ;;
- armv6l|arm|armhf|arm7l|armv7l)
- machine="armv6l"
- debian_arch="armhf"
- ;;
- esac
-
- echo "Assembling Linux installer for $machine and version $vmajor.$vminor.$revision"
-
- mkdir -p 'build-installer/var/lib/zerotier-one/ui'
- cp -fp 'ext/installfiles/linux/uninstall.sh' 'build-installer/var/lib/zerotier-one'
- cp -fp 'zerotier-one' 'build-installer/var/lib/zerotier-one'
- for f in ui/*.html ui/*.js ui/*.css ui/*.jsx ; do
- cp -fp "$f" 'build-installer/var/lib/zerotier-one/ui'
- done
- mkdir -p 'build-installer/tmp'
- cp -fp 'ext/installfiles/linux/init.d/zerotier-one' 'build-installer/tmp/init.d_zerotier-one'
- cp -fp 'ext/installfiles/linux/systemd/zerotier-one.service' 'build-installer/tmp/systemd_zerotier-one.service'
-
- targ="ZeroTierOneInstaller-linux-${machine}-${vmajor}_${vminor}_${revision}"
- # Use gzip in Linux since some minimal Linux systems do not have bunzip2
- rm -f build-installer-tmp.tar.gz
- cd build-installer
- tar -cf - * | gzip -9 >../build-installer-tmp.tar.gz
- cd ..
- rm -f $targ
- cat ext/installfiles/linux/install.tmpl.sh build-installer-tmp.tar.gz >$targ
- chmod 0755 $targ
- rm -f build-installer-tmp.tar.gz
- ls -l $targ
-
- if [ -f /usr/bin/dpkg-deb -a "$UID" -eq 0 ]; then
- echo
- echo Found dpkg-deb and you are root, trying to build Debian package.
-
- rm -rf build-installer-deb
-
- debbase="build-installer-deb/zerotier-one_${vmajor}.${vminor}.${revision}_$debian_arch"
- debfolder="${debbase}/DEBIAN"
- mkdir -p $debfolder
-
- cat 'ext/installfiles/linux/DEBIAN/control.in' | sed "s/__VERSION__/${vmajor}.${vminor}.${revision}/" | sed "s/__ARCH__/${debian_arch}/" >$debfolder/control
- cat $debfolder/control
- cp -f 'ext/installfiles/linux/DEBIAN/conffiles' "${debfolder}/conffiles"
-
- mkdir -p "${debbase}/var/lib/zerotier-one/updates.d"
- cp -f $targ "${debbase}/var/lib/zerotier-one/updates.d"
-
- rm -f "${debfolder}/postinst" "${debfolder}/prerm"
-
- echo '#!/bin/bash' >${debfolder}/postinst
- echo "/var/lib/zerotier-one/updates.d/${targ} >>/dev/null 2>&1" >>${debfolder}/postinst
- echo "/bin/rm -f /var/lib/zerotier-one/updates.d/*" >>${debfolder}/postinst
- chmod a+x ${debfolder}/postinst
-
- echo '#!/bin/bash' >${debfolder}/prerm
- echo 'export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin' >>${debfolder}/prerm
- echo 'if [ "$1" != "upgrade" ]; then' >>${debfolder}/prerm
- echo ' /var/lib/zerotier-one/uninstall.sh >>/dev/null 2>&1' >>${debfolder}/prerm
- echo 'fi' >>${debfolder}/prerm
- chmod a+x ${debfolder}/prerm
-
- dpkg-deb --build $debbase
-
- mv -f build-installer-deb/*.deb .
- rm -rf build-installer-deb
- fi
-
- if [ -f /usr/bin/rpmbuild ]; then
- echo
- echo Found rpmbuild, trying to build RedHat/CentOS package.
-
- rm -f /tmp/zerotier-one.spec
- curr_dir=`pwd`
- cat ext/installfiles/linux/RPM/zerotier-one.spec.in | sed "s/__VERSION__/${vmajor}.${vminor}.${revision}/g" | sed "s/__INSTALLER__/${targ}/g" >/tmp/zerotier-one.spec
-
- rpmbuild -ba /tmp/zerotier-one.spec
-
- rm -f /tmp/zerotier-one.spec
- fi
-
- ;;
-
- *)
- echo "Unsupported platform: $system"
- exit 2
-
-esac
-
-rm -rf build-installer
-
-exit 0
diff --git a/attic/old-linux-installer/install.tmpl.sh b/attic/old-linux-installer/install.tmpl.sh
deleted file mode 100644
index 2d18d24c..00000000
--- a/attic/old-linux-installer/install.tmpl.sh
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
-shopt -s expand_aliases
-
-dryRun=0
-
-echo "*** ZeroTier One install/update ***"
-echo
-
-if [ "$UID" -ne 0 ]; then
- echo "Not running as root so doing dry run (no modifications to system)..."
- dryRun=1
-fi
-
-if [ $dryRun -gt 0 ]; then
- alias ln="echo '>> ln'"
- alias rm="echo '>> rm'"
- alias mv="echo '>> mv'"
- alias cp="echo '>> cp'"
- alias chown="echo '>> chown'"
- alias chgrp="echo '>> chgrp'"
- alias chmod="echo '>> chmod'"
- alias chkconfig="echo '>> chkconfig'"
- alias zerotier-cli="echo '>> zerotier-cli'"
- alias service="echo '>> service'"
- alias systemctl="echo '>> systemctl'"
-fi
-
-scriptPath="`dirname "$0"`/`basename "$0"`"
-if [ ! -r "$scriptPath" ]; then
- scriptPath="$0"
- if [ ! -r "$scriptPath" ]; then
- echo "Installer cannot determine its own path; $scriptPath is not readable."
- exit 2
- fi
-fi
-
-# Check for systemd vs. old school SysV init
-SYSTEMDUNITDIR=
-if [ -e /bin/systemctl -o -e /usr/bin/systemctl -o -e /usr/local/bin/systemctl -o -e /sbin/systemctl -o -e /usr/sbin/systemctl ]; then
- # Second check: test if systemd appears to actually be running. Apparently Ubuntu
- # thought it was a good idea to ship with systemd installed but not used. Issue #133
- if [ -d /var/run/systemd/system -o -d /run/systemd/system ]; then
- if [ -e /usr/bin/pkg-config ]; then
- SYSTEMDUNITDIR=`/usr/bin/pkg-config systemd --variable=systemdsystemunitdir`
- fi
- if [ -z "$SYSTEMDUNITDIR" -o ! -d "$SYSTEMDUNITDIR" ]; then
- if [ -d /usr/lib/systemd/system ]; then
- SYSTEMDUNITDIR=/usr/lib/systemd/system
- fi
- if [ -d /etc/systemd/system ]; then
- SYSTEMDUNITDIR=/etc/systemd/system
- fi
- fi
- fi
-fi
-
-# Find the end of this script, which is where we have appended binary data.
-endMarkerIndex=`grep -a -b -E '^################' "$scriptPath" | head -c 16 | cut -d : -f 1`
-if [ "$endMarkerIndex" -le 100 ]; then
- echo 'Internal error: unable to find end of script / start of binary data marker.'
- exit 2
-fi
-blobStart=`expr $endMarkerIndex + 17`
-if [ "$blobStart" -le "$endMarkerIndex" ]; then
- echo 'Internal error: unable to find end of script / start of binary data marker.'
- exit 2
-fi
-
-echo -n 'Getting version of existing install... '
-origVersion=NONE
-if [ -x /var/lib/zerotier-one/zerotier-one ]; then
- origVersion=`/var/lib/zerotier-one/zerotier-one -v`
-fi
-echo $origVersion
-
-echo 'Extracting files...'
-if [ $dryRun -gt 0 ]; then
- echo ">> tail -c +$blobStart \"$scriptPath\" | gunzip -c | tar -xvop -C / -f -"
- tail -c +$blobStart "$scriptPath" | gunzip -c | tar -t -f - | sed 's/^/>> /'
-else
- tail -c +$blobStart "$scriptPath" | gunzip -c | tar -xvop --no-overwrite-dir -C / -f -
-fi
-
-if [ $dryRun -eq 0 -a ! -x "/var/lib/zerotier-one/zerotier-one" ]; then
- echo 'Archive extraction failed, cannot find zerotier-one binary in "/var/lib/zerotier-one".'
- exit 2
-fi
-
-echo -n 'Getting version of new install... '
-newVersion=`/var/lib/zerotier-one/zerotier-one -v`
-echo $newVersion
-
-echo 'Creating symlinks...'
-
-rm -f /usr/bin/zerotier-cli /usr/bin/zerotier-idtool
-ln -sf /var/lib/zerotier-one/zerotier-one /usr/bin/zerotier-cli
-ln -sf /var/lib/zerotier-one/zerotier-one /usr/bin/zerotier-idtool
-
-echo 'Installing zerotier-one service...'
-
-if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" ]; then
- # SYSTEMD
-
- # If this was updated or upgraded from an init.d based system, clean up the old
- # init.d stuff before installing directly via systemd.
- if [ -f /etc/init.d/zerotier-one ]; then
- if [ -e /sbin/chkconfig -o -e /usr/sbin/chkconfig -o -e /bin/chkconfig -o -e /usr/bin/chkconfig ]; then
- chkconfig zerotier-one off
- fi
- rm -f /etc/init.d/zerotier-one
- fi
-
- cp -f /tmp/systemd_zerotier-one.service "$SYSTEMDUNITDIR/zerotier-one.service"
- chown 0 "$SYSTEMDUNITDIR/zerotier-one.service"
- chgrp 0 "$SYSTEMDUNITDIR/zerotier-one.service"
- chmod 0644 "$SYSTEMDUNITDIR/zerotier-one.service"
- rm -f /tmp/systemd_zerotier-one.service /tmp/init.d_zerotier-one
-
- systemctl enable zerotier-one.service
-
- echo
- echo 'Done! Installed and service configured to start at system boot.'
- echo
- echo "To start now or restart the service if it's already running:"
- echo ' sudo systemctl restart zerotier-one.service'
-else
- # SYSV INIT -- also covers upstart which supports SysVinit backward compatibility
-
- cp -f /tmp/init.d_zerotier-one /etc/init.d/zerotier-one
- chmod 0755 /etc/init.d/zerotier-one
- rm -f /tmp/systemd_zerotier-one.service /tmp/init.d_zerotier-one
-
- if [ -f /sbin/chkconfig -o -f /usr/sbin/chkconfig -o -f /usr/bin/chkconfig -o -f /bin/chkconfig ]; then
- chkconfig zerotier-one on
- else
- # Yes Virginia, some systems lack chkconfig.
- if [ -d /etc/rc0.d ]; then
- rm -f /etc/rc0.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc0.d/K89zerotier-one
- fi
- if [ -d /etc/rc1.d ]; then
- rm -f /etc/rc1.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc1.d/K89zerotier-one
- fi
- if [ -d /etc/rc2.d ]; then
- rm -f /etc/rc2.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc2.d/S11zerotier-one
- fi
- if [ -d /etc/rc3.d ]; then
- rm -f /etc/rc3.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc3.d/S11zerotier-one
- fi
- if [ -d /etc/rc4.d ]; then
- rm -f /etc/rc4.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc4.d/S11zerotier-one
- fi
- if [ -d /etc/rc5.d ]; then
- rm -f /etc/rc5.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc5.d/S11zerotier-one
- fi
- if [ -d /etc/rc6.d ]; then
- rm -f /etc/rc6.d/???zerotier-one
- ln -sf /etc/init.d/zerotier-one /etc/rc6.d/K89zerotier-one
- fi
- fi
-
- echo
- echo 'Done! Installed and service configured to start at system boot.'
- echo
- echo "To start now or restart the service if it's already running:"
- echo ' sudo service zerotier-one restart'
-fi
-
-exit 0
-
-# Do not remove the last line or add a carriage return to it! The installer
-# looks for an unterminated line beginning with 16 #'s in itself to find
-# the binary blob data, which is appended after it.
-
-################ \ No newline at end of file
diff --git a/attic/old-linux-installer/uninstall.sh b/attic/old-linux-installer/uninstall.sh
deleted file mode 100755
index d9495a18..00000000
--- a/attic/old-linux-installer/uninstall.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
-
-if [ "$UID" -ne 0 ]; then
- echo "Must be run as root; try: sudo $0"
- exit 1
-fi
-
-# Detect systemd vs. regular init
-SYSTEMDUNITDIR=
-if [ -e /bin/systemctl -o -e /usr/bin/systemctl -o -e /usr/local/bin/systemctl -o -e /sbin/systemctl -o -e /usr/sbin/systemctl ]; then
- if [ -e /usr/bin/pkg-config ]; then
- SYSTEMDUNITDIR=`/usr/bin/pkg-config systemd --variable=systemdsystemunitdir`
- fi
- if [ -z "$SYSTEMDUNITDIR" -o ! -d "$SYSTEMDUNITDIR" ]; then
- if [ -d /usr/lib/systemd/system ]; then
- SYSTEMDUNITDIR=/usr/lib/systemd/system
- fi
- if [ -d /etc/systemd/system ]; then
- SYSTEMDUNITDIR=/etc/systemd/system
- fi
- fi
-fi
-
-echo "Killing any running zerotier-one service..."
-if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" ]; then
- systemctl stop zerotier-one.service
- systemctl disable zerotier-one.service
-else
- if [ -f /sbin/service -o -f /usr/sbin/service -o -f /bin/service -o -f /usr/bin/service ]; then
- service zerotier-one stop
- fi
-fi
-
-sleep 1
-if [ -f /var/lib/zerotier-one/zerotier-one.pid ]; then
- kill -TERM `cat /var/lib/zerotier-one/zerotier-one.pid`
- sleep 1
-fi
-if [ -f /var/lib/zerotier-one/zerotier-one.pid ]; then
- kill -KILL `cat /var/lib/zerotier-one/zerotier-one.pid`
-fi
-
-if [ -f /etc/init.d/zerotier-one ]; then
- echo "Removing SysV init items..."
- if [ -f /sbin/chkconfig -o -f /usr/sbin/chkconfig -o -f /bin/chkconfig -o -f /usr/bin/chkconfig ]; then
- chkconfig zerotier-one off
- fi
- rm -f /etc/init.d/zerotier-one
- find /etc/rc*.d -type f -name '???zerotier-one' -print0 | xargs -0 rm -f
-fi
-
-if [ -n "$SYSTEMDUNITDIR" -a -d "$SYSTEMDUNITDIR" -a -f "$SYSTEMDUNITDIR/zerotier-one.service" ]; then
- echo "Removing systemd service..."
- rm -f "$SYSTEMDUNITDIR/zerotier-one.service"
-fi
-
-echo "Erasing binary and support files..."
-if [ -d /var/lib/zerotier-one ]; then
- cd /var/lib/zerotier-one
- rm -rf zerotier-one *.persist identity.public *.log *.pid *.sh updates.d networks.d iddb.d root-topology ui
-fi
-
-echo "Erasing anything installed into system bin directories..."
-rm -f /usr/local/bin/zerotier-cli /usr/bin/zerotier-cli /usr/local/bin/zerotier-idtool /usr/bin/zerotier-idtool
-
-echo "Done."
-echo
-echo "Your ZeroTier One identity is still preserved in /var/lib/zerotier-one"
-echo "as identity.secret and can be manually deleted if you wish. Save it if"
-echo "you wish to re-use the address of this node, as it cannot be regenerated."
-
-echo
-
-exit 0
diff --git a/world/README.md b/attic/world/README.md
index dda4920a..dda4920a 100644
--- a/world/README.md
+++ b/attic/world/README.md
diff --git a/world/build.sh b/attic/world/build.sh
index b783702c..b783702c 100755
--- a/world/build.sh
+++ b/attic/world/build.sh
diff --git a/world/earth-2016-01-13.bin b/attic/world/earth-2016-01-13.bin
index 5dea4d21..5dea4d21 100644
--- a/world/earth-2016-01-13.bin
+++ b/attic/world/earth-2016-01-13.bin
Binary files differ
diff --git a/world/mkworld.cpp b/attic/world/mkworld.cpp
index 061d6341..e0f477b3 100644
--- a/world/mkworld.cpp
+++ b/attic/world/mkworld.cpp
@@ -50,25 +50,6 @@
using namespace ZeroTier;
-class WorldMaker : public World
-{
-public:
- static inline World make(uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
- {
- WorldMaker w;
- w._id = id;
- w._ts = ts;
- w._updateSigningKey = sk;
- w._roots = roots;
-
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
- w.serialize(tmp,true);
- w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
-
- return w;
- }
-};
-
int main(int argc,char **argv)
{
std::string previous,current;
@@ -139,7 +120,7 @@ int main(int argc,char **argv)
fprintf(stderr,"INFO: generating and signing id==%llu ts==%llu"ZT_EOL_S,(unsigned long long)id,(unsigned long long)ts);
- World nw = WorldMaker::make(id,ts,currentKP.pub,roots,previousKP);
+ World nw = World::make(World::TYPE_PLANET,id,ts,currentKP.pub,roots,previousKP);
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> outtmp;
nw.serialize(outtmp,false);
diff --git a/world/old/earth-2015-11-16.bin b/attic/world/old/earth-2015-11-16.bin
index 910ff144..910ff144 100644
--- a/world/old/earth-2015-11-16.bin
+++ b/attic/world/old/earth-2015-11-16.bin
Binary files differ
diff --git a/world/old/earth-2015-11-20.bin b/attic/world/old/earth-2015-11-20.bin
index 198682e5..198682e5 100644
--- a/world/old/earth-2015-11-20.bin
+++ b/attic/world/old/earth-2015-11-20.bin
Binary files differ
diff --git a/world/old/earth-2015-12-17.bin b/attic/world/old/earth-2015-12-17.bin
index 20fadb56..20fadb56 100644
--- a/world/old/earth-2015-12-17.bin
+++ b/attic/world/old/earth-2015-12-17.bin
Binary files differ
diff --git a/cli/README.md b/cli/README.md
deleted file mode 100644
index dabbd302..00000000
--- a/cli/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-ZeroTier Newer-Spiffier Command Line Interface
-======
-
-This will be the future home of our new unified CLI for ZeroTier One, controllers, and Central (my.zerotier.com etc.).
-
-IT IS NOT DONE AND DOES NOT WORK EVEN A LITTLE BIT. GO AWAY.
diff --git a/cli/zerotier.cpp b/cli/zerotier.cpp
deleted file mode 100644
index f9eec5d0..00000000
--- a/cli/zerotier.cpp
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-// Note: unlike the rest of ZT's code base, this requires C++11 due to
-// the JSON library it uses and other things.
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "../node/Constants.hpp"
-#include "../version.h"
-#include "../osdep/OSUtils.hpp"
-#include "../ext/json/json.hpp"
-
-#ifdef __WINDOWS__
-#include <WinSock2.h>
-#include <windows.h>
-#include <tchar.h>
-#include <wchar.h>
-#else
-#include <ctype.h>
-#include <unistd.h>
-#endif
-
-#include <iostream>
-#include <string>
-#include <map>
-#include <vector>
-#include <tuple>
-
-#include <curl/curl.h>
-
-using json = nlohmann::json;
-using namespace ZeroTier;
-
-#define ZT_CLI_FLAG_VERBOSE 'v'
-#define ZT_CLI_FLAG_UNSAFE_SSL 'X'
-
-struct CLIState
-{
- std::string atname;
- std::string command;
- std::vector<std::string> args;
- std::map<char,std::string> opts;
- json settings;
-};
-
-namespace {
-
-static std::string trimString(const std::string &s)
-{
- unsigned long end = (unsigned long)s.length();
- while (end) {
- char c = s[end - 1];
- if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
- --end;
- else break;
- }
- unsigned long start = 0;
- while (start < end) {
- char c = s[start];
- if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
- ++start;
- else break;
- }
- return s.substr(start,end - start);
-}
-
-static inline std::string getSettingsFilePath()
-{
-#ifdef __WINDOWS__
-#else
- const char *home = getenv("HOME");
- if (!home)
- home = "/";
- return (std::string(home) + "/.zerotierCliSettings");
-#endif
-}
-
-static bool saveSettings(const json &settings)
-{
- std::string sfp(getSettingsFilePath().c_str());
- std::string buf(settings.dump(2));
- if (OSUtils::writeFile(sfp.c_str(),buf)) {
- OSUtils::lockDownFile(sfp.c_str(),false);
- return true;
- }
- return false;
-}
-
-static void dumpHelp()
-{
- std::cout << "ZeroTier Newer-Spiffier CLI " << ZEROTIER_ONE_VERSION_MAJOR << "." << ZEROTIER_ONE_VERSION_MINOR << "." << ZEROTIER_ONE_VERSION_REVISION << std::endl;
- std::cout << "(c)2016 ZeroTier, Inc. / Licensed under the GNU GPL v3" << std::endl;
- std::cout << std::endl;
- std::cout << "Configuration path: " << getSettingsFilePath() << std::endl;
- std::cout << std::endl;
- std::cout << "Usage: zerotier [-option] [@name] <command> [<command options>]" << std::endl;
- std::cout << std::endl;
- std::cout << "Options:" << std::endl;
- std::cout << " -v - Verbose JSON output" << std::endl;
- std::cout << " -X - Do not check SSL certs (CAUTION!)" << std::endl;
- std::cout << std::endl;
- std::cout << "CLI Configuration Commands:" << std::endl;
- std::cout << " cli-set <setting> <value> - Set a CLI option ('cli-set help')" << std::endl;
- std::cout << " cli-ls - List configured @things" << std::endl;
- std::cout << " cli-rm @name - Remove a configured @thing" << std::endl;
- std::cout << " cli-add-zt @name <url> <auth> - Add a ZeroTier service" << std::endl;
- std::cout << " cli-add-central @name <url> <auth> - Add ZeroTier Central instance" << std::endl;
- std::cout << std::endl;
- std::cout << "ZeroTier One Service Commands:" << std::endl;
- std::cout << " ls - List currently joined networks" << std::endl;
- std::cout << " join <network> [opt=value ...] - Join a network" << std::endl;
- std::cout << " leave <network> - Leave a network" << std::endl;
- std::cout << " peers - List ZeroTier VL1 peers" << std::endl;
- std::cout << " show [<network/peer address>] - Get info about self or object" << std::endl;
- std::cout << std::endl;
- std::cout << "Network Controller Commands:" << std::endl;
- std::cout << " net-create - Create a new network" << std::endl;
- std::cout << " net-rm <network> - Delete a network (CAUTION!)" << std::endl;
- std::cout << " net-ls - List administered networks" << std::endl;
- std::cout << " net-members <network> - List members of a network" << std::endl;
- std::cout << " net-show <network> [<address>] - Get network or member info" << std::endl;
- std::cout << " net-auth <network> <address> - Authorize a member" << std::endl;
- std::cout << " net-set <path> <value> - See 'net-set help'" << std::endl;
- std::cout << std::endl;
- std::cout << "Identity Commands:" << std::endl;
- std::cout << " id-generate [<vanity prefix>] - Generate a ZeroTier identity" << std::endl;
- std::cout << " id-validate <identity> - Locally validate an identity" << std::endl;
- std::cout << " id-sign <identity> <file> - Sign a file" << std::endl;
- std::cout << " id-verify <secret> <file> <sig> - Verify a file's signature" << std::endl;
- std::cout << " id-getpublic <secret> - Get full identity's public portion" << std::endl;
- std::cout << std::endl;
-}
-
-static size_t _curlStringAppendCallback(void *contents,size_t size,size_t nmemb,void *stdstring)
-{
- size_t totalSize = size * nmemb;
- reinterpret_cast<std::string *>(stdstring)->append((const char *)contents,totalSize);
- return totalSize;
-}
-
-static std::tuple<int,std::string> GET(const CLIState &state,const std::map<std::string,std::string> &headers,const std::string &url)
-{
- std::string body;
- char errbuf[CURL_ERROR_SIZE];
- char urlbuf[4096];
-
- CURL *curl = curl_easy_init();
- if (!curl) {
- std::cerr << "FATAL: curl_easy_init() failed" << std::endl;
- exit(-1);
- }
-
- curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curlStringAppendCallback);
- curl_easy_setopt(curl,CURLOPT_WRITEDATA,(void *)&body);
- curl_easy_setopt(curl,CURLOPT_USERAGENT,"ZeroTier-CLI");
- curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(state.opts.count(ZT_CLI_FLAG_UNSAFE_SSL) > 0) ? 0L : 1L);
- curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf);
- curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,0L);
-
- Utils::scopy(urlbuf,sizeof(urlbuf),url.c_str());
- curl_easy_setopt(curl,CURLOPT_URL,urlbuf);
-
- struct curl_slist *hdrs = (struct curl_slist *)0;
- for(std::map<std::string,std::string>::const_iterator i(headers.begin());i!=headers.end();++i) {
- std::string htmp(i->first);
- htmp.append(": ");
- htmp.append(i->second);
- hdrs = curl_slist_append(hdrs,htmp.c_str());
- }
- if (hdrs)
- curl_easy_setopt(curl,CURLOPT_HTTPHEADER,hdrs);
-
- memset(errbuf,0,sizeof(errbuf));
- CURLcode res = curl_easy_perform(curl);
- errbuf[CURL_ERROR_SIZE-1] = (char)0; // sanity check
-
- if (res != CURLE_OK)
- return std::make_tuple(-1,std::string(errbuf));
-
- int rc = (int)curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE);
-
- curl_easy_cleanup(curl);
- if (hdrs)
- curl_slist_free_all(hdrs);
-
- return std::make_tuple(rc,body);
-}
-
-} // anonymous namespace
-
-//////////////////////////////////////////////////////////////////////////////
-
-#ifdef __WINDOWS__
-int _tmain(int argc, _TCHAR* argv[])
-#else
-int main(int argc,char **argv)
-#endif
-{
-#ifdef __WINDOWS__
- {
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2,2),&wsaData);
- }
-#endif
-
- curl_global_init(CURL_GLOBAL_DEFAULT);
-
- CLIState state;
-
- for(int i=1;i<argc;++i) {
- if ((i == 1)&&(argv[i][0] == '@')) {
- state.atname = argv[i];
- } else if (state.command.length() == 0) {
- if (argv[i][0] == '-') {
- if (!argv[i][1]) {
- dumpHelp();
- return -1;
- } else if (argv[i][2]) {
- state.opts[argv[i][1]] = argv[i] + 2;
- } else {
- state.opts[argv[i][1]] = "";
- }
- } else {
- state.command = argv[i];
- }
- } else {
- state.args.push_back(std::string(argv[i]));
- }
- }
-
- {
- std::string buf;
- if (OSUtils::readFile(getSettingsFilePath().c_str(),buf))
- state.settings = json::parse(buf);
-
- if (state.settings.empty()) {
- // Default settings
- state.settings = {
- { "configVersion", 1 },
- { "things", {
- { "my.zerotier.com", {
- { "type", "central" },
- { "url", "https://my.zerotier.com/" },
- { "auth", "" }
- }},
- { "local", {
- { "type", "one" },
- { "url", "" },
- { "auth", "" }
- }}
- }},
- { "defaultController", "@my.zerotier.com" },
- { "defaultOne", "@local" }
- };
-
- std::string oneHome(OSUtils::platformDefaultHomePath());
- std::string authToken,portStr;
- bool initSuccess = false;
- if (OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken)&&OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr)) {
- portStr = trimString(portStr);
- authToken = trimString(authToken);
- int port = Utils::strToInt(portStr.c_str());
- if (((port > 0)&&(port < 65536))&&(authToken.length() > 0)) {
- state.settings["things"]["local"]["url"] = (std::string("http://127.0.0.1:") + portStr + "/");
- state.settings["things"]["local"]["auth"] = authToken;
- initSuccess = true;
- }
- }
-
- if (!saveSettings(state.settings)) {
- std::cerr << "FATAL: unable to write " << getSettingsFilePath() << std::endl;
- exit(-1);
- }
-
- if (initSuccess) {
- std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << std::endl;
- } else {
- std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << " but could not auto-init local ZeroTier One service config from " << oneHome << " -- you will need to set local service URL and port manually if you want to control a local instance of ZeroTier One. (This happens if you are not root/administrator.)" << std::endl;
- }
- }
- }
-
- if ((state.command.length() == 0)||(state.command == "help")) {
- dumpHelp();
- return -1;
- } else if (state.command == "cli-set") {
- } else if (state.command == "cli-ls") {
- } else if (state.command == "cli-rm") {
- } else if (state.command == "cli-add-zt") {
- } else if (state.command == "cli-add-central") {
- } else if (state.command == "ls") {
- } else if (state.command == "join") {
- } else if (state.command == "leave") {
- } else if (state.command == "peers") {
- } else if (state.command == "show") {
- } else if (state.command == "net-create") {
- } else if (state.command == "net-rm") {
- } else if (state.command == "net-ls") {
- } else if (state.command == "net-members") {
- } else if (state.command == "net-show") {
- } else if (state.command == "net-auth") {
- } else if (state.command == "net-set") {
- } else if (state.command == "id-generate") {
- } else if (state.command == "id-validate") {
- } else if (state.command == "id-sign") {
- } else if (state.command == "id-verify") {
- } else if (state.command == "id-getpublic") {
- } else {
- dumpHelp();
- return -1;
- }
-
- curl_global_cleanup();
-
- return 0;
-}
diff --git a/controller/DB.cpp b/controller/DB.cpp
new file mode 100644
index 00000000..b2e8878a
--- /dev/null
+++ b/controller/DB.cpp
@@ -0,0 +1,511 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "DB.hpp"
+#include "EmbeddedNetworkController.hpp"
+
+#include <chrono>
+#include <algorithm>
+#include <stdexcept>
+
+using json = nlohmann::json;
+
+namespace ZeroTier {
+
+void DB::initNetwork(nlohmann::json &network)
+{
+ if (!network.count("private")) network["private"] = true;
+ if (!network.count("creationTime")) network["creationTime"] = OSUtils::now();
+ if (!network.count("name")) network["name"] = "";
+ if (!network.count("multicastLimit")) network["multicastLimit"] = (uint64_t)32;
+ if (!network.count("enableBroadcast")) network["enableBroadcast"] = true;
+ if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}};
+ if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}};
+ if (!network.count("authTokens")) network["authTokens"] = {{}};
+ if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array();
+ if (!network.count("tags")) network["tags"] = nlohmann::json::array();
+ if (!network.count("routes")) network["routes"] = nlohmann::json::array();
+ if (!network.count("ipAssignmentPools")) network["ipAssignmentPools"] = nlohmann::json::array();
+ if (!network.count("mtu")) network["mtu"] = ZT_DEFAULT_MTU;
+ if (!network.count("remoteTraceTarget")) network["remoteTraceTarget"] = nlohmann::json();
+ if (!network.count("removeTraceLevel")) network["remoteTraceLevel"] = 0;
+ if (!network.count("rules")) {
+ // If unspecified, rules are set to allow anything and behave like a flat L2 segment
+ network["rules"] = {{
+ { "not",false },
+ { "or", false },
+ { "type","ACTION_ACCEPT" }
+ }};
+ }
+ network["objtype"] = "network";
+}
+
+void DB::initMember(nlohmann::json &member)
+{
+ if (!member.count("authorized")) member["authorized"] = false;
+ if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array();
+ if (!member.count("activeBridge")) member["activeBridge"] = false;
+ if (!member.count("tags")) member["tags"] = nlohmann::json::array();
+ if (!member.count("capabilities")) member["capabilities"] = nlohmann::json::array();
+ if (!member.count("creationTime")) member["creationTime"] = OSUtils::now();
+ if (!member.count("noAutoAssignIps")) member["noAutoAssignIps"] = false;
+ if (!member.count("revision")) member["revision"] = 0ULL;
+ if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL;
+ if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL;
+ if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json();
+ if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json();
+ if (!member.count("vMajor")) member["vMajor"] = -1;
+ if (!member.count("vMinor")) member["vMinor"] = -1;
+ if (!member.count("vRev")) member["vRev"] = -1;
+ if (!member.count("vProto")) member["vProto"] = -1;
+ if (!member.count("remoteTraceTarget")) member["remoteTraceTarget"] = nlohmann::json();
+ if (!member.count("removeTraceLevel")) member["remoteTraceLevel"] = 0;
+ member["objtype"] = "member";
+}
+
+void DB::cleanNetwork(nlohmann::json &network)
+{
+ network.erase("clock");
+ network.erase("authorizedMemberCount");
+ network.erase("activeMemberCount");
+ network.erase("totalMemberCount");
+ network.erase("lastModified");
+}
+
+void DB::cleanMember(nlohmann::json &member)
+{
+ member.erase("clock");
+ member.erase("physicalAddr");
+ member.erase("recentLog");
+ member.erase("lastModified");
+ member.erase("lastRequestMetaData");
+}
+
+DB::DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
+ _controller(nc),
+ _myId(myId),
+ _myAddress(myId.address()),
+ _path((path) ? path : "")
+{
+ char tmp[32];
+ _myAddress.toString(tmp);
+ _myAddressStr = tmp;
+}
+
+DB::~DB()
+{
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ }
+ return true;
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ auto m = nw->members.find(memberId);
+ if (m == nw->members.end())
+ return false;
+ member = m->second;
+ }
+ return true;
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ _fillSummaryInfo(nw,info);
+ auto m = nw->members.find(memberId);
+ if (m == nw->members.end())
+ return false;
+ member = m->second;
+ }
+ return true;
+}
+
+bool DB::get(const uint64_t networkId,nlohmann::json &network,std::vector<nlohmann::json> &members)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ network = nw->config;
+ for(auto m=nw->members.begin();m!=nw->members.end();++m)
+ members.push_back(m->second);
+ }
+ return true;
+}
+
+bool DB::summary(const uint64_t networkId,NetworkSummaryInfo &info)
+{
+ waitForReady();
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nwi = _networks.find(networkId);
+ if (nwi == _networks.end())
+ return false;
+ nw = nwi->second;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ _fillSummaryInfo(nw,info);
+ }
+ return true;
+}
+
+void DB::networks(std::vector<uint64_t> &networks)
+{
+ waitForReady();
+ std::lock_guard<std::mutex> l(_networks_l);
+ networks.reserve(_networks.size() + 1);
+ for(auto n=_networks.begin();n!=_networks.end();++n)
+ networks.push_back(n->first);
+}
+
+void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool push)
+{
+ uint64_t memberId = 0;
+ uint64_t networkId = 0;
+ bool isAuth = false;
+ bool wasAuth = false;
+ std::shared_ptr<_Network> nw;
+
+ if (old.is_object()) {
+ memberId = OSUtils::jsonIntHex(old["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(old["nwid"],0ULL);
+ if ((memberId)&&(networkId)) {
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nw2 = _networks.find(networkId);
+ if (nw2 != _networks.end())
+ nw = nw2->second;
+ }
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ if (OSUtils::jsonBool(old["activeBridge"],false))
+ nw->activeBridgeMembers.erase(memberId);
+ wasAuth = OSUtils::jsonBool(old["authorized"],false);
+ if (wasAuth)
+ nw->authorizedMembers.erase(memberId);
+ json &ips = old["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.erase(ipa);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (memberConfig.is_object()) {
+ if (!nw) {
+ memberId = OSUtils::jsonIntHex(memberConfig["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(memberConfig["nwid"],0ULL);
+ if ((!memberId)||(!networkId))
+ return;
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[networkId];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+
+ {
+ std::lock_guard<std::mutex> l(nw->lock);
+
+ nw->members[memberId] = memberConfig;
+
+ if (OSUtils::jsonBool(memberConfig["activeBridge"],false))
+ nw->activeBridgeMembers.insert(memberId);
+ isAuth = OSUtils::jsonBool(memberConfig["authorized"],false);
+ if (isAuth)
+ nw->authorizedMembers.insert(memberId);
+ json &ips = memberConfig["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.insert(ipa);
+ }
+ }
+ }
+
+ if (!isAuth) {
+ const int64_t ldt = (int64_t)OSUtils::jsonInt(memberConfig["lastDeauthorizedTime"],0ULL);
+ if (ldt > nw->mostRecentDeauthTime)
+ nw->mostRecentDeauthTime = ldt;
+ }
+ }
+
+ if (push)
+ _controller->onNetworkMemberUpdate(networkId,memberId);
+ } else if (memberId) {
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ nw->members.erase(memberId);
+ }
+ if (networkId) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto er = _networkByMember.equal_range(memberId);
+ for(auto i=er.first;i!=er.second;++i) {
+ if (i->second == networkId) {
+ _networkByMember.erase(i);
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ if (old.is_object()) {
+ json &config = old["config"];
+ if (config.is_object()) {
+ memberId = OSUtils::jsonIntHex(config["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(config["nwid"],0ULL);
+ if ((memberId)&&(networkId)) {
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto nw2 = _networks.find(networkId);
+ if (nw2 != _networks.end())
+ nw = nw2->second;
+ }
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ if (OSUtils::jsonBool(config["activeBridge"],false))
+ nw->activeBridgeMembers.erase(memberId);
+ wasAuth = OSUtils::jsonBool(config["authorized"],false);
+ if (wasAuth)
+ nw->authorizedMembers.erase(memberId);
+ json &ips = config["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.erase(ipa);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (member.is_object()) {
+ json &config = member["config"];
+ if (config.is_object()) {
+ if (!nw) {
+ memberId = OSUtils::jsonIntHex(config["id"],0ULL);
+ networkId = OSUtils::jsonIntHex(config["nwid"],0ULL);
+ if ((!memberId)||(!networkId))
+ return;
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[networkId];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+
+ {
+ std::lock_guard<std::mutex> l(nw->lock);
+
+ nw->members[memberId] = config;
+
+ if (OSUtils::jsonBool(config["activeBridge"],false))
+ nw->activeBridgeMembers.insert(memberId);
+ isAuth = OSUtils::jsonBool(config["authorized"],false);
+ if (isAuth)
+ nw->authorizedMembers.insert(memberId);
+ json &ips = config["ipAssignments"];
+ if (ips.is_array()) {
+ for(unsigned long i=0;i<ips.size();++i) {
+ json &ipj = ips[i];
+ if (ipj.is_string()) {
+ const std::string ips = ipj;
+ InetAddress ipa(ips.c_str());
+ ipa.setPort(0);
+ nw->allocatedIps.insert(ipa);
+ }
+ }
+ }
+
+ if (!isAuth) {
+ const int64_t ldt = (int64_t)OSUtils::jsonInt(config["lastDeauthorizedTime"],0ULL);
+ if (ldt > nw->mostRecentDeauthTime)
+ nw->mostRecentDeauthTime = ldt;
+ }
+ }
+
+ if (push)
+ _controller->onNetworkMemberUpdate(networkId,memberId);
+ }
+ } else if (memberId) {
+ if (nw) {
+ std::lock_guard<std::mutex> l(nw->lock);
+ nw->members.erase(memberId);
+ }
+ if (networkId) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ auto er = _networkByMember.equal_range(memberId);
+ for(auto i=er.first;i!=er.second;++i) {
+ if (i->second == networkId) {
+ _networkByMember.erase(i);
+ break;
+ }
+ }
+ }
+ }
+ */
+
+ if ((push)&&((wasAuth)&&(!isAuth)&&(networkId)&&(memberId)))
+ _controller->onNetworkMemberDeauthorize(networkId,memberId);
+}
+
+void DB::_networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool push)
+{
+ if (networkConfig.is_object()) {
+ const std::string ids = networkConfig["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[id];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ nw->config = networkConfig;
+ }
+ if (push)
+ _controller->onNetworkUpdate(id);
+ }
+ } else if (old.is_object()) {
+ const std::string ids = old["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ _networks.erase(id);
+ }
+ }
+
+ /*
+ if (network.is_object()) {
+ json &config = network["config"];
+ if (networkConfig.is_object()) {
+ const std::string ids = config["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::shared_ptr<_Network> nw;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ std::shared_ptr<_Network> &nw2 = _networks[id];
+ if (!nw2)
+ nw2.reset(new _Network);
+ nw = nw2;
+ }
+ {
+ std::lock_guard<std::mutex> l2(nw->lock);
+ nw->config = config;
+ }
+ if (push)
+ _controller->onNetworkUpdate(id);
+ }
+ }
+ } else if (old.is_object()) {
+ const std::string ids = old["id"];
+ const uint64_t id = Utils::hexStrToU64(ids.c_str());
+ if (id) {
+ std::lock_guard<std::mutex> l(_networks_l);
+ _networks.erase(id);
+ }
+ }
+ */
+}
+
+void DB::_fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info)
+{
+ for(auto ab=nw->activeBridgeMembers.begin();ab!=nw->activeBridgeMembers.end();++ab)
+ info.activeBridges.push_back(Address(*ab));
+ std::sort(info.activeBridges.begin(),info.activeBridges.end());
+ for(auto ip=nw->allocatedIps.begin();ip!=nw->allocatedIps.end();++ip)
+ info.allocatedIps.push_back(*ip);
+ std::sort(info.allocatedIps.begin(),info.allocatedIps.end());
+ info.authorizedMemberCount = (unsigned long)nw->authorizedMembers.size();
+ info.totalMemberCount = (unsigned long)nw->members.size();
+ info.mostRecentDeauthTime = nw->mostRecentDeauthTime;
+}
+
+} // namespace ZeroTier
diff --git a/controller/DB.hpp b/controller/DB.hpp
new file mode 100644
index 00000000..4757bb40
--- /dev/null
+++ b/controller/DB.hpp
@@ -0,0 +1,139 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_CONTROLLER_DB_HPP
+#define ZT_CONTROLLER_DB_HPP
+
+#include "../node/Constants.hpp"
+#include "../node/Identity.hpp"
+#include "../node/InetAddress.hpp"
+#include "../osdep/OSUtils.hpp"
+#include "../osdep/BlockingQueue.hpp"
+
+#include <memory>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+#include <atomic>
+
+#include "../ext/json/json.hpp"
+
+#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 2
+
+namespace ZeroTier
+{
+
+class EmbeddedNetworkController;
+
+/**
+ * Base class with common infrastructure for all controller DB implementations
+ */
+class DB
+{
+public:
+ struct NetworkSummaryInfo
+ {
+ NetworkSummaryInfo() : authorizedMemberCount(0),totalMemberCount(0),mostRecentDeauthTime(0) {}
+ std::vector<Address> activeBridges;
+ std::vector<InetAddress> allocatedIps;
+ unsigned long authorizedMemberCount;
+ unsigned long totalMemberCount;
+ int64_t mostRecentDeauthTime;
+ };
+
+ /**
+ * Ensure that all network fields are present
+ */
+ static void initNetwork(nlohmann::json &network);
+
+ /**
+ * Ensure that all member fields are present
+ */
+ static void initMember(nlohmann::json &member);
+
+ /**
+ * Remove old and temporary network fields
+ */
+ static void cleanNetwork(nlohmann::json &network);
+
+ /**
+ * Remove old and temporary member fields
+ */
+ static void cleanMember(nlohmann::json &member);
+
+ DB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
+ virtual ~DB();
+
+ virtual bool waitForReady() = 0;
+
+ inline bool hasNetwork(const uint64_t networkId) const
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ return (_networks.find(networkId) != _networks.end());
+ }
+
+ bool get(const uint64_t networkId,nlohmann::json &network);
+ bool get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member);
+ bool get(const uint64_t networkId,nlohmann::json &network,const uint64_t memberId,nlohmann::json &member,NetworkSummaryInfo &info);
+ bool get(const uint64_t networkId,nlohmann::json &network,std::vector<nlohmann::json> &members);
+
+ bool summary(const uint64_t networkId,NetworkSummaryInfo &info);
+
+ void networks(std::vector<uint64_t> &networks);
+
+ virtual void save(nlohmann::json *orig,nlohmann::json &record) = 0;
+
+ virtual void eraseNetwork(const uint64_t networkId) = 0;
+
+ virtual void eraseMember(const uint64_t networkId,const uint64_t memberId) = 0;
+
+ virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress) = 0;
+
+protected:
+ struct _Network
+ {
+ _Network() : mostRecentDeauthTime(0) {}
+ nlohmann::json config;
+ std::unordered_map<uint64_t,nlohmann::json> members;
+ std::unordered_set<uint64_t> activeBridgeMembers;
+ std::unordered_set<uint64_t> authorizedMembers;
+ std::unordered_set<InetAddress,InetAddress::Hasher> allocatedIps;
+ int64_t mostRecentDeauthTime;
+ std::mutex lock;
+ };
+
+ void _memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool push);
+ void _networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool push);
+ void _fillSummaryInfo(const std::shared_ptr<_Network> &nw,NetworkSummaryInfo &info);
+
+ EmbeddedNetworkController *const _controller;
+ const Identity _myId;
+ const Address _myAddress;
+ const std::string _path;
+ std::string _myAddressStr;
+
+ std::unordered_map< uint64_t,std::shared_ptr<_Network> > _networks;
+ std::unordered_multimap< uint64_t,uint64_t > _networkByMember;
+ mutable std::mutex _networks_l;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp
new file mode 100644
index 00000000..9a07b285
--- /dev/null
+++ b/controller/EmbeddedNetworkController.cpp
@@ -0,0 +1,1710 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+#include <sys/types.h>
+
+#include <algorithm>
+#include <utility>
+#include <stdexcept>
+#include <map>
+#include <thread>
+#include <memory>
+
+#include "../include/ZeroTierOne.h"
+#include "../version.h"
+
+#include "EmbeddedNetworkController.hpp"
+
+#include "../node/Node.hpp"
+#include "../node/CertificateOfMembership.hpp"
+#include "../node/NetworkConfig.hpp"
+#include "../node/Dictionary.hpp"
+#include "../node/MAC.hpp"
+
+using json = nlohmann::json;
+
+// API version reported via JSON control plane
+#define ZT_NETCONF_CONTROLLER_API_VERSION 4
+
+// Min duration between requests for an address/nwid combo to prevent floods
+#define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
+
+// Global maximum size of arrays in JSON objects
+#define ZT_CONTROLLER_MAX_ARRAY_SIZE 16384
+
+namespace ZeroTier {
+
+namespace {
+
+static json _renderRule(ZT_VirtualNetworkRule &rule)
+{
+ char tmp[128];
+ json r = json::object();
+ const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rule.t & 0x3f);
+
+ switch(rt) {
+ case ZT_NETWORK_RULE_ACTION_DROP:
+ r["type"] = "ACTION_DROP";
+ break;
+ case ZT_NETWORK_RULE_ACTION_ACCEPT:
+ r["type"] = "ACTION_ACCEPT";
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ r["type"] = "ACTION_TEE";
+ r["address"] = Address(rule.v.fwd.address).toString(tmp);
+ r["flags"] = (unsigned int)rule.v.fwd.flags;
+ r["length"] = (unsigned int)rule.v.fwd.length;
+ break;
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ r["type"] = "ACTION_WATCH";
+ r["address"] = Address(rule.v.fwd.address).toString(tmp);
+ r["flags"] = (unsigned int)rule.v.fwd.flags;
+ r["length"] = (unsigned int)rule.v.fwd.length;
+ break;
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ r["type"] = "ACTION_REDIRECT";
+ r["address"] = Address(rule.v.fwd.address).toString(tmp);
+ r["flags"] = (unsigned int)rule.v.fwd.flags;
+ break;
+ case ZT_NETWORK_RULE_ACTION_BREAK:
+ r["type"] = "ACTION_BREAK";
+ break;
+ default:
+ break;
+ }
+
+ if (r.size() == 0) {
+ switch(rt) {
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ r["type"] = "MATCH_SOURCE_ZEROTIER_ADDRESS";
+ r["zt"] = Address(rule.v.zt).toString(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ r["type"] = "MATCH_DEST_ZEROTIER_ADDRESS";
+ r["zt"] = Address(rule.v.zt).toString(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ r["type"] = "MATCH_VLAN_ID";
+ r["vlanId"] = (unsigned int)rule.v.vlanId;
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ r["type"] = "MATCH_VLAN_PCP";
+ r["vlanPcp"] = (unsigned int)rule.v.vlanPcp;
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ r["type"] = "MATCH_VLAN_DEI";
+ r["vlanDei"] = (unsigned int)rule.v.vlanDei;
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ r["type"] = "MATCH_MAC_SOURCE";
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
+ r["mac"] = tmp;
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ r["type"] = "MATCH_MAC_DEST";
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
+ r["mac"] = tmp;
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ r["type"] = "MATCH_IPV4_SOURCE";
+ r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ r["type"] = "MATCH_IPV4_DEST";
+ r["ip"] = InetAddress(&(rule.v.ipv4.ip),4,(unsigned int)rule.v.ipv4.mask).toString(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ r["type"] = "MATCH_IPV6_SOURCE";
+ r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ r["type"] = "MATCH_IPV6_DEST";
+ r["ip"] = InetAddress(rule.v.ipv6.ip,16,(unsigned int)rule.v.ipv6.mask).toString(tmp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ r["type"] = "MATCH_IP_TOS";
+ r["mask"] = (unsigned int)rule.v.ipTos.mask;
+ r["start"] = (unsigned int)rule.v.ipTos.value[0];
+ r["end"] = (unsigned int)rule.v.ipTos.value[1];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ r["type"] = "MATCH_IP_PROTOCOL";
+ r["ipProtocol"] = (unsigned int)rule.v.ipProtocol;
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ r["type"] = "MATCH_ETHERTYPE";
+ r["etherType"] = (unsigned int)rule.v.etherType;
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ r["type"] = "MATCH_ICMP";
+ r["icmpType"] = (unsigned int)rule.v.icmp.type;
+ if ((rule.v.icmp.flags & 0x01) != 0)
+ r["icmpCode"] = (unsigned int)rule.v.icmp.code;
+ else r["icmpCode"] = json();
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ r["type"] = "MATCH_IP_SOURCE_PORT_RANGE";
+ r["start"] = (unsigned int)rule.v.port[0];
+ r["end"] = (unsigned int)rule.v.port[1];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ r["type"] = "MATCH_IP_DEST_PORT_RANGE";
+ r["start"] = (unsigned int)rule.v.port[0];
+ r["end"] = (unsigned int)rule.v.port[1];
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ r["type"] = "MATCH_CHARACTERISTICS";
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.characteristics);
+ r["mask"] = tmp;
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ r["type"] = "MATCH_FRAME_SIZE_RANGE";
+ r["start"] = (unsigned int)rule.v.frameSize[0];
+ r["end"] = (unsigned int)rule.v.frameSize[1];
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ r["type"] = "MATCH_RANDOM";
+ r["probability"] = (unsigned long)rule.v.randomProbability;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ r["type"] = "MATCH_TAGS_DIFFERENCE";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ r["type"] = "MATCH_TAGS_BITWISE_AND";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ r["type"] = "MATCH_TAGS_BITWISE_OR";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ r["type"] = "MATCH_TAGS_BITWISE_XOR";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
+ r["type"] = "MATCH_TAGS_EQUAL";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ r["type"] = "MATCH_TAG_SENDER";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
+ r["type"] = "MATCH_TAG_RECEIVER";
+ r["id"] = rule.v.tag.id;
+ r["value"] = rule.v.tag.value;
+ break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ r["type"] = "INTEGER_RANGE";
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.intRange.start);
+ r["start"] = tmp;
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.intRange.start + (uint64_t)rule.v.intRange.end);
+ r["end"] = tmp;
+ r["idx"] = rule.v.intRange.idx;
+ r["little"] = ((rule.v.intRange.format & 0x80) != 0);
+ r["bits"] = (rule.v.intRange.format & 63) + 1;
+ break;
+ default:
+ break;
+ }
+
+ if (r.size() > 0) {
+ r["not"] = ((rule.t & 0x80) != 0);
+ r["or"] = ((rule.t & 0x40) != 0);
+ }
+ }
+
+ return r;
+}
+
+static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule)
+{
+ if (!r.is_object())
+ return false;
+
+ const std::string t(OSUtils::jsonString(r["type"],""));
+ memset(&rule,0,sizeof(ZT_VirtualNetworkRule));
+
+ if (OSUtils::jsonBool(r["not"],false))
+ rule.t = 0x80;
+ else rule.t = 0x00;
+ if (OSUtils::jsonBool(r["or"],false))
+ rule.t |= 0x40;
+
+ bool tag = false;
+ if (t == "ACTION_DROP") {
+ rule.t |= ZT_NETWORK_RULE_ACTION_DROP;
+ return true;
+ } else if (t == "ACTION_ACCEPT") {
+ rule.t |= ZT_NETWORK_RULE_ACTION_ACCEPT;
+ return true;
+ } else if (t == "ACTION_TEE") {
+ rule.t |= ZT_NETWORK_RULE_ACTION_TEE;
+ rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL;
+ rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL);
+ rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"],0ULL) & 0xffffULL);
+ return true;
+ } else if (t == "ACTION_WATCH") {
+ rule.t |= ZT_NETWORK_RULE_ACTION_WATCH;
+ rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL;
+ rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL);
+ rule.v.fwd.length = (uint16_t)(OSUtils::jsonInt(r["length"],0ULL) & 0xffffULL);
+ return true;
+ } else if (t == "ACTION_REDIRECT") {
+ rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT;
+ rule.v.fwd.address = Utils::hexStrToU64(OSUtils::jsonString(r["address"],"0").c_str()) & 0xffffffffffULL;
+ rule.v.fwd.flags = (uint32_t)(OSUtils::jsonInt(r["flags"],0ULL) & 0xffffffffULL);
+ return true;
+ } else if (t == "ACTION_BREAK") {
+ rule.t |= ZT_NETWORK_RULE_ACTION_BREAK;
+ return true;
+ } else if (t == "MATCH_SOURCE_ZEROTIER_ADDRESS") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS;
+ rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"],"0").c_str()) & 0xffffffffffULL;
+ return true;
+ } else if (t == "MATCH_DEST_ZEROTIER_ADDRESS") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS;
+ rule.v.zt = Utils::hexStrToU64(OSUtils::jsonString(r["zt"],"0").c_str()) & 0xffffffffffULL;
+ return true;
+ } else if (t == "MATCH_VLAN_ID") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_ID;
+ rule.v.vlanId = (uint16_t)(OSUtils::jsonInt(r["vlanId"],0ULL) & 0xffffULL);
+ return true;
+ } else if (t == "MATCH_VLAN_PCP") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_PCP;
+ rule.v.vlanPcp = (uint8_t)(OSUtils::jsonInt(r["vlanPcp"],0ULL) & 0xffULL);
+ return true;
+ } else if (t == "MATCH_VLAN_DEI") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_DEI;
+ rule.v.vlanDei = (uint8_t)(OSUtils::jsonInt(r["vlanDei"],0ULL) & 0xffULL);
+ return true;
+ } else if (t == "MATCH_MAC_SOURCE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_MAC_SOURCE;
+ const std::string mac(OSUtils::jsonString(r["mac"],"0"));
+ Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6);
+ return true;
+ } else if (t == "MATCH_MAC_DEST") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_MAC_DEST;
+ const std::string mac(OSUtils::jsonString(r["mac"],"0"));
+ Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6);
+ return true;
+ } else if (t == "MATCH_IPV4_SOURCE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_SOURCE;
+ InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0").c_str());
+ rule.v.ipv4.ip = reinterpret_cast<struct sockaddr_in *>(&ip)->sin_addr.s_addr;
+ rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in *>(&ip)->sin_port) & 0xff;
+ if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32;
+ return true;
+ } else if (t == "MATCH_IPV4_DEST") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_DEST;
+ InetAddress ip(OSUtils::jsonString(r["ip"],"0.0.0.0").c_str());
+ rule.v.ipv4.ip = reinterpret_cast<struct sockaddr_in *>(&ip)->sin_addr.s_addr;
+ rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in *>(&ip)->sin_port) & 0xff;
+ if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32;
+ return true;
+ } else if (t == "MATCH_IPV6_SOURCE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_SOURCE;
+ InetAddress ip(OSUtils::jsonString(r["ip"],"::0").c_str());
+ ZT_FAST_MEMCPY(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_port) & 0xff;
+ if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128;
+ return true;
+ } else if (t == "MATCH_IPV6_DEST") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_DEST;
+ InetAddress ip(OSUtils::jsonString(r["ip"],"::0").c_str());
+ ZT_FAST_MEMCPY(rule.v.ipv6.ip,reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast<struct sockaddr_in6 *>(&ip)->sin6_port) & 0xff;
+ if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128;
+ return true;
+ } else if (t == "MATCH_IP_TOS") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IP_TOS;
+ rule.v.ipTos.mask = (uint8_t)(OSUtils::jsonInt(r["mask"],0ULL) & 0xffULL);
+ rule.v.ipTos.value[0] = (uint8_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffULL);
+ rule.v.ipTos.value[1] = (uint8_t)(OSUtils::jsonInt(r["end"],0ULL) & 0xffULL);
+ return true;
+ } else if (t == "MATCH_IP_PROTOCOL") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ rule.v.ipProtocol = (uint8_t)(OSUtils::jsonInt(r["ipProtocol"],0ULL) & 0xffULL);
+ return true;
+ } else if (t == "MATCH_ETHERTYPE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_ETHERTYPE;
+ rule.v.etherType = (uint16_t)(OSUtils::jsonInt(r["etherType"],0ULL) & 0xffffULL);
+ return true;
+ } else if (t == "MATCH_ICMP") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_ICMP;
+ rule.v.icmp.type = (uint8_t)(OSUtils::jsonInt(r["icmpType"],0ULL) & 0xffULL);
+ json &code = r["icmpCode"];
+ if (code.is_null()) {
+ rule.v.icmp.code = 0;
+ rule.v.icmp.flags = 0x00;
+ } else {
+ rule.v.icmp.code = (uint8_t)(OSUtils::jsonInt(code,0ULL) & 0xffULL);
+ rule.v.icmp.flags = 0x01;
+ }
+ return true;
+ } else if (t == "MATCH_IP_SOURCE_PORT_RANGE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE;
+ rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL);
+ rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL);
+ return true;
+ } else if (t == "MATCH_IP_DEST_PORT_RANGE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
+ rule.v.port[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL);
+ rule.v.port[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL);
+ return true;
+ } else if (t == "MATCH_CHARACTERISTICS") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ if (r.count("mask")) {
+ json &v = r["mask"];
+ if (v.is_number()) {
+ rule.v.characteristics = v;
+ } else {
+ std::string tmp = v;
+ rule.v.characteristics = Utils::hexStrToU64(tmp.c_str());
+ }
+ }
+ return true;
+ } else if (t == "MATCH_FRAME_SIZE_RANGE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE;
+ rule.v.frameSize[0] = (uint16_t)(OSUtils::jsonInt(r["start"],0ULL) & 0xffffULL);
+ rule.v.frameSize[1] = (uint16_t)(OSUtils::jsonInt(r["end"],(uint64_t)rule.v.frameSize[0]) & 0xffffULL);
+ return true;
+ } else if (t == "MATCH_RANDOM") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_RANDOM;
+ rule.v.randomProbability = (uint32_t)(OSUtils::jsonInt(r["probability"],0ULL) & 0xffffffffULL);
+ return true;
+ } else if (t == "MATCH_TAGS_DIFFERENCE") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE;
+ tag = true;
+ } else if (t == "MATCH_TAGS_BITWISE_AND") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND;
+ tag = true;
+ } else if (t == "MATCH_TAGS_BITWISE_OR") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR;
+ tag = true;
+ } else if (t == "MATCH_TAGS_BITWISE_XOR") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR;
+ tag = true;
+ } else if (t == "MATCH_TAGS_EQUAL") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_EQUAL;
+ tag = true;
+ } else if (t == "MATCH_TAG_SENDER") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAG_SENDER;
+ tag = true;
+ } else if (t == "MATCH_TAG_RECEIVER") {
+ rule.t |= ZT_NETWORK_RULE_MATCH_TAG_RECEIVER;
+ tag = true;
+ } else if (t == "INTEGER_RANGE") {
+ json &s = r["start"];
+ if (s.is_string()) {
+ std::string tmp = s;
+ rule.v.intRange.start = Utils::hexStrToU64(tmp.c_str());
+ } else {
+ rule.v.intRange.start = OSUtils::jsonInt(s,0ULL);
+ }
+ json &e = r["end"];
+ if (e.is_string()) {
+ std::string tmp = e;
+ rule.v.intRange.end = (uint32_t)(Utils::hexStrToU64(tmp.c_str()) - rule.v.intRange.start);
+ } else {
+ rule.v.intRange.end = (uint32_t)(OSUtils::jsonInt(e,0ULL) - rule.v.intRange.start);
+ }
+ rule.v.intRange.idx = (uint16_t)OSUtils::jsonInt(r["idx"],0ULL);
+ rule.v.intRange.format = (OSUtils::jsonBool(r["little"],false)) ? 0x80 : 0x00;
+ rule.v.intRange.format |= (uint8_t)((OSUtils::jsonInt(r["bits"],1ULL) - 1) & 63);
+ }
+
+ if (tag) {
+ rule.v.tag.id = (uint32_t)(OSUtils::jsonInt(r["id"],0ULL) & 0xffffffffULL);
+ rule.v.tag.value = (uint32_t)(OSUtils::jsonInt(r["value"],0ULL) & 0xffffffffULL);
+ return true;
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPath) :
+ _startTime(OSUtils::now()),
+ _node(node),
+ _path(dbPath),
+ _sender((NetworkController::Sender *)0)
+{
+}
+
+EmbeddedNetworkController::~EmbeddedNetworkController()
+{
+ std::lock_guard<std::mutex> l(_threads_l);
+ _queue.stop();
+ for(auto t=_threads.begin();t!=_threads.end();++t)
+ t->join();
+}
+
+void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender)
+{
+ char tmp[64];
+ _signingId = signingId;
+ _sender = sender;
+ _signingIdAddressString = signingId.address().toString(tmp);
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+ if ((_path.length() > 10)&&(_path.substr(0,10) == "rethinkdb:"))
+ _db.reset(new RethinkDB(this,_signingId,_path.c_str()));
+ else // else use FileDB after endif
+#endif
+ _db.reset(new FileDB(this,_signingId,_path.c_str()));
+ _db->waitForReady();
+}
+
+void EmbeddedNetworkController::request(
+ uint64_t nwid,
+ const InetAddress &fromAddr,
+ uint64_t requestPacketId,
+ const Identity &identity,
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData)
+{
+ if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender))
+ return;
+ _startThreads();
+ _RQEntry *qe = new _RQEntry;
+ qe->nwid = nwid;
+ qe->requestPacketId = requestPacketId;
+ qe->fromAddr = fromAddr;
+ qe->identity = identity;
+ qe->metaData = metaData;
+ qe->type = _RQEntry::RQENTRY_TYPE_REQUEST;
+ _queue.post(qe);
+}
+
+unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
+ const std::vector<std::string> &path,
+ const std::map<std::string,std::string> &urlArgs,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType)
+{
+ if (!_db)
+ return 500;
+
+ if ((path.size() > 0)&&(path[0] == "network")) {
+
+ if ((path.size() >= 2)&&(path[1].length() == 16)) {
+ const uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
+ json network;
+ if (!_db->get(nwid,network))
+ return 404;
+
+ if (path.size() >= 3) {
+
+ if (path[2] == "member") {
+
+ if (path.size() >= 4) {
+ // Get member
+
+ const uint64_t address = Utils::hexStrToU64(path[3].c_str());
+ json member;
+ if (!_db->get(nwid,network,address,member))
+ return 404;
+ responseBody = OSUtils::jsonDump(member);
+ responseContentType = "application/json";
+
+ } else {
+ // List members and their revisions
+
+ responseBody = "{";
+ std::vector<json> members;
+ if (_db->get(nwid,network,members)) {
+ responseBody.reserve((members.size() + 2) * 32);
+ std::string mid;
+ for(auto member=members.begin();member!=members.end();++member) {
+ mid = (*member)["id"];
+ char tmp[128];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s\"%s\":%llu",(responseBody.length() > 1) ? ",\"" : "\"",mid.c_str(),(unsigned long long)OSUtils::jsonInt((*member)["revision"],0));
+ responseBody.append(tmp);
+ }
+ }
+ responseBody.push_back('}');
+ responseContentType = "application/json";
+
+ }
+ return 200;
+
+ } // else 404
+
+ } else {
+ // Get network
+
+ responseBody = OSUtils::jsonDump(network);
+ responseContentType = "application/json";
+ return 200;
+
+ }
+ } else if (path.size() == 1) {
+ // List networks
+
+ std::vector<uint64_t> networkIds;
+ _db->networks(networkIds);
+ char tmp[64];
+ responseBody = "[";
+ responseBody.reserve((networkIds.size() + 1) * 24);
+ for(std::vector<uint64_t>::const_iterator i(networkIds.begin());i!=networkIds.end();++i) {
+ if (responseBody.length() > 1)
+ responseBody.push_back(',');
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"\"%.16llx\"",(unsigned long long)*i);
+ responseBody.append(tmp);
+ }
+ responseBody.push_back(']');
+ responseContentType = "application/json";
+
+ return 200;
+
+ } // else 404
+
+ } else {
+ // Controller status
+
+ char tmp[4096];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
+ responseBody = tmp;
+ responseContentType = "application/json";
+ return 200;
+
+ }
+
+ return 404;
+}
+
+unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
+ const std::vector<std::string> &path,
+ const std::map<std::string,std::string> &urlArgs,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType)
+{
+ if (!_db)
+ return 500;
+ if (path.empty())
+ return 404;
+
+ json b;
+ try {
+ b = OSUtils::jsonParse(body);
+ if (!b.is_object()) {
+ responseBody = "{ \"message\": \"body is not a JSON object\" }";
+ responseContentType = "application/json";
+ return 400;
+ }
+ } catch ( ... ) {
+ responseBody = "{ \"message\": \"body JSON is invalid\" }";
+ responseContentType = "application/json";
+ return 400;
+ }
+ const int64_t now = OSUtils::now();
+
+ if (path[0] == "network") {
+
+ if ((path.size() >= 2)&&(path[1].length() == 16)) {
+ uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
+ char nwids[24];
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
+
+ if (path.size() >= 3) {
+
+ if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
+ uint64_t address = Utils::hexStrToU64(path[3].c_str());
+ char addrs[24];
+ OSUtils::ztsnprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address);
+
+ json member,network;
+ _db->get(nwid,network,address,member);
+ json origMember(member); // for detecting changes
+ DB::initMember(member);
+
+ try {
+ if (b.count("activeBridge")) member["activeBridge"] = OSUtils::jsonBool(b["activeBridge"],false);
+ if (b.count("noAutoAssignIps")) member["noAutoAssignIps"] = OSUtils::jsonBool(b["noAutoAssignIps"],false);
+
+ if (b.count("remoteTraceTarget")) {
+ const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],""));
+ if (rtt.length() == 10) {
+ member["remoteTraceTarget"] = rtt;
+ } else {
+ member["remoteTraceTarget"] = json();
+ }
+ }
+ if (b.count("remoteTraceLevel")) member["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL);
+
+ if (b.count("authorized")) {
+ const bool newAuth = OSUtils::jsonBool(b["authorized"],false);
+ if (newAuth != OSUtils::jsonBool(member["authorized"],false)) {
+ member["authorized"] = newAuth;
+ member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = now;
+ if (newAuth) {
+ member["lastAuthorizedCredentialType"] = "api";
+ member["lastAuthorizedCredential"] = json();
+ }
+ }
+ }
+
+ if (b.count("ipAssignments")) {
+ json &ipa = b["ipAssignments"];
+ if (ipa.is_array()) {
+ json mipa(json::array());
+ for(unsigned long i=0;i<ipa.size();++i) {
+ std::string ips = ipa[i];
+ InetAddress ip(ips.c_str());
+ if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) {
+ char tmpip[64];
+ mipa.push_back(ip.toIpString(tmpip));
+ if (mipa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ }
+ member["ipAssignments"] = mipa;
+ }
+ }
+
+ if (b.count("tags")) {
+ json &tags = b["tags"];
+ if (tags.is_array()) {
+ std::map<uint64_t,uint64_t> mtags;
+ for(unsigned long i=0;i<tags.size();++i) {
+ json &tag = tags[i];
+ if ((tag.is_array())&&(tag.size() == 2))
+ mtags[OSUtils::jsonInt(tag[0],0ULL) & 0xffffffffULL] = OSUtils::jsonInt(tag[1],0ULL) & 0xffffffffULL;
+ }
+ json mtagsa = json::array();
+ for(std::map<uint64_t,uint64_t>::iterator t(mtags.begin());t!=mtags.end();++t) {
+ json ta = json::array();
+ ta.push_back(t->first);
+ ta.push_back(t->second);
+ mtagsa.push_back(ta);
+ if (mtagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ member["tags"] = mtagsa;
+ }
+ }
+
+ if (b.count("capabilities")) {
+ json &capabilities = b["capabilities"];
+ if (capabilities.is_array()) {
+ json mcaps = json::array();
+ for(unsigned long i=0;i<capabilities.size();++i) {
+ mcaps.push_back(OSUtils::jsonInt(capabilities[i],0ULL));
+ if (mcaps.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ std::sort(mcaps.begin(),mcaps.end());
+ mcaps.erase(std::unique(mcaps.begin(),mcaps.end()),mcaps.end());
+ member["capabilities"] = mcaps;
+ }
+ }
+ } catch ( ... ) {
+ responseBody = "{ \"message\": \"exception while processing parameters in JSON body\" }";
+ responseContentType = "application/json";
+ return 400;
+ }
+
+ member["id"] = addrs;
+ member["address"] = addrs; // legacy
+ member["nwid"] = nwids;
+
+ DB::cleanMember(member);
+ _db->save(&origMember,member);
+ responseBody = OSUtils::jsonDump(member);
+ responseContentType = "application/json";
+
+ return 200;
+ } // else 404
+
+ } else {
+ // POST to network ID
+
+ // Magic ID ending with ______ picks a random unused network ID
+ if (path[1].substr(10) == "______") {
+ nwid = 0;
+ uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL;
+ uint64_t nwidPostfix = 0;
+ for(unsigned long k=0;k<100000;++k) { // sanity limit on trials
+ Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix));
+ uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL);
+ if ((tryNwid & 0xffffffULL) == 0ULL) tryNwid |= 1ULL;
+ if (!_db->hasNetwork(tryNwid)) {
+ nwid = tryNwid;
+ break;
+ }
+ }
+ if (!nwid)
+ return 503;
+ }
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
+
+ json network;
+ _db->get(nwid,network);
+ json origNetwork(network); // for detecting changes
+ DB::initNetwork(network);
+
+ try {
+ if (b.count("name")) network["name"] = OSUtils::jsonString(b["name"],"");
+ if (b.count("private")) network["private"] = OSUtils::jsonBool(b["private"],true);
+ if (b.count("enableBroadcast")) network["enableBroadcast"] = OSUtils::jsonBool(b["enableBroadcast"],false);
+ if (b.count("multicastLimit")) network["multicastLimit"] = OSUtils::jsonInt(b["multicastLimit"],32ULL);
+ if (b.count("mtu")) network["mtu"] = std::max(std::min((unsigned int)OSUtils::jsonInt(b["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
+
+ if (b.count("remoteTraceTarget")) {
+ const std::string rtt(OSUtils::jsonString(b["remoteTraceTarget"],""));
+ if (rtt.length() == 10) {
+ network["remoteTraceTarget"] = rtt;
+ } else {
+ network["remoteTraceTarget"] = json();
+ }
+ }
+ if (b.count("remoteTraceLevel")) network["remoteTraceLevel"] = OSUtils::jsonInt(b["remoteTraceLevel"],0ULL);
+
+ if (b.count("v4AssignMode")) {
+ json nv4m;
+ json &v4m = b["v4AssignMode"];
+ if (v4m.is_string()) { // backward compatibility
+ nv4m["zt"] = (OSUtils::jsonString(v4m,"") == "zt");
+ } else if (v4m.is_object()) {
+ nv4m["zt"] = OSUtils::jsonBool(v4m["zt"],false);
+ } else nv4m["zt"] = false;
+ network["v4AssignMode"] = nv4m;
+ }
+
+ if (b.count("v6AssignMode")) {
+ json nv6m;
+ json &v6m = b["v6AssignMode"];
+ if (!nv6m.is_object()) nv6m = json::object();
+ if (v6m.is_string()) { // backward compatibility
+ std::vector<std::string> v6ms(OSUtils::split(OSUtils::jsonString(v6m,"").c_str(),",","",""));
+ std::sort(v6ms.begin(),v6ms.end());
+ v6ms.erase(std::unique(v6ms.begin(),v6ms.end()),v6ms.end());
+ nv6m["rfc4193"] = false;
+ nv6m["zt"] = false;
+ nv6m["6plane"] = false;
+ for(std::vector<std::string>::iterator i(v6ms.begin());i!=v6ms.end();++i) {
+ if (*i == "rfc4193")
+ nv6m["rfc4193"] = true;
+ else if (*i == "zt")
+ nv6m["zt"] = true;
+ else if (*i == "6plane")
+ nv6m["6plane"] = true;
+ }
+ } else if (v6m.is_object()) {
+ if (v6m.count("rfc4193")) nv6m["rfc4193"] = OSUtils::jsonBool(v6m["rfc4193"],false);
+ if (v6m.count("zt")) nv6m["zt"] = OSUtils::jsonBool(v6m["zt"],false);
+ if (v6m.count("6plane")) nv6m["6plane"] = OSUtils::jsonBool(v6m["6plane"],false);
+ } else {
+ nv6m["rfc4193"] = false;
+ nv6m["zt"] = false;
+ nv6m["6plane"] = false;
+ }
+ network["v6AssignMode"] = nv6m;
+ }
+
+ if (b.count("routes")) {
+ json &rts = b["routes"];
+ if (rts.is_array()) {
+ json nrts = json::array();
+ for(unsigned long i=0;i<rts.size();++i) {
+ json &rt = rts[i];
+ if (rt.is_object()) {
+ json &target = rt["target"];
+ json &via = rt["via"];
+ if (target.is_string()) {
+ InetAddress t(target.get<std::string>().c_str());
+ InetAddress v;
+ if (via.is_string()) v.fromString(via.get<std::string>().c_str());
+ if ( ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) && (t.netmaskBitsValid()) ) {
+ json tmp;
+ char tmp2[64];
+ tmp["target"] = t.toString(tmp2);
+ if (v.ss_family == t.ss_family)
+ tmp["via"] = v.toIpString(tmp2);
+ else tmp["via"] = json();
+ nrts.push_back(tmp);
+ if (nrts.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ }
+ }
+ }
+ network["routes"] = nrts;
+ }
+ }
+
+ if (b.count("ipAssignmentPools")) {
+ json &ipp = b["ipAssignmentPools"];
+ if (ipp.is_array()) {
+ json nipp = json::array();
+ for(unsigned long i=0;i<ipp.size();++i) {
+ json &ip = ipp[i];
+ if ((ip.is_object())&&(ip.count("ipRangeStart"))&&(ip.count("ipRangeEnd"))) {
+ InetAddress f(OSUtils::jsonString(ip["ipRangeStart"],"").c_str());
+ InetAddress t(OSUtils::jsonString(ip["ipRangeEnd"],"").c_str());
+ if ( ((f.ss_family == AF_INET)||(f.ss_family == AF_INET6)) && (f.ss_family == t.ss_family) ) {
+ json tmp = json::object();
+ char tmp2[64];
+ tmp["ipRangeStart"] = f.toIpString(tmp2);
+ tmp["ipRangeEnd"] = t.toIpString(tmp2);
+ nipp.push_back(tmp);
+ if (nipp.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ }
+ }
+ network["ipAssignmentPools"] = nipp;
+ }
+ }
+
+ if (b.count("rules")) {
+ json &rules = b["rules"];
+ if (rules.is_array()) {
+ json nrules = json::array();
+ for(unsigned long i=0;i<rules.size();++i) {
+ json &rule = rules[i];
+ if (rule.is_object()) {
+ ZT_VirtualNetworkRule ztr;
+ if (_parseRule(rule,ztr)) {
+ nrules.push_back(_renderRule(ztr));
+ if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ }
+ }
+ network["rules"] = nrules;
+ }
+ }
+
+ if (b.count("authTokens")) {
+ json &authTokens = b["authTokens"];
+ if (authTokens.is_object()) {
+ json nat;
+ for(json::iterator t(authTokens.begin());t!=authTokens.end();++t) {
+ if ((t.value().is_number())&&(t.value() >= 0))
+ nat[t.key()] = t.value();
+ }
+ network["authTokens"] = nat;
+ } else {
+ network["authTokens"] = {{}};
+ }
+ }
+
+ if (b.count("capabilities")) {
+ json &capabilities = b["capabilities"];
+ if (capabilities.is_array()) {
+ std::map< uint64_t,json > ncaps;
+ for(unsigned long i=0;i<capabilities.size();++i) {
+ json &cap = capabilities[i];
+ if (cap.is_object()) {
+ json ncap = json::object();
+ const uint64_t capId = OSUtils::jsonInt(cap["id"],0ULL);
+ ncap["id"] = capId;
+ ncap["default"] = OSUtils::jsonBool(cap["default"],false);
+
+ json &rules = cap["rules"];
+ json nrules = json::array();
+ if (rules.is_array()) {
+ for(unsigned long i=0;i<rules.size();++i) {
+ json &rule = rules[i];
+ if (rule.is_object()) {
+ ZT_VirtualNetworkRule ztr;
+ if (_parseRule(rule,ztr)) {
+ nrules.push_back(_renderRule(ztr));
+ if (nrules.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ }
+ }
+ }
+ ncap["rules"] = nrules;
+
+ ncaps[capId] = ncap;
+ }
+ }
+
+ json ncapsa = json::array();
+ for(std::map< uint64_t,json >::iterator c(ncaps.begin());c!=ncaps.end();++c) {
+ ncapsa.push_back(c->second);
+ if (ncapsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ network["capabilities"] = ncapsa;
+ }
+ }
+
+ if (b.count("tags")) {
+ json &tags = b["tags"];
+ if (tags.is_array()) {
+ std::map< uint64_t,json > ntags;
+ for(unsigned long i=0;i<tags.size();++i) {
+ json &tag = tags[i];
+ if (tag.is_object()) {
+ json ntag = json::object();
+ const uint64_t tagId = OSUtils::jsonInt(tag["id"],0ULL);
+ ntag["id"] = tagId;
+ json &dfl = tag["default"];
+ if (dfl.is_null())
+ ntag["default"] = dfl;
+ else ntag["default"] = OSUtils::jsonInt(dfl,0ULL);
+ ntags[tagId] = ntag;
+ }
+ }
+
+ json ntagsa = json::array();
+ for(std::map< uint64_t,json >::iterator t(ntags.begin());t!=ntags.end();++t) {
+ ntagsa.push_back(t->second);
+ if (ntagsa.size() >= ZT_CONTROLLER_MAX_ARRAY_SIZE)
+ break;
+ }
+ network["tags"] = ntagsa;
+ }
+ }
+
+ } catch ( ... ) {
+ responseBody = "{ \"message\": \"exception occurred while parsing body variables\" }";
+ responseContentType = "application/json";
+ return 400;
+ }
+
+ network["id"] = nwids;
+ network["nwid"] = nwids; // legacy
+
+ DB::cleanNetwork(network);
+ _db->save(&origNetwork,network);
+
+ responseBody = OSUtils::jsonDump(network);
+ responseContentType = "application/json";
+ return 200;
+ } // else 404
+
+ } // else 404
+
+ }
+
+ return 404;
+}
+
+unsigned int EmbeddedNetworkController::handleControlPlaneHttpDELETE(
+ const std::vector<std::string> &path,
+ const std::map<std::string,std::string> &urlArgs,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType)
+{
+ if (!_db)
+ return 500;
+ if (path.empty())
+ return 404;
+
+ if (path[0] == "network") {
+ if ((path.size() >= 2)&&(path[1].length() == 16)) {
+ const uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
+ if (path.size() >= 3) {
+ if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
+ const uint64_t address = Utils::hexStrToU64(path[3].c_str());
+
+ json network,member;
+ _db->get(nwid,network,address,member);
+
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _memberStatus.erase(_MemberStatusKey(nwid,address));
+ }
+
+ if (!member.size())
+ return 404;
+ responseBody = OSUtils::jsonDump(member);
+ responseContentType = "application/json";
+ return 200;
+ }
+ } else {
+ json network;
+ _db->get(nwid,network);
+ _db->eraseNetwork(nwid);
+
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ for(auto i=_memberStatus.begin();i!=_memberStatus.end();) {
+ if (i->first.networkId == nwid)
+ _memberStatus.erase(i++);
+ else ++i;
+ }
+ }
+
+ if (!network.size())
+ return 404;
+ responseBody = OSUtils::jsonDump(network);
+ responseContentType = "application/json";
+ return 200;
+ }
+ } // else 404
+
+ } // else 404
+
+ return 404;
+}
+
+void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt)
+{
+ static volatile unsigned long idCounter = 0;
+ char id[128],tmp[128];
+ std::string k,v;
+
+ if (!_db)
+ return;
+
+ try {
+ // Convert Dictionary into JSON object
+ json d;
+ char *saveptr = (char *)0;
+ for(char *l=Utils::stok(rt.data,"\n",&saveptr);(l);l=Utils::stok((char *)0,"\n",&saveptr)) {
+ char *eq = strchr(l,'=');
+ if (eq > l) {
+ k.assign(l,(unsigned long)(eq - l));
+ v.clear();
+ ++eq;
+ while (*eq) {
+ if (*eq == '\\') {
+ ++eq;
+ if (*eq) {
+ switch(*eq) {
+ case 'r': v.push_back('\r'); break;
+ case 'n': v.push_back('\n'); break;
+ case '0': v.push_back((char)0); break;
+ case 'e': v.push_back('='); break;
+ default: v.push_back(*eq); break;
+ }
+ ++eq;
+ }
+ } else {
+ v.push_back(*(eq++));
+ }
+ }
+ if ((k.length() > 0)&&(v.length() > 0))
+ d[k] = v;
+ }
+ }
+
+ const int64_t now = OSUtils::now();
+ OSUtils::ztsnprintf(id,sizeof(id),"%.10llx-%.16llx-%.10llx-%.4x",_signingId.address().toInt(),now,rt.origin,(unsigned int)(idCounter++ & 0xffff));
+ d["id"] = id;
+ d["objtype"] = "trace";
+ d["ts"] = now;
+ d["nodeId"] = Utils::hex10(rt.origin,tmp);
+ _db->save((nlohmann::json *)0,d);
+ } catch ( ... ) {
+ // drop invalid trace messages if an error occurs
+ }
+}
+
+void EmbeddedNetworkController::onNetworkUpdate(const uint64_t networkId)
+{
+ // Send an update to all members of the network that are online
+ const int64_t now = OSUtils::now();
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) {
+ if ((i->first.networkId == networkId)&&(i->second.online(now))&&(i->second.lastRequestMetaData))
+ request(networkId,InetAddress(),0,i->second.identity,i->second.lastRequestMetaData);
+ }
+}
+
+void EmbeddedNetworkController::onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId)
+{
+ // Push update to member if online
+ try {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _MemberStatus &ms = _memberStatus[_MemberStatusKey(networkId,memberId)];
+ if ((ms.online(OSUtils::now()))&&(ms.lastRequestMetaData))
+ request(networkId,InetAddress(),0,ms.identity,ms.lastRequestMetaData);
+ } catch ( ... ) {}
+}
+
+void EmbeddedNetworkController::onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId)
+{
+ const int64_t now = OSUtils::now();
+ Revocation rev((uint32_t)_node->prng(),networkId,0,now,ZT_REVOCATION_FLAG_FAST_PROPAGATE,Address(memberId),Revocation::CREDENTIAL_TYPE_COM);
+ rev.sign(_signingId);
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) {
+ if ((i->first.networkId == networkId)&&(i->second.online(now)))
+ _node->ncSendRevocation(Address(i->first.nodeId),rev);
+ }
+ }
+}
+
+void EmbeddedNetworkController::_request(
+ uint64_t nwid,
+ const InetAddress &fromAddr,
+ uint64_t requestPacketId,
+ const Identity &identity,
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData)
+{
+ char nwids[24];
+ DB::NetworkSummaryInfo ns;
+ json network,member,origMember;
+
+ if (!_db)
+ return;
+
+ if (((!_signingId)||(!_signingId.hasPrivate()))||(_signingId.address().toInt() != (nwid >> 24))||(!_sender))
+ return;
+
+ const int64_t now = OSUtils::now();
+
+ if (requestPacketId) {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())];
+ if ((now - ms.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
+ return;
+ ms.lastRequestTime = now;
+ }
+
+ _db->nodeIsOnline(nwid,identity.address().toInt(),fromAddr);
+
+ Utils::hex(nwid,nwids);
+ _db->get(nwid,network,identity.address().toInt(),member,ns);
+ if ((!network.is_object())||(network.size() == 0)) {
+ _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND);
+ return;
+ }
+ origMember = member;
+ const bool newMember = ((!member.is_object())||(member.size() == 0));
+ DB::initMember(member);
+
+ {
+ const std::string haveIdStr(OSUtils::jsonString(member["identity"],""));
+ if (haveIdStr.length() > 0) {
+ // If we already know this member's identity perform a full compare. This prevents
+ // a "collision" from being able to auth onto our network in place of an already
+ // known member.
+ try {
+ if (Identity(haveIdStr.c_str()) != identity) {
+ _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
+ return;
+ }
+ } catch ( ... ) {
+ _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
+ return;
+ }
+ } else {
+ // If we do not yet know this member's identity, learn it.
+ char idtmp[1024];
+ member["identity"] = identity.toString(false,idtmp);
+ }
+ }
+
+ // These are always the same, but make sure they are set
+ {
+ char tmpid[128];
+ const std::string addrs(identity.address().toString(tmpid));
+ member["id"] = addrs;
+ member["address"] = addrs;
+ member["nwid"] = nwids;
+ }
+
+ // Determine whether and how member is authorized
+ bool authorized = false;
+ bool autoAuthorized = false;
+ json autoAuthCredentialType,autoAuthCredential;
+ if (OSUtils::jsonBool(member["authorized"],false)) {
+ authorized = true;
+ } else if (!OSUtils::jsonBool(network["private"],true)) {
+ authorized = true;
+ autoAuthorized = true;
+ autoAuthCredentialType = "public";
+ } else {
+ char presentedAuth[512];
+ if (metaData.get(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH,presentedAuth,sizeof(presentedAuth)) > 0) {
+ presentedAuth[511] = (char)0; // sanity check
+ if ((strlen(presentedAuth) > 6)&&(!strncmp(presentedAuth,"token:",6))) {
+ const char *const presentedToken = presentedAuth + 6;
+ json authTokens(network["authTokens"]);
+ json &tokenExpires = authTokens[presentedToken];
+ if (tokenExpires.is_number()) {
+ if ((tokenExpires == 0)||(tokenExpires > now)) {
+ authorized = true;
+ autoAuthorized = true;
+ autoAuthCredentialType = "token";
+ autoAuthCredential = presentedToken;
+ }
+ }
+ }
+ }
+ }
+
+ // If we auto-authorized, update member record
+ if ((autoAuthorized)&&(authorized)) {
+ member["authorized"] = true;
+ member["lastAuthorizedTime"] = now;
+ member["lastAuthorizedCredentialType"] = autoAuthCredentialType;
+ member["lastAuthorizedCredential"] = autoAuthCredential;
+ }
+
+ if (authorized) {
+ // Update version info and meta-data if authorized and if this is a genuine request
+ if (requestPacketId) {
+ const uint64_t vMajor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0);
+ const uint64_t vMinor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0);
+ const uint64_t vRev = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0);
+ const uint64_t vProto = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,0);
+
+ member["vMajor"] = vMajor;
+ member["vMinor"] = vMinor;
+ member["vRev"] = vRev;
+ member["vProto"] = vProto;
+
+ {
+ std::lock_guard<std::mutex> l(_memberStatus_l);
+ _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,identity.address().toInt())];
+
+ ms.vMajor = (int)vMajor;
+ ms.vMinor = (int)vMinor;
+ ms.vRev = (int)vRev;
+ ms.vProto = (int)vProto;
+ ms.lastRequestMetaData = metaData;
+ ms.identity = identity;
+ }
+ }
+ } else {
+ // If they are not authorized, STOP!
+ DB::cleanMember(member);
+ _db->save(&origMember,member);
+ _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED);
+ return;
+ }
+
+ // -------------------------------------------------------------------------
+ // If we made it this far, they are authorized.
+ // -------------------------------------------------------------------------
+
+ int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ if (now > ns.mostRecentDeauthTime) {
+ // If we recently de-authorized a member, shrink credential TTL/max delta to
+ // be below the threshold required to exclude it. Cap this to a min/max to
+ // prevent jitter or absurdly large values.
+ const uint64_t deauthWindow = now - ns.mostRecentDeauthTime;
+ if (deauthWindow < ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA) {
+ credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA;
+ } else if (deauthWindow < (ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA + 5000ULL)) {
+ credentialtmd = deauthWindow - 5000ULL;
+ }
+ }
+
+ std::unique_ptr<NetworkConfig> nc(new NetworkConfig());
+
+ nc->networkId = nwid;
+ nc->type = OSUtils::jsonBool(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
+ nc->timestamp = now;
+ nc->credentialTimeMaxDelta = credentialtmd;
+ nc->revision = OSUtils::jsonInt(network["revision"],0ULL);
+ nc->issuedTo = identity.address();
+ if (OSUtils::jsonBool(network["enableBroadcast"],true)) nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
+ Utils::scopy(nc->name,sizeof(nc->name),OSUtils::jsonString(network["name"],"").c_str());
+ nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
+ nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
+
+ std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],""));
+ if (rtt.length() == 10) {
+ nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
+ nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(member["remoteTraceLevel"],0ULL);
+ } else {
+ rtt = OSUtils::jsonString(network["remoteTraceTarget"],"");
+ if (rtt.length() == 10) {
+ nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
+ } else {
+ nc->remoteTraceTarget.zero();
+ }
+ nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(network["remoteTraceLevel"],0ULL);
+ }
+
+ for(std::vector<Address>::const_iterator ab(ns.activeBridges.begin());ab!=ns.activeBridges.end();++ab)
+ nc->addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
+
+ json &v4AssignMode = network["v4AssignMode"];
+ json &v6AssignMode = network["v6AssignMode"];
+ json &ipAssignmentPools = network["ipAssignmentPools"];
+ json &routes = network["routes"];
+ json &rules = network["rules"];
+ json &capabilities = network["capabilities"];
+ json &tags = network["tags"];
+ json &memberCapabilities = member["capabilities"];
+ json &memberTags = member["tags"];
+
+ if (metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,0) <= 0) {
+ // Old versions with no rules engine support get an allow everything rule.
+ // Since rules are enforced bidirectionally, newer versions *will* still
+ // enforce rules on the inbound side.
+ nc->ruleCount = 1;
+ nc->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ } else {
+ if (rules.is_array()) {
+ for(unsigned long i=0;i<rules.size();++i) {
+ if (nc->ruleCount >= ZT_MAX_NETWORK_RULES)
+ break;
+ if (_parseRule(rules[i],nc->rules[nc->ruleCount]))
+ ++nc->ruleCount;
+ }
+ }
+
+ std::map< uint64_t,json * > capsById;
+ if (!memberCapabilities.is_array())
+ memberCapabilities = json::array();
+ if (capabilities.is_array()) {
+ for(unsigned long i=0;i<capabilities.size();++i) {
+ json &cap = capabilities[i];
+ if (cap.is_object()) {
+ const uint64_t id = OSUtils::jsonInt(cap["id"],0ULL) & 0xffffffffULL;
+ capsById[id] = &cap;
+ if ((newMember)&&(OSUtils::jsonBool(cap["default"],false))) {
+ bool have = false;
+ for(unsigned long i=0;i<memberCapabilities.size();++i) {
+ if (id == (OSUtils::jsonInt(memberCapabilities[i],0ULL) & 0xffffffffULL)) {
+ have = true;
+ break;
+ }
+ }
+ if (!have)
+ memberCapabilities.push_back(id);
+ }
+ }
+ }
+ }
+ for(unsigned long i=0;i<memberCapabilities.size();++i) {
+ const uint64_t capId = OSUtils::jsonInt(memberCapabilities[i],0ULL) & 0xffffffffULL;
+ std::map< uint64_t,json * >::const_iterator ctmp = capsById.find(capId);
+ if (ctmp != capsById.end()) {
+ json *cap = ctmp->second;
+ if ((cap)&&(cap->is_object())&&(cap->size() > 0)) {
+ ZT_VirtualNetworkRule capr[ZT_MAX_CAPABILITY_RULES];
+ unsigned int caprc = 0;
+ json &caprj = (*cap)["rules"];
+ if ((caprj.is_array())&&(caprj.size() > 0)) {
+ for(unsigned long j=0;j<caprj.size();++j) {
+ if (caprc >= ZT_MAX_CAPABILITY_RULES)
+ break;
+ if (_parseRule(caprj[j],capr[caprc]))
+ ++caprc;
+ }
+ }
+ nc->capabilities[nc->capabilityCount] = Capability((uint32_t)capId,nwid,now,1,capr,caprc);
+ if (nc->capabilities[nc->capabilityCount].sign(_signingId,identity.address()))
+ ++nc->capabilityCount;
+ if (nc->capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES)
+ break;
+ }
+ }
+ }
+
+ std::map< uint32_t,uint32_t > memberTagsById;
+ if (memberTags.is_array()) {
+ for(unsigned long i=0;i<memberTags.size();++i) {
+ json &t = memberTags[i];
+ if ((t.is_array())&&(t.size() == 2))
+ memberTagsById[(uint32_t)(OSUtils::jsonInt(t[0],0ULL) & 0xffffffffULL)] = (uint32_t)(OSUtils::jsonInt(t[1],0ULL) & 0xffffffffULL);
+ }
+ }
+ if (tags.is_array()) { // check network tags array for defaults that are not present in member tags
+ for(unsigned long i=0;i<tags.size();++i) {
+ json &t = tags[i];
+ if (t.is_object()) {
+ const uint32_t id = (uint32_t)(OSUtils::jsonInt(t["id"],0) & 0xffffffffULL);
+ json &dfl = t["default"];
+ if ((dfl.is_number())&&(memberTagsById.find(id) == memberTagsById.end())) {
+ memberTagsById[id] = (uint32_t)(OSUtils::jsonInt(dfl,0) & 0xffffffffULL);
+ json mt = json::array();
+ mt.push_back(id);
+ mt.push_back(dfl);
+ memberTags.push_back(mt); // add default to member tags if not present
+ }
+ }
+ }
+ }
+ for(std::map< uint32_t,uint32_t >::const_iterator t(memberTagsById.begin());t!=memberTagsById.end();++t) {
+ if (nc->tagCount >= ZT_MAX_NETWORK_TAGS)
+ break;
+ nc->tags[nc->tagCount] = Tag(nwid,now,identity.address(),t->first,t->second);
+ if (nc->tags[nc->tagCount].sign(_signingId))
+ ++nc->tagCount;
+ }
+ }
+
+ if (routes.is_array()) {
+ for(unsigned long i=0;i<routes.size();++i) {
+ if (nc->routeCount >= ZT_MAX_NETWORK_ROUTES)
+ break;
+ json &route = routes[i];
+ json &target = route["target"];
+ json &via = route["via"];
+ if (target.is_string()) {
+ const InetAddress t(target.get<std::string>().c_str());
+ InetAddress v;
+ if (via.is_string()) v.fromString(via.get<std::string>().c_str());
+ if ((t.ss_family == AF_INET)||(t.ss_family == AF_INET6)) {
+ ZT_VirtualNetworkRoute *r = &(nc->routes[nc->routeCount]);
+ *(reinterpret_cast<InetAddress *>(&(r->target))) = t;
+ if (v.ss_family == t.ss_family)
+ *(reinterpret_cast<InetAddress *>(&(r->via))) = v;
+ ++nc->routeCount;
+ }
+ }
+ }
+ }
+
+ const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false);
+
+ if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) {
+ if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());
+ nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ }
+ if ((OSUtils::jsonBool(v6AssignMode["6plane"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt());
+ nc->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ }
+ }
+
+ bool haveManagedIpv4AutoAssignment = false;
+ bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count
+ json ipAssignments = member["ipAssignments"]; // we want to make a copy
+ if (ipAssignments.is_array()) {
+ for(unsigned long i=0;i<ipAssignments.size();++i) {
+ if (ipAssignments[i].is_string()) {
+ const std::string ips = ipAssignments[i];
+ InetAddress ip(ips.c_str());
+
+ // IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
+ // this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
+ // of user error / poor UX.
+ int routedNetmaskBits = -1;
+ for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+ if ( (!nc->routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->containsAddress(ip)) )
+ routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->netmaskBits();
+ }
+
+ if (routedNetmaskBits >= 0) {
+ if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ ip.setPort(routedNetmaskBits);
+ nc->staticIps[nc->staticIpCount++] = ip;
+ }
+ if (ip.ss_family == AF_INET)
+ haveManagedIpv4AutoAssignment = true;
+ else if (ip.ss_family == AF_INET6)
+ haveManagedIpv6AutoAssignment = true;
+ }
+ }
+ }
+ } else {
+ ipAssignments = json::array();
+ }
+
+ if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(OSUtils::jsonBool(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!noAutoAssignIps) ) {
+ for(unsigned long p=0;((p<ipAssignmentPools.size())&&(!haveManagedIpv6AutoAssignment));++p) {
+ json &pool = ipAssignmentPools[p];
+ if (pool.is_object()) {
+ InetAddress ipRangeStart(OSUtils::jsonString(pool["ipRangeStart"],"").c_str());
+ InetAddress ipRangeEnd(OSUtils::jsonString(pool["ipRangeEnd"],"").c_str());
+ if ( (ipRangeStart.ss_family == AF_INET6) && (ipRangeEnd.ss_family == AF_INET6) ) {
+ uint64_t s[2],e[2],x[2],xx[2];
+ ZT_FAST_MEMCPY(s,ipRangeStart.rawIpData(),16);
+ ZT_FAST_MEMCPY(e,ipRangeEnd.rawIpData(),16);
+ s[0] = Utils::ntoh(s[0]);
+ s[1] = Utils::ntoh(s[1]);
+ e[0] = Utils::ntoh(e[0]);
+ e[1] = Utils::ntoh(e[1]);
+ x[0] = s[0];
+ x[1] = s[1];
+
+ for(unsigned int trialCount=0;trialCount<1000;++trialCount) {
+ if ((trialCount == 0)&&(e[1] > s[1])&&((e[1] - s[1]) >= 0xffffffffffULL)) {
+ // First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that.
+ xx[0] = Utils::hton(x[0]);
+ xx[1] = Utils::hton(x[1] + identity.address().toInt());
+ } else {
+ // Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway
+ Utils::getSecureRandom((void *)xx,16);
+ if ((e[0] > s[0]))
+ xx[0] %= (e[0] - s[0]);
+ else xx[0] = 0;
+ if ((e[1] > s[1]))
+ xx[1] %= (e[1] - s[1]);
+ else xx[1] = 0;
+ xx[0] = Utils::hton(x[0] + xx[0]);
+ xx[1] = Utils::hton(x[1] + xx[1]);
+ }
+
+ InetAddress ip6((const void *)xx,16,0);
+
+ // Check if this IP is within a local-to-Ethernet routed network
+ int routedNetmaskBits = 0;
+ for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+ if ( (!nc->routes[rk].via.ss_family) && (nc->routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->containsAddress(ip6)) )
+ routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc->routes[rk].target))->netmaskBits();
+ }
+
+ // If it's routed, then try to claim and assign it and if successful end loop
+ if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip6)) ) {
+ char tmpip[64];
+ const std::string ipStr(ip6.toIpString(tmpip));
+ if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) {
+ ipAssignments.push_back(ipStr);
+ member["ipAssignments"] = ipAssignments;
+ ip6.setPort((unsigned int)routedNetmaskBits);
+ if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
+ nc->staticIps[nc->staticIpCount++] = ip6;
+ haveManagedIpv6AutoAssignment = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( (ipAssignmentPools.is_array()) && ((v4AssignMode.is_object())&&(OSUtils::jsonBool(v4AssignMode["zt"],false))) && (!haveManagedIpv4AutoAssignment) && (!noAutoAssignIps) ) {
+ for(unsigned long p=0;((p<ipAssignmentPools.size())&&(!haveManagedIpv4AutoAssignment));++p) {
+ json &pool = ipAssignmentPools[p];
+ if (pool.is_object()) {
+ InetAddress ipRangeStartIA(OSUtils::jsonString(pool["ipRangeStart"],"").c_str());
+ InetAddress ipRangeEndIA(OSUtils::jsonString(pool["ipRangeEnd"],"").c_str());
+ if ( (ipRangeStartIA.ss_family == AF_INET) && (ipRangeEndIA.ss_family == AF_INET) ) {
+ uint32_t ipRangeStart = Utils::ntoh((uint32_t)(reinterpret_cast<struct sockaddr_in *>(&ipRangeStartIA)->sin_addr.s_addr));
+ uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast<struct sockaddr_in *>(&ipRangeEndIA)->sin_addr.s_addr));
+ if ((ipRangeEnd < ipRangeStart)||(ipRangeStart == 0))
+ continue;
+ uint32_t ipRangeLen = ipRangeEnd - ipRangeStart;
+
+ // Start with the LSB of the member's address
+ uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff);
+
+ for(uint32_t k=ipRangeStart,trialCount=0;((k<=ipRangeEnd)&&(trialCount < 1000));++k,++trialCount) {
+ uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart;
+ ++ipTrialCounter;
+ if ((ip & 0x000000ff) == 0x000000ff)
+ continue; // don't allow addresses that end in .255
+
+ // Check if this IP is within a local-to-Ethernet routed network
+ int routedNetmaskBits = -1;
+ for(unsigned int rk=0;rk<nc->routeCount;++rk) {
+ if (nc->routes[rk].target.ss_family == AF_INET) {
+ uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc->routes[rk].target))->sin_addr.s_addr));
+ int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc->routes[rk].target))->sin_port));
+ if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) {
+ routedNetmaskBits = targetBits;
+ break;
+ }
+ }
+ }
+
+ // If it's routed, then try to claim and assign it and if successful end loop
+ const InetAddress ip4(Utils::hton(ip),0);
+ if ( (routedNetmaskBits > 0) && (!std::binary_search(ns.allocatedIps.begin(),ns.allocatedIps.end(),ip4)) ) {
+ char tmpip[64];
+ const std::string ipStr(ip4.toIpString(tmpip));
+ if (std::find(ipAssignments.begin(),ipAssignments.end(),ipStr) == ipAssignments.end()) {
+ ipAssignments.push_back(ipStr);
+ member["ipAssignments"] = ipAssignments;
+ if (nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc->staticIps[nc->staticIpCount++]));
+ v4ip->sin_family = AF_INET;
+ v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
+ v4ip->sin_addr.s_addr = Utils::hton(ip);
+ }
+ haveManagedIpv4AutoAssignment = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Issue a certificate of ownership for all static IPs
+ if (nc->staticIpCount) {
+ nc->certificatesOfOwnership[0] = CertificateOfOwnership(nwid,now,identity.address(),1);
+ for(unsigned int i=0;i<nc->staticIpCount;++i)
+ nc->certificatesOfOwnership[0].addThing(nc->staticIps[i]);
+ nc->certificatesOfOwnership[0].sign(_signingId);
+ nc->certificateOfOwnershipCount = 1;
+ }
+
+ CertificateOfMembership com(now,credentialtmd,nwid,identity.address());
+ if (com.sign(_signingId)) {
+ nc->com = com;
+ } else {
+ _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ DB::cleanMember(member);
+ _db->save(&origMember,member);
+ _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6);
+}
+
+void EmbeddedNetworkController::_startThreads()
+{
+ std::lock_guard<std::mutex> l(_threads_l);
+ if (!_threads.empty())
+ return;
+ const long hwc = std::max((long)std::thread::hardware_concurrency(),(long)1);
+ for(long t=0;t<hwc;++t) {
+ _threads.emplace_back([this]() {
+ for(;;) {
+ _RQEntry *qe = (_RQEntry *)0;
+ if (!_queue.get(qe))
+ break;
+ try {
+ if (qe) {
+ _request(qe->nwid,qe->fromAddr,qe->requestPacketId,qe->identity,qe->metaData);
+ delete qe;
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"ERROR: exception in controller request handling thread: %s" ZT_EOL_S,e.what());
+ } catch ( ... ) {
+ fprintf(stderr,"ERROR: exception in controller request handling thread: unknown exception" ZT_EOL_S);
+ }
+ }
+ });
+ }
+}
+
+} // namespace ZeroTier
diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp
new file mode 100644
index 00000000..417005a4
--- /dev/null
+++ b/controller/EmbeddedNetworkController.hpp
@@ -0,0 +1,159 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_SQLITENETWORKCONTROLLER_HPP
+#define ZT_SQLITENETWORKCONTROLLER_HPP
+
+#include <stdint.h>
+
+#include <string>
+#include <map>
+#include <vector>
+#include <set>
+#include <list>
+#include <thread>
+#include <unordered_map>
+#include <atomic>
+
+#include "../node/Constants.hpp"
+#include "../node/NetworkController.hpp"
+#include "../node/Utils.hpp"
+#include "../node/Address.hpp"
+#include "../node/InetAddress.hpp"
+
+#include "../osdep/OSUtils.hpp"
+#include "../osdep/Thread.hpp"
+#include "../osdep/BlockingQueue.hpp"
+
+#include "../ext/json/json.hpp"
+
+#include "DB.hpp"
+#include "FileDB.hpp"
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+#include "RethinkDB.hpp"
+#endif
+
+namespace ZeroTier {
+
+class Node;
+
+class EmbeddedNetworkController : public NetworkController
+{
+public:
+ /**
+ * @param node Parent node
+ * @param dbPath Database path (file path or database credentials)
+ */
+ EmbeddedNetworkController(Node *node,const char *dbPath);
+ virtual ~EmbeddedNetworkController();
+
+ virtual void init(const Identity &signingId,Sender *sender);
+
+ virtual void request(
+ uint64_t nwid,
+ const InetAddress &fromAddr,
+ uint64_t requestPacketId,
+ const Identity &identity,
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
+
+ unsigned int handleControlPlaneHttpGET(
+ const std::vector<std::string> &path,
+ const std::map<std::string,std::string> &urlArgs,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType);
+ unsigned int handleControlPlaneHttpPOST(
+ const std::vector<std::string> &path,
+ const std::map<std::string,std::string> &urlArgs,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType);
+ unsigned int handleControlPlaneHttpDELETE(
+ const std::vector<std::string> &path,
+ const std::map<std::string,std::string> &urlArgs,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType);
+
+ void handleRemoteTrace(const ZT_RemoteTrace &rt);
+
+ // Called on update via POST or by JSONDB on external update of network or network member records
+ void onNetworkUpdate(const uint64_t networkId);
+ void onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId);
+ void onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId);
+
+private:
+ void _request(uint64_t nwid,const InetAddress &fromAddr,uint64_t requestPacketId,const Identity &identity,const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData);
+ void _startThreads();
+
+ struct _RQEntry
+ {
+ uint64_t nwid;
+ uint64_t requestPacketId;
+ InetAddress fromAddr;
+ Identity identity;
+ Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData;
+ enum {
+ RQENTRY_TYPE_REQUEST = 0
+ } type;
+ };
+ struct _MemberStatusKey
+ {
+ _MemberStatusKey() : networkId(0),nodeId(0) {}
+ _MemberStatusKey(const uint64_t nwid,const uint64_t nid) : networkId(nwid),nodeId(nid) {}
+ uint64_t networkId;
+ uint64_t nodeId;
+ inline bool operator==(const _MemberStatusKey &k) const { return ((k.networkId == networkId)&&(k.nodeId == nodeId)); }
+ };
+ struct _MemberStatus
+ {
+ _MemberStatus() : lastRequestTime(0),vMajor(-1),vMinor(-1),vRev(-1),vProto(-1) {}
+ uint64_t lastRequestTime;
+ int vMajor,vMinor,vRev,vProto;
+ Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> lastRequestMetaData;
+ Identity identity;
+ inline bool online(const int64_t now) const { return ((now - lastRequestTime) < (ZT_NETWORK_AUTOCONF_DELAY * 2)); }
+ };
+ struct _MemberStatusHash
+ {
+ inline std::size_t operator()(const _MemberStatusKey &networkIdNodeId) const
+ {
+ return (std::size_t)(networkIdNodeId.networkId + networkIdNodeId.nodeId);
+ }
+ };
+
+ const int64_t _startTime;
+ Node *const _node;
+ std::string _path;
+ Identity _signingId;
+ std::string _signingIdAddressString;
+ NetworkController::Sender *_sender;
+ std::unique_ptr<DB> _db;
+ BlockingQueue< _RQEntry * > _queue;
+ std::vector<std::thread> _threads;
+ std::mutex _threads_l;
+ std::unordered_map< _MemberStatusKey,_MemberStatus,_MemberStatusHash > _memberStatus;
+ std::mutex _memberStatus_l;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp
new file mode 100644
index 00000000..a7b59cbf
--- /dev/null
+++ b/controller/FileDB.cpp
@@ -0,0 +1,150 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "FileDB.hpp"
+
+namespace ZeroTier
+{
+
+FileDB::FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
+ DB(nc,myId,path),
+ _networksPath(_path + ZT_PATH_SEPARATOR_S + "network"),
+ _tracePath(_path + ZT_PATH_SEPARATOR_S + "trace")
+{
+ OSUtils::mkdir(_path.c_str());
+ OSUtils::lockDownFile(_path.c_str(),true);
+ OSUtils::mkdir(_networksPath.c_str());
+ OSUtils::mkdir(_tracePath.c_str());
+
+ std::vector<std::string> networks(OSUtils::listDirectory(_networksPath.c_str(),false));
+ std::string buf;
+ for(auto n=networks.begin();n!=networks.end();++n) {
+ buf.clear();
+ if ((n->length() == 21)&&(OSUtils::readFile((_networksPath + ZT_PATH_SEPARATOR_S + *n).c_str(),buf))) {
+ try {
+ nlohmann::json network(OSUtils::jsonParse(buf));
+ const std::string nwids = network["id"];
+ if (nwids.length() == 16) {
+ nlohmann::json nullJson;
+ _networkChanged(nullJson,network,false);
+ std::string membersPath(_networksPath + ZT_PATH_SEPARATOR_S + nwids + ZT_PATH_SEPARATOR_S "member");
+ std::vector<std::string> members(OSUtils::listDirectory(membersPath.c_str(),false));
+ for(auto m=members.begin();m!=members.end();++m) {
+ buf.clear();
+ if ((m->length() == 15)&&(OSUtils::readFile((membersPath + ZT_PATH_SEPARATOR_S + *m).c_str(),buf))) {
+ try {
+ nlohmann::json member(OSUtils::jsonParse(buf));
+ const std::string addrs = member["id"];
+ if (addrs.length() == 10) {
+ nlohmann::json nullJson2;
+ _memberChanged(nullJson2,member,false);
+ }
+ } catch ( ... ) {}
+ }
+ }
+ }
+ } catch ( ... ) {}
+ }
+ }
+}
+
+FileDB::~FileDB()
+{
+}
+
+bool FileDB::waitForReady()
+{
+ return true;
+}
+
+void FileDB::save(nlohmann::json *orig,nlohmann::json &record)
+{
+ char p1[4096],p2[4096],pb[4096];
+ try {
+ if (orig) {
+ if (*orig != record) {
+ record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1;
+ }
+ } else {
+ record["revision"] = 1;
+ }
+
+ const std::string objtype = record["objtype"];
+ if (objtype == "network") {
+ const uint64_t nwid = OSUtils::jsonIntHex(record["id"],0ULL);
+ if (nwid) {
+ nlohmann::json old;
+ get(nwid,old);
+
+ OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json.new",_networksPath.c_str(),nwid);
+ OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),nwid);
+ if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)))
+ fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1);
+ OSUtils::rename(p1,p2);
+
+ _networkChanged(old,record,true);
+ }
+ } else if (objtype == "member") {
+ const uint64_t id = OSUtils::jsonIntHex(record["id"],0ULL);
+ const uint64_t nwid = OSUtils::jsonIntHex(record["nwid"],0ULL);
+ if ((id)&&(nwid)) {
+ nlohmann::json network,old;
+ get(nwid,network,id,old);
+
+ OSUtils::ztsnprintf(pb,sizeof(pb),"%s" ZT_PATH_SEPARATOR_S "%.16llx" ZT_PATH_SEPARATOR_S "member",_networksPath.c_str(),(unsigned long long)nwid);
+ OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json.new",pb,(unsigned long long)id);
+ OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.10llx.json",pb,(unsigned long long)id);
+ if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) {
+ OSUtils::mkdir(pb);
+ if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)))
+ fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1);
+ }
+ OSUtils::rename(p1,p2);
+
+ _memberChanged(old,record,true);
+ }
+ } else if (objtype == "trace") {
+ const std::string id = record["id"];
+ if (id.length() > 0) {
+ OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%s.json",_tracePath.c_str(),id.c_str());
+ OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1));
+ }
+ }
+ } catch ( ... ) {} // drop invalid records missing fields
+}
+
+void FileDB::eraseNetwork(const uint64_t networkId)
+{
+ nlohmann::json network,nullJson;
+ get(networkId,network);
+ char p[16384];
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),networkId);
+ OSUtils::rm(p);
+ _networkChanged(network,nullJson,true);
+}
+
+void FileDB::eraseMember(const uint64_t networkId,const uint64_t memberId)
+{
+}
+
+void FileDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress)
+{
+ // Nothing to do here right now in the filesystem store mode since we can just get this from the peer list
+}
+
+} // namespace ZeroTier
diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp
new file mode 100644
index 00000000..1e275a36
--- /dev/null
+++ b/controller/FileDB.hpp
@@ -0,0 +1,46 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ZT_CONTROLLER_FILEDB_HPP
+#define ZT_CONTROLLER_FILEDB_HPP
+
+#include "DB.hpp"
+
+namespace ZeroTier
+{
+
+class FileDB : public DB
+{
+public:
+ FileDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
+ virtual ~FileDB();
+
+ virtual bool waitForReady();
+ virtual void save(nlohmann::json *orig,nlohmann::json &record);
+ virtual void eraseNetwork(const uint64_t networkId);
+ virtual void eraseMember(const uint64_t networkId,const uint64_t memberId);
+ virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress);
+
+protected:
+ std::string _networksPath;
+ std::string _tracePath;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/controller/README.md b/controller/README.md
index 8b789a3e..23bd931d 100644
--- a/controller/README.md
+++ b/controller/README.md
@@ -1,66 +1,47 @@
Network Controller Microservice
======
-ZeroTier's 16-digit network IDs are really just a concatenation of the 10-digit ZeroTier address of a network controller followed by a 6-digit (24-bit) network number on that controller. Fans of software defined networking will recognize this as a variation of the familiar [separation of data plane and control plane](http://sdntutorials.com/difference-between-control-plane-and-data-plane/) SDN design pattern.
+Every ZeroTier virtual network has a *network controller* responsible for admitting members to the network, issuing certificates, and issuing default configuration information.
-This code implements the *node/NetworkController.hpp* interface and provides a SQLite3-backed network controller microservice. Including it in the build allows ZeroTier One to act as a controller and create/manage networks.
+This is our reference controller implementation and is the same one we use to power our own hosted services at [my.zerotier.com](https://my.zerotier.com/). As of ZeroTier One version 1.2.0 this code is included in normal builds for desktop, laptop, and server (Linux, etc.) targets.
-This is the same code we use to run [my.zerotier.com](https://my.zerotier.com/), which is a web UI and API that runs in front of a pool of controllers.
+Controller data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, rsync'd, placed in `git`, etc. The files under `controller.d` should not be modified in place while the controller is running or data loss may result, and if they are edited directly take care not to save corrupt JSON since that can also lead to data loss when the controller is restarted. Going through the API is strongly preferred to directly modifying these files.
-### Building
+See the API section below for information about controlling the controller.
-On Linux, Mac, or BSD you can create a controller-enabled build with:
+### Scalability and Reliability
- make ZT_ENABLE_NETWORK_CONTROLLER=1
+Controllers can in theory host up to 2^24 networks and serve many millions of devices (or more), but we recommend spreading large numbers of networks across many controllers for load balancing and fault tolerance reasons. Since the controller uses the filesystem as its data store we recommend fast filesystems and fast SSD drives for heavily loaded controllers.
-You will need the development headers and libraries for SQLite3 installed.
-
-### Running
-
-After building and installing (`make install`) a controller-enabled build of ZeroTier One, start it and try:
-
- sudo zerotier-cli /controller
-
-You should see something like:
-
- {
- "controller": true,
- "apiVersion": 2,
- "clock": 1468002975497,
- "instanceId": "8ab354604debe1da27ee627c9ef94a48"
- }
-
-When started, a controller-enabled build of ZeroTier One will automatically create and initialize a `controller.db` file in its home folder. This is where all the controller's data and persistent state lives. If you're upgrading an old controller it will upgrade its database schema automatically on first launch. Make a backup of the old controller's database first since you can't go backward.
-
-Controllers periodically make backups of their database as `controller.db.backup`. This is done so that this file can be more easily copied/rsync'ed to other systems without worrying about corruption. SQLite3 supports multiple processes accessing the same database file, so `sqlite3 /path/to/controller.db .dump` also works but can be slow on a busy controller.
-
-Controllers can in theory host up to 2^24 networks and serve many millions of devices (or more), but we recommend running multiple controllers for a lot of networks to spread load and be more fault tolerant.
+Since ZeroTier nodes are mobile and do not need static IPs, implementing high availability fail-over for controllers is easy. Just replicate their working directories from master to backup and have something automatically fire up the backup if the master goes down. Modern orchestration tools like Nomad and Kubernetes can be of help here.
### Dockerizing Controllers
ZeroTier network controllers can easily be run in Docker or other container systems. Since containers do not need to actually join networks, extra privilege options like "--device=/dev/net/tun --privileged" are not needed. You'll just need to map the local JSON API port of the running controller and allow it to access the Internet (over UDP/9993 at a minimum) so things can reach and query it.
-### Implementing High Availability Fail-Over
+### RethinkDB Database Implementation
-ZeroTier network controllers are not single points of failure for networks-- in the sense that if a controller goes down *existing* members of a network can continue to communicate. But new members (or those that have been offline for a while) can't join, existing members can't be de-authorized, and other changes to the network's configuration can't be made. This means that short "glitches" in controller availability are not a major problem but long periods of unavailability can be.
+The default controller stores its data in the filesystem in `controller.d` under ZeroTier's home folder. There's an alternative implementation that stores data in RethinkDB that can be built with `make central-controller`. Right now this is only guaranteed to build and run on Linux and is designed for use with [ZeroTier Central](https://my.zerotier.com/). You're welcome to use it but we don't "officially" support it for end-user use and it could change at any time.
-Because controllers are just regular ZeroTier nodes and controller queries are in-band, controllers can trivially be moved without worrying about changes to underlying physical IPs. This makes high-availability fail-over very easy to implement.
+### Upgrading from Older (1.1.14 or earlier) Versions
-Just set up two cloud hosts, preferably in different data centers (e.g. two different AWS regions or Digital Ocean SF and NYC). Now set up the hot spare controller to constantly mirror `controller.db.backup` from its active sibling.
+Older versions of this code used a SQLite database instead of in-filesystem JSON. A migration utility called `migrate-sqlite` is included here and *must* be used to migrate this data to the new format. If the controller is started with an old `controller.db` in its working directory it will terminate after printing an error to *stderr*. This is done to prevent "surprises" for those running DIY controllers using the old code.
-If the active controller goes down, rename `controller.db.backup` to `controller.db` on the hot spare and start the ZeroTier One service there. The spare will take over and has now become the active controller. If the original active node comes back, it should take on the role of spare and should not start its service. Instead it should start mirroring the active controller's backup and wait until it is needed.
+The migration tool is written in nodeJS and can be used like this:
-The details of actually implementing this kind of HA fail-over on Linux or other OSes are beyond the scope of these docs and there are many ways to do it. Docker orchestration tools like Kubernetes can also be used to accomplish this if you've dockerized your controller.
+ cd migrate-sqlite
+ npm install
+ node migrate.js </path/to/controller.db> </path/to/controller.d>
### Network Controller API
The controller API is hosted via the same JSON API endpoint that ZeroTier One uses for local control (usually at 127.0.0.1 port 9993). All controller options are routed under the `/controller` base path.
-The controller microservice does not implement any fine-grained access control (authentication is via authtoken.secret just like the regular JSON API) or other complex mangement features. It just takes network and network member configurations and reponds to controller queries. We have an enterprise product called [ZeroTier Central](https://my.zerotier.com/) that we host as a service (and that companies can license to self-host) that does this.
+The controller microservice itself does not implement any fine-grained access control. Access control is via the ZeroTier control interface itself and `authtoken.secret`. This can be sent as the `X-ZT1-Auth` HTTP header field or appended to the URL as `?auth=<token>`. Take care when doing the latter that request URLs are not being logged.
-All working network IDs on a controller must begin with the controller's ZeroTier address. The API will *allow* "foreign" networks to be added but the controller will have no way of doing anything with them.
+While networks with any valid ID can be added to the controller's database, it will only actually work to control networks whose first 10 hex digits correspond with the network controller's ZeroTier ID. See [section 2.2.1 of the ZeroTier manual](https://zerotier.com/manual.shtml#2_2_1).
-Also note that the API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrectly typed and unrecognized fields are just ignored.
+The controller JSON API is *very* sensitive about types. Integers must be integers and strings strings, etc. Incorrect types may be ignored, set to default values, or set to undefined values.
#### `/controller`
@@ -71,11 +52,8 @@ Also note that the API is *very* sensitive about types. Integers must be integer
| Field | Type | Description | Writable |
| ------------------ | ----------- | ------------------------------------------------- | -------- |
| controller | boolean | Always 'true' | no |
-| apiVersion | integer | Controller API version, currently 2 | no |
+| apiVersion | integer | Controller API version, currently 3 | no |
| clock | integer | Current clock on controller, ms since epoch | no |
-| instanceId | string | A random ID generated on first controller DB init | no |
-
-The instance ID can be used to check whether a controller's database has been reset or otherwise switched.
#### `/controller/network`
@@ -95,54 +73,54 @@ By making queries to this path you can create, configure, and delete networks. D
When POSTing new networks take care that their IDs are not in use, otherwise you may overwrite an existing one. To create a new network with a random unused ID, POST to `/controller/network/##########______`. The #'s are the controller's 10-digit ZeroTier address and they're followed by six underscores. Check the `nwid` field of the returned JSON object for your network's newly allocated ID. Subsequent POSTs to this network must refer to its actual path.
+Example:
+
+`curl -X POST --header "X-ZT1-Auth: secret" -d '{"name":"my network"}' http://localhost:9993/controller/network/305f406058______`
+
+**Network object format:**
+
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
-| nwid | string | 16-digit network ID | no |
-| controllerInstanceId | string | Controller database instance ID | no |
-| clock | integer | Current clock, ms since epoch | no |
+| id | string | 16-digit network ID | no |
+| nwid | string | 16-digit network ID (legacy) | no |
+| objtype | string | Always "network" | no |
| name | string | A short name for this network | YES |
+| creationTime | integer | Time network record was created (ms since epoch) | no |
| private | boolean | Is access control enabled? | YES |
| enableBroadcast | boolean | Ethernet ff:ff:ff:ff:ff:ff allowed? | YES |
-| allowPassiveBridging | boolean | Allow any member to bridge (very experimental) | YES |
-| v4AssignMode | string | If 'zt', auto-assign IPv4 from pool(s) | YES |
-| v6AssignMode | string | IPv6 address auto-assign modes; see below | YES |
+| v4AssignMode | object | IPv4 management and assign options (see below) | YES |
+| v6AssignMode | object | IPv6 management and assign options (see below) | YES |
+| mtu | integer | Network MTU (default: 2800) | YES |
| multicastLimit | integer | Maximum recipients for a multicast packet | YES |
| creationTime | integer | Time network was first created | no |
| revision | integer | Network config revision counter | no |
-| memberRevisionCounter | integer | Network member revision counter | no |
-| authorizedMemberCount | integer | Number of authorized members (for private nets) | no |
-| relays | array[object] | Alternative relays; see below | YES |
| routes | array[object] | Managed IPv4 and IPv6 routes; see below | YES |
| ipAssignmentPools | array[object] | IP auto-assign ranges; see below | YES |
| rules | array[object] | Traffic rules; see below | YES |
+| capabilities | array[object] | Array of capability objects (see below) | YES |
+| tags | array[object] | Array of tag objects (see below) | YES |
+| remoteTraceTarget | string | 10-digit ZeroTier ID of remote trace target | YES |
+| remoteTraceLevel | integer | Remote trace verbosity level | YES |
-(The `ipLocalRoutes` field appeared in older versions but is no longer present. Routes will now show up in `routes`.)
+ * Networks without rules won't carry any traffic. If you don't specify any on network creation an "accept anything" rule set will automatically be added.
+ * Managed IP address assignments and IP assignment pools that do not fall within a route configured in `routes` are ignored and won't be used or sent to members.
+ * The default for `private` is `true` and this is probably what you want. Turning `private` off means *anyone* can join your network with only its 16-digit network ID. It's also impossible to de-authorize a member as these networks don't issue or enforce certificates. Such "party line" networks are used for decentralized app backplanes, gaming, and testing but are otherwise not common.
+ * Changing the MTU can be disruptive and on some operating systems may require a leave/rejoin of the network or a restart of the ZeroTier service.
-Two important things to know about networks:
+**Auto-Assign Modes:**
- - Networks without rules won't carry any traffic. See below for an example with rules to permit IPv4 and IPv6.
- - Managed IP address assignments and IP assignment pools that do not fall within a route configured in `routes` are ignored and won't be used or sent to members.
- - The default for `private` is `true` and this is probably what you want. Turning `private` off means *anyone* can join your network with only its 16-digit network ID. It's also impossible to de-authorize a member as these networks don't issue or enforce certificates. Such "party line" networks are used for decentralized app backplanes, gaming, and testing but are not common in ordinary use.
+Auto assign modes (`v4AssignMode` and `v6AssignMode`) contain objects that map assignment modes to booleans.
-**IPv6 Auto-Assign Modes:**
+For IPv4 the only valid setting is `zt` which, if true, causes IPv4 addresses to be auto-assigned from `ipAssignmentPools` to members that do not have an IPv4 assignment. Note that active bridges are exempt and will not get auto-assigned IPs since this can interfere with bridging. (You can still manually assign one if you want.)
-This field is (for legacy reasons) a comma-delimited list of strings. These can be `rfc4193`, `6plane`, and `zt`. RFC4193 and 6PLANE are special addressing modes that deterministically assign IPv6 addresses based on the network ID and the ZeroTier address of each member. The `zt` mode enables IPv6 auto-assignment from arbitrary IPv6 IP ranges configured in `ipAssignmentPools`.
-
-**Relay object format:**
-
-Relay objects define network-specific preferred relay nodes. Traffic to peers on this network will preferentially use these relays if they are available, and otherwise will fall back to the global rootserver infrastructure.
-
-| Field | Type | Description | Writable |
-| --------------------- | ------------- | ------------------------------------------------- | -------- |
-| address | string | 10-digit ZeroTier address of relay | YES |
-| phyAddress | string | Optional IP/port suggestion for finding relay | YES |
+IPv6 includes this option and two others: `6plane` and `rfc4193`. These assign private IPv6 addresses to each member based on a deterministic assignment scheme that allows members to emulate IPv6 NDP to skip multicast for better performance and scalability. The `rfc4193` mode gives every member a /128 on a /88 network, while `6plane` gives every member a /80 within a /40 network but uses NDP emulation to route *all* IPs under that /80 to its owner. The `6plane` mode is great for use cases like Docker since it allows every member to assign IPv6 addresses within its /80 that just work instantly and globally across the network.
**IP assignment pool object format:**
-| Field | Type | Description | Writable |
-| --------------------- | ------------- | ------------------------------------------------- | -------- |
-| ipRangeStart | string | Starting IP address in range | YES |
-| ipRangeEnd | string | Ending IP address in range (inclusive) | YES |
+| Field | Type | Description |
+| --------------------- | ------------- | ------------------------------------------------- |
+| ipRangeStart | string | Starting IP address in range |
+| ipRangeEnd | string | Ending IP address in range (inclusive) |
Pools are only used if auto-assignment is on for the given address type (IPv4 or IPv6) and if the entire range falls within a managed route.
@@ -159,57 +137,68 @@ That defines a range within network `fd00:feed:feed:beef::/64` that contains up
**Rule object format:**
-Rules are matched in order of ruleNo. If no rules match, the default action is `drop`. To allow all traffic, create a single rule with all *null* fields and an action of `accept`.
+Each rule is actually a sequence of zero or more `MATCH_` entries in the rule array followed by an `ACTION_` entry that describes what to do if all the preceding entries match. An `ACTION_` without any preceding `MATCH_` entries is always taken, so setting a single `ACTION_ACCEPT` rule yields a network that allows all traffic. If no rules are present the default action is `ACTION_DROP`.
-In the future there will be many, many more types of rules. As of today only filtering by Ethernet packet type is supported.
+Rules are evaluated in the order in which they appear in the array. There is currently a limit of 256 entries per network. Capabilities should be used if a larger and more complex rule set is needed since they allow rules to be grouped by purpose and only shipped to members that need them.
-| Field | Type | Description | Writable |
-| --------------------- | ------------- | ------------------------------------------------- | -------- |
-| ruleNo | integer | Rule sorting key | YES |
-| etherType | integer | Ethernet frame type (e.g. 34525 for IPv6) | YES |
-| action | string | Currently either `allow` or `drop` | YES |
-
-**An Example: The Configuration for Earth**
+Each rule table entry has two common fields.
-Here is an example of a correctly configured ZeroTier network with IPv4 auto-assigned addresses from 28.0.0.0/7 (a "de-facto private" space) and RFC4193 IPv6 addressing. Users might recognize this as *Earth*, our public "global LAN party" that's used for demos and testing and occasionally gaming.
+| Field | Type | Description |
+| --------------------- | ------------- | ------------------------------------------------- |
+| type | string | Entry type (all caps, case sensitive) |
+| not | boolean | If true, MATCHes match if they don't match |
-For your own networks you'll probably want to change `private` to `true` unless you like company. These rules on the other hand probably are what you want. These allow IPv4, IPv4 ARP, and IPv6 Ethernet frames. To allow only IPv4 omit the one for Ethernet type 34525 (IPv6).
+The following fields may or may not be present depending on rule type:
- {
- "nwid": "8056c2e21c000001",
- "controllerInstanceId": "8ab354604debe1da27ee627c9ef94a48",
- "clock": 1468004857100,
- "name": "earth.zerotier.net",
- "private": false,
- "enableBroadcast": false,
- "allowPassiveBridging": false,
- "v4AssignMode": "zt",
- "v6AssignMode": "rfc4193",
- "multicastLimit": 64,
- "creationTime": 1442292573165,
- "revision": 234,
- "memberRevisionCounter": 3326,
- "authorizedMemberCount": 2873,
- "relays": [],
- "routes": [
- {"target":"28.0.0.0/7","via":null,"flags":0,"metric":0}],
- "ipAssignmentPools": [
- {"ipRangeStart":"28.0.0.1","ipRangeEnd":"29.255.255.254"}],
- "rules": [
- {
- "ruleNo": 20,
- "etherType": 2048,
- "action": "accept"
- },{
- "ruleNo": 21,
- "etherType": 2054,
- "action": "accept"
- },{
- "ruleNo": 30,
- "etherType": 34525,
- "action": "accept"
- }]
- }
+| Field | Type | Description |
+| --------------------- | ------------- | ------------------------------------------------- |
+| zt | string | 10-digit hex ZeroTier address |
+| etherType | integer | Ethernet frame type |
+| mac | string | Hex MAC address (with or without :'s) |
+| ip | string | IPv4 or IPv6 address |
+| ipTos | integer | IP type of service |
+| ipProtocol | integer | IP protocol (e.g. TCP) |
+| start | integer | Start of an integer range (e.g. port range) |
+| end | integer | End of an integer range (inclusive) |
+| id | integer | Tag ID |
+| value | integer | Tag value or comparison value |
+| mask | integer | Bit mask (for characteristics flags) |
+
+The entry types and their additional fields are:
+
+| Entry type | Description | Fields |
+| ------------------------------- | ----------------------------------------------------------------- | -------------- |
+| `ACTION_DROP` | Drop any packets matching this rule | (none) |
+| `ACTION_ACCEPT` | Accept any packets matching this rule | (none) |
+| `ACTION_TEE` | Send a copy of this packet to a node (rule parsing continues) | `zt` |
+| `ACTION_REDIRECT` | Redirect this packet to another node | `zt` |
+| `ACTION_DEBUG_LOG` | Output debug info on match (if built with rules engine debug) | (none) |
+| `MATCH_SOURCE_ZEROTIER_ADDRESS` | Match VL1 ZeroTier address of packet sender. | `zt` |
+| `MATCH_DEST_ZEROTIER_ADDRESS` | Match VL1 ZeroTier address of recipient | `zt` |
+| `MATCH_ETHERTYPE` | Match Ethernet frame type | `etherType` |
+| `MATCH_MAC_SOURCE` | Match source Ethernet MAC address | `mac` |
+| `MATCH_MAC_DEST` | Match destination Ethernet MAC address | `mac` |
+| `MATCH_IPV4_SOURCE` | Match source IPv4 address | `ip` |
+| `MATCH_IPV4_DEST` | Match destination IPv4 address | `ip` |
+| `MATCH_IPV6_SOURCE` | Match source IPv6 address | `ip` |
+| `MATCH_IPV6_DEST` | Match destination IPv6 address | `ip` |
+| `MATCH_IP_TOS` | Match IP TOS field | `ipTos` |
+| `MATCH_IP_PROTOCOL` | Match IP protocol field | `ipProtocol` |
+| `MATCH_IP_SOURCE_PORT_RANGE` | Match a source IP port range | `start`,`end` |
+| `MATCH_IP_DEST_PORT_RANGE` | Match a destination IP port range | `start`,`end` |
+| `MATCH_CHARACTERISTICS` | Match on characteristics flags | `mask`,`value` |
+| `MATCH_FRAME_SIZE_RANGE` | Match a range of Ethernet frame sizes | `start`,`end` |
+| `MATCH_TAGS_SAMENESS` | Match if both sides' tags differ by no more than value | `id`,`value` |
+| `MATCH_TAGS_BITWISE_AND` | Match if both sides' tags AND to value | `id`,`value` |
+| `MATCH_TAGS_BITWISE_OR` | Match if both sides' tags OR to value | `id`,`value` |
+| `MATCH_TAGS_BITWISE_XOR` | Match if both sides' tags XOR to value | `id`,`value` |
+
+Important notes about rules engine behavior:
+
+ * IPv4 and IPv6 IP address rules do not match for frames that are not IPv4 or IPv6 respectively.
+ * `ACTION_DEBUG_LOG` is a no-op on nodes not built with `ZT_RULES_ENGINE_DEBUGGING` enabled (see Network.cpp). If that is enabled nodes will dump a trace of rule evaluation results to *stdout* when this action is encountered but will otherwise keep evaluating rules. This is used for basic "smoke testing" of the rules engine.
+ * Multicast packets and packets destined for bridged devices treated a little differently. They are matched more than once. They are matched at the point of send with a NULL ZeroTier destination address, meaning that `MATCH_DEST_ZEROTIER_ADDRESS` is useless. That's because the true VL1 destination is not yet known. Then they are matched again for each true VL1 destination. On these later subsequent matches TEE actions are ignored and REDIRECT rules are interpreted as DROPs. This prevents multiple TEE or REDIRECT packets from being sent to third party devices.
+ * Rules in capabilities are always matched as if the current device is the sender (inbound == false). A capability specifies sender side rules that can be enforced on both sides.
#### `/controller/network/<network ID>/member`
@@ -235,27 +224,18 @@ This returns an object containing all currently online members and the most rece
| Field | Type | Description | Writable |
| --------------------- | ------------- | ------------------------------------------------- | -------- |
-| nwid | string | 16-digit network ID | no |
-| clock | integer | Current clock, ms since epoch | no |
+| id | string | Member's 10-digit ZeroTier address | no |
| address | string | Member's 10-digit ZeroTier address | no |
+| nwid | string | 16-digit network ID | no |
| authorized | boolean | Is member authorized? (for private networks) | YES |
| activeBridge | boolean | Member is able to bridge to other Ethernet nets | YES |
| identity | string | Member's public ZeroTier identity (if known) | no |
| ipAssignments | array[string] | Managed IP address assignments | YES |
-| memberRevision | integer | Member revision counter | no |
-| recentLog | array[object] | Recent member activity log; see below | no |
+| revision | integer | Member revision counter | no |
+| vMajor | integer | Most recently known major version | no |
+| vMinor | integer | Most recently known minor version | no |
+| vRev | integer | Most recently known revision | no |
+| vProto | integer | Most recently known protocl version | no |
Note that managed IP assignments are only used if they fall within a managed route. Otherwise they are ignored.
-**Recent log object format:**
-
-| Field | Type | Description |
-| --------------------- | ------------- | ------------------------------------------------- |
-| ts | integer | Time of request, ms since epoch |
-| authorized | boolean | Was member authorized? |
-| clientMajorVersion | integer | Client major version or -1 if unknown |
-| clientMinorVersion | integer | Client minor version or -1 if unknown |
-| clientRevision | integer | Client revision or -1 if unknown |
-| fromAddr | string | Physical address if known |
-
-The controller can only know a member's `fromAddr` if it's able to establish a direct path to it. Members behind very restrictive firewalls may not have this information since the controller will be receiving the member's requests by way of a relay. ZeroTier does not back-trace IP paths as packets are relayed since this would add a lot of protocol overhead.
diff --git a/controller/RethinkDB.cpp b/controller/RethinkDB.cpp
new file mode 100644
index 00000000..f6c8a59c
--- /dev/null
+++ b/controller/RethinkDB.cpp
@@ -0,0 +1,488 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+//#define ZT_CONTROLLER_USE_RETHINKDB
+
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "RethinkDB.hpp"
+#include "EmbeddedNetworkController.hpp"
+
+#include "../version.h"
+
+#include <chrono>
+#include <algorithm>
+#include <stdexcept>
+
+#include "../ext/librethinkdbxx/build/include/rethinkdb.h"
+
+namespace R = RethinkDB;
+using json = nlohmann::json;
+
+namespace ZeroTier {
+
+static const char *_timestr()
+{
+ time_t t = time(0);
+ char *ts = ctime(&t);
+ char *p = ts;
+ if (!p)
+ return "";
+ while (*p) {
+ if (*p == '\n') {
+ *p = (char)0;
+ break;
+ }
+ ++p;
+ }
+ return ts;
+}
+
+RethinkDB::RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path) :
+ DB(nc,myId,path),
+ _ready(2), // two tables need to be synchronized before we're ready, so this is ready when it reaches 0
+ _run(1),
+ _waitNoticePrinted(false)
+{
+ // rethinkdb:host:port:db[:auth]
+ std::vector<std::string> ps(OSUtils::split(path,":","",""));
+ if ((ps.size() < 4)||(ps[0] != "rethinkdb"))
+ throw std::runtime_error("invalid rethinkdb database url");
+ _host = ps[1];
+ _port = Utils::strToInt(ps[2].c_str());
+ _db = ps[3];
+ if (ps.size() > 4)
+ _auth = ps[4];
+
+ _readyLock.lock();
+
+ _membersDbWatcher = std::thread([this]() {
+ try {
+ while (_run == 1) {
+ try {
+ std::unique_ptr<R::Connection> rdb(R::connect(this->_host,this->_port,this->_auth));
+ if (rdb) {
+ _membersDbWatcherConnection = (void *)rdb.get();
+ auto cur = R::db(this->_db).table("Member",R::optargs("read_mode","outdated")).get_all(this->_myAddressStr,R::optargs("index","controllerId")).changes(R::optargs("squash",0.05,"include_initial",true,"include_types",true,"include_states",true)).run(*rdb);
+ while (cur.has_next()) {
+ if (_run != 1) break;
+ json tmp(json::parse(cur.next().as_json()));
+ if ((tmp["type"] == "state")&&(tmp["state"] == "ready")) {
+ if (--this->_ready == 0) {
+ if (_waitNoticePrinted)
+ fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ this->_readyLock.unlock();
+ }
+ } else {
+ try {
+ json &ov = tmp["old_val"];
+ json &nv = tmp["new_val"];
+ json oldConfig,newConfig;
+ if (ov.is_object()) oldConfig = ov["config"];
+ if (nv.is_object()) newConfig = nv["config"];
+ if (oldConfig.is_object()||newConfig.is_object())
+ this->_memberChanged(oldConfig,newConfig,(this->_ready <= 0));
+ } catch ( ... ) {} // ignore bad records
+ }
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (member change stream): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ } catch ( ... ) {}
+ });
+
+ _networksDbWatcher = std::thread([this]() {
+ try {
+ while (_run == 1) {
+ try {
+ std::unique_ptr<R::Connection> rdb(R::connect(this->_host,this->_port,this->_auth));
+ if (rdb) {
+ _networksDbWatcherConnection = (void *)rdb.get();
+ auto cur = R::db(this->_db).table("Network",R::optargs("read_mode","outdated")).get_all(this->_myAddressStr,R::optargs("index","controllerId")).changes(R::optargs("squash",0.05,"include_initial",true,"include_types",true,"include_states",true)).run(*rdb);
+ while (cur.has_next()) {
+ if (_run != 1) break;
+ json tmp(json::parse(cur.next().as_json()));
+ if ((tmp["type"] == "state")&&(tmp["state"] == "ready")) {
+ if (--this->_ready == 0) {
+ if (_waitNoticePrinted)
+ fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB data download complete." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ this->_readyLock.unlock();
+ }
+ } else {
+ try {
+ json &ov = tmp["old_val"];
+ json &nv = tmp["new_val"];
+ json oldConfig,newConfig;
+ if (ov.is_object()) oldConfig = ov["config"];
+ if (nv.is_object()) newConfig = nv["config"];
+ if (oldConfig.is_object()||newConfig.is_object())
+ this->_networkChanged(oldConfig,newConfig,(this->_ready <= 0));
+ } catch ( ... ) {} // ignore bad records
+ }
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (network change stream): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ } catch ( ... ) {}
+ });
+
+ for(int t=0;t<ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS;++t) {
+ _commitThread[t] = std::thread([this]() {
+ try {
+ std::unique_ptr<R::Connection> rdb;
+ nlohmann::json *config = (nlohmann::json *)0;
+ while ((this->_commitQueue.get(config))&&(_run == 1)) {
+ if (!config)
+ continue;
+ nlohmann::json record;
+ const char *table = (const char *)0;
+ std::string deleteId;
+ try {
+ const std::string objtype = (*config)["objtype"];
+ if (objtype == "member") {
+ const std::string nwid = (*config)["nwid"];
+ const std::string id = (*config)["id"];
+ record["id"] = nwid + "-" + id;
+ record["controllerId"] = this->_myAddressStr;
+ record["networkId"] = nwid;
+ record["nodeId"] = id;
+ record["config"] = *config;
+ table = "Member";
+ } else if (objtype == "network") {
+ const std::string id = (*config)["id"];
+ record["id"] = id;
+ record["controllerId"] = this->_myAddressStr;
+ record["config"] = *config;
+ table = "Network";
+ } else if (objtype == "trace") {
+ record = *config;
+ table = "RemoteTrace";
+ } else if (objtype == "_delete_network") {
+ deleteId = (*config)["id"];
+ table = "Network";
+ } else if (objtype == "_delete_member") {
+ deleteId = (*config)["nwid"];
+ deleteId.push_back('-');
+ const std::string tmp = (*config)["id"];
+ deleteId.append(tmp);
+ table = "Member";
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ table = (const char *)0;
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ table = (const char *)0;
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update record creation): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ table = (const char *)0;
+ }
+ delete config;
+ if (!table)
+ continue;
+ const std::string jdump(OSUtils::jsonDump(record,-1));
+
+ while (_run == 1) {
+ try {
+ if (!rdb)
+ rdb = R::connect(this->_host,this->_port,this->_auth);
+ if (rdb) {
+ if (deleteId.length() > 0) {
+ //printf("DELETE: %s" ZT_EOL_S,deleteId.c_str());
+ R::db(this->_db).table(table).get(deleteId).delete_().run(*rdb);
+ } else {
+ //printf("UPSERT: %s" ZT_EOL_S,record.dump().c_str());
+ R::db(this->_db).table(table).insert(R::Datum::from_json(jdump),R::optargs("conflict","update","return_changes",false)).run(*rdb);
+ }
+ break;
+ } else {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): connect failed (will retry)" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ rdb.reset();
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what(),jdump.c_str());
+ rdb.reset();
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): %s [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str(),jdump.c_str());
+ rdb.reset();
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update): unknown exception [%s]" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),jdump.c_str());
+ rdb.reset();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (insert/update outer loop): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ });
+ }
+
+ _onlineNotificationThread = std::thread([this]() {
+ int64_t lastUpdatedNetworkStatus = 0;
+ std::unordered_map< std::pair<uint64_t,uint64_t>,int64_t,_PairHasher > lastOnlineCumulative;
+ try {
+ std::unique_ptr<R::Connection> rdb;
+ while (_run == 1) {
+ try {
+ if (!rdb)
+ rdb = R::connect(this->_host,this->_port,this->_auth);
+ if (rdb) {
+ R::Array batch;
+ R::Object tmpobj;
+
+ std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > lastOnline;
+ {
+ std::lock_guard<std::mutex> l(_lastOnline_l);
+ lastOnline.swap(_lastOnline);
+ }
+
+ for(auto i=lastOnline.begin();i!=lastOnline.end();++i) {
+ lastOnlineCumulative[i->first] = i->second.first;
+ char tmp[64],tmp2[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx-%.10llx",i->first.first,i->first.second);
+ tmpobj["id"] = tmp;
+ tmpobj["ts"] = i->second.first;
+ tmpobj["phy"] = i->second.second.toIpString(tmp2);
+ batch.emplace_back(tmpobj);
+ if (batch.size() >= 1024) {
+ R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ }
+ if (batch.size() > 0) {
+ R::db(this->_db).table("MemberStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ tmpobj.clear();
+
+ const int64_t now = OSUtils::now();
+ if ((now - lastUpdatedNetworkStatus) > 10000) {
+ lastUpdatedNetworkStatus = now;
+
+ std::vector< std::pair< uint64_t,std::shared_ptr<_Network> > > networks;
+ {
+ std::lock_guard<std::mutex> l(_networks_l);
+ networks.reserve(_networks.size() + 1);
+ for(auto i=_networks.begin();i!=_networks.end();++i)
+ networks.push_back(*i);
+ }
+
+ for(auto i=networks.begin();i!=networks.end();++i) {
+ char tmp[64];
+ Utils::hex(i->first,tmp);
+ tmpobj["id"] = tmp;
+ {
+ std::lock_guard<std::mutex> l2(i->second->lock);
+ tmpobj["authorizedMemberCount"] = i->second->authorizedMembers.size();
+ tmpobj["totalMemberCount"] = i->second->members.size();
+ unsigned long onlineMemberCount = 0;
+ for(auto m=i->second->members.begin();m!=i->second->members.end();++m) {
+ auto lo = lastOnlineCumulative.find(std::pair<uint64_t,uint64_t>(i->first,m->first));
+ if (lo != lastOnlineCumulative.end()) {
+ if ((now - lo->second) <= (ZT_NETWORK_AUTOCONF_DELAY * 2))
+ ++onlineMemberCount;
+ else lastOnlineCumulative.erase(lo);
+ }
+ }
+ tmpobj["onlineMemberCount"] = onlineMemberCount;
+ tmpobj["bridgeCount"] = i->second->activeBridgeMembers.size();
+ tmpobj["ts"] = now;
+ }
+ batch.emplace_back(tmpobj);
+ if (batch.size() >= 1024) {
+ R::db(this->_db).table("NetworkStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ }
+ if (batch.size() > 0) {
+ R::db(this->_db).table("NetworkStatus",R::optargs("read_mode","outdated")).insert(batch,R::optargs("conflict","update")).run(*rdb);
+ batch.clear();
+ }
+ }
+ }
+ } catch (std::exception &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.what());
+ rdb.reset();
+ } catch (R::Error &e) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): %s" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt(),e.message.c_str());
+ rdb.reset();
+ } catch ( ... ) {
+ fprintf(stderr,"[%s] ERROR: %.10llx controller RethinkDB (node status update): unknown exception" ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ rdb.reset();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ }
+ } catch ( ... ) {}
+ });
+
+ _heartbeatThread = std::thread([this]() {
+ try {
+ R::Object controllerRecord;
+ std::unique_ptr<R::Connection> rdb;
+
+ {
+ char publicId[1024];
+ //char secretId[1024];
+ char hostname[1024];
+ this->_myId.toString(false,publicId);
+ //this->_myId.toString(true,secretId);
+ if (gethostname(hostname,sizeof(hostname)) != 0) {
+ hostname[0] = (char)0;
+ } else {
+ for(int i=0;i<sizeof(hostname);++i) {
+ if ((hostname[i] == '.')||(hostname[i] == 0)) {
+ hostname[i] = (char)0;
+ break;
+ }
+ }
+ }
+ controllerRecord["id"] = this->_myAddressStr.c_str();
+ controllerRecord["publicIdentity"] = publicId;
+ //controllerRecord["secretIdentity"] = secretId;
+ if (hostname[0])
+ controllerRecord["clusterHost"] = hostname;
+ controllerRecord["vMajor"] = ZEROTIER_ONE_VERSION_MAJOR;
+ controllerRecord["vMinor"] = ZEROTIER_ONE_VERSION_MINOR;
+ controllerRecord["vRev"] = ZEROTIER_ONE_VERSION_REVISION;
+ controllerRecord["vBuild"] = ZEROTIER_ONE_VERSION_BUILD;
+ }
+
+ while (_run == 1) {
+ try {
+ if (!rdb)
+ rdb = R::connect(this->_host,this->_port,this->_auth);
+ if (rdb) {
+ controllerRecord["lastAlive"] = OSUtils::now();
+ //printf("HEARTBEAT: %s" ZT_EOL_S,tmp);
+ R::db(this->_db).table("Controller",R::optargs("read_mode","outdated")).insert(controllerRecord,R::optargs("conflict","update")).run(*rdb);
+ }
+ } catch ( ... ) {
+ rdb.reset();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+ }
+ } catch ( ... ) {}
+ });
+}
+
+RethinkDB::~RethinkDB()
+{
+ _run = 0;
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ _commitQueue.stop();
+ for(int t=0;t<ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS;++t)
+ _commitThread[t].join();
+ if (_membersDbWatcherConnection)
+ ((R::Connection *)_membersDbWatcherConnection)->close();
+ if (_networksDbWatcherConnection)
+ ((R::Connection *)_networksDbWatcherConnection)->close();
+ _membersDbWatcher.join();
+ _networksDbWatcher.join();
+ _heartbeatThread.join();
+ _onlineNotificationThread.join();
+}
+
+bool RethinkDB::waitForReady()
+{
+ while (_ready > 0) {
+ if (!_waitNoticePrinted) {
+ _waitNoticePrinted = true;
+ fprintf(stderr,"[%s] NOTICE: %.10llx controller RethinkDB waiting for initial data download..." ZT_EOL_S,_timestr(),(unsigned long long)_myAddress.toInt());
+ }
+ _readyLock.lock();
+ _readyLock.unlock();
+ }
+ return true;
+}
+
+void RethinkDB::save(nlohmann::json *orig,nlohmann::json &record)
+{
+ if (!record.is_object()) // sanity check
+ return;
+ waitForReady();
+ if (orig) {
+ if (*orig != record) {
+ record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1;
+ _commitQueue.post(new nlohmann::json(record));
+ }
+ } else {
+ record["revision"] = 1;
+ _commitQueue.post(new nlohmann::json(record));
+ }
+}
+
+void RethinkDB::eraseNetwork(const uint64_t networkId)
+{
+ char tmp2[24];
+ waitForReady();
+ Utils::hex(networkId,tmp2);
+ json *tmp = new json();
+ (*tmp)["id"] = tmp2;
+ (*tmp)["objtype"] = "_delete_network"; // pseudo-type, tells thread to delete network
+ _commitQueue.post(tmp);
+}
+
+void RethinkDB::eraseMember(const uint64_t networkId,const uint64_t memberId)
+{
+ char tmp2[24];
+ json *tmp = new json();
+ waitForReady();
+ Utils::hex(networkId,tmp2);
+ (*tmp)["nwid"] = tmp2;
+ Utils::hex10(memberId,tmp2);
+ (*tmp)["id"] = tmp2;
+ (*tmp)["objtype"] = "_delete_member"; // pseudo-type, tells thread to delete network
+ _commitQueue.post(tmp);
+}
+
+void RethinkDB::nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress)
+{
+ std::lock_guard<std::mutex> l(_lastOnline_l);
+ std::pair<int64_t,InetAddress> &i = _lastOnline[std::pair<uint64_t,uint64_t>(networkId,memberId)];
+ i.first = OSUtils::now();
+ if (physicalAddress)
+ i.second = physicalAddress;
+}
+
+} // namespace ZeroTier
+
+#endif // ZT_CONTROLLER_USE_RETHINKDB
diff --git a/controller/RethinkDB.hpp b/controller/RethinkDB.hpp
new file mode 100644
index 00000000..b1049ac3
--- /dev/null
+++ b/controller/RethinkDB.hpp
@@ -0,0 +1,84 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef ZT_CONTROLLER_USE_RETHINKDB
+
+#ifndef ZT_CONTROLLER_RETHINKDB_HPP
+#define ZT_CONTROLLER_RETHINKDB_HPP
+
+#include "DB.hpp"
+
+#define ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS 4
+
+namespace ZeroTier
+{
+
+/**
+ * A controller database driver that talks to RethinkDB
+ *
+ * This is for use with ZeroTier Central. Others are free to build and use it
+ * but be aware that we might change it at any time.
+ */
+class RethinkDB : public DB
+{
+public:
+ RethinkDB(EmbeddedNetworkController *const nc,const Identity &myId,const char *path);
+ virtual ~RethinkDB();
+
+ virtual bool waitForReady();
+ virtual void save(nlohmann::json *orig,nlohmann::json &record);
+ virtual void eraseNetwork(const uint64_t networkId);
+ virtual void eraseMember(const uint64_t networkId,const uint64_t memberId);
+ virtual void nodeIsOnline(const uint64_t networkId,const uint64_t memberId,const InetAddress &physicalAddress);
+
+protected:
+ struct _PairHasher
+ {
+ inline std::size_t operator()(const std::pair<uint64_t,uint64_t> &p) const { return (std::size_t)(p.first ^ p.second); }
+ };
+
+ std::string _host;
+ std::string _db;
+ std::string _auth;
+ int _port;
+
+ void *_networksDbWatcherConnection;
+ void *_membersDbWatcherConnection;
+ std::thread _networksDbWatcher;
+ std::thread _membersDbWatcher;
+
+ BlockingQueue< nlohmann::json * > _commitQueue;
+ std::thread _commitThread[ZT_CONTROLLER_RETHINKDB_COMMIT_THREADS];
+
+ std::unordered_map< std::pair<uint64_t,uint64_t>,std::pair<int64_t,InetAddress>,_PairHasher > _lastOnline;
+ mutable std::mutex _lastOnline_l;
+ std::thread _onlineNotificationThread;
+
+ std::thread _heartbeatThread;
+
+ mutable std::mutex _readyLock; // locked until ready
+ std::atomic<int> _ready;
+ std::atomic<int> _run;
+ mutable volatile bool _waitNoticePrinted;
+};
+
+} // namespace ZeroTier
+
+#endif
+
+#endif // ZT_CONTROLLER_USE_RETHINKDB
diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp
deleted file mode 100644
index 65051744..00000000
--- a/controller/SqliteNetworkController.cpp
+++ /dev/null
@@ -1,2195 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, 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 <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/types.h>
-
-#include <algorithm>
-#include <utility>
-#include <stdexcept>
-#include <set>
-
-#include "../include/ZeroTierOne.h"
-#include "../node/Constants.hpp"
-
-#ifdef ZT_USE_SYSTEM_JSON_PARSER
-#include <json-parser/json.h>
-#else
-#include "../ext/json-parser/json.h"
-#endif
-
-#include "SqliteNetworkController.hpp"
-
-#include "../node/Node.hpp"
-#include "../node/Utils.hpp"
-#include "../node/CertificateOfMembership.hpp"
-#include "../node/NetworkConfig.hpp"
-#include "../node/Dictionary.hpp"
-#include "../node/InetAddress.hpp"
-#include "../node/MAC.hpp"
-#include "../node/Address.hpp"
-
-#include "../osdep/OSUtils.hpp"
-
-// Include ZT_NETCONF_SCHEMA_SQL constant to init database
-#include "schema.sql.c"
-
-// Stored in database as schemaVersion key in Config.
-// If not present, database is assumed to be empty and at the current schema version
-// and this key/value is added automatically.
-#define ZT_NETCONF_SQLITE_SCHEMA_VERSION 4
-#define ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR "4"
-
-// API version reported via JSON control plane
-#define ZT_NETCONF_CONTROLLER_API_VERSION 2
-
-// Number of requests to remember in member history
-#define ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH 8
-
-// Min duration between requests for an address/nwid combo to prevent floods
-#define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
-
-// Delay between backups in milliseconds
-#define ZT_NETCONF_BACKUP_PERIOD 300000
-
-// Nodes are considered active if they've queried in less than this long
-#define ZT_NETCONF_NODE_ACTIVE_THRESHOLD ((ZT_NETWORK_AUTOCONF_DELAY * 2) + 5000)
-
-// Flags for Network 'flags' field in table
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN 1
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193 2
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE 4
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN 8
-
-// Flags with all V6 managed mode flags flipped off -- for masking in update operation and in string form for SQL building
-#define ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_MASK_S "268435441"
-
-// Uncomment to trace Sqlite for debugging
-//#define ZT_NETCONF_SQLITE_TRACE 1
-
-namespace ZeroTier {
-
-namespace {
-
-static std::string _jsonEscape(const char *s)
-{
- if (!s)
- return std::string();
- std::string buf;
- for(const char *p=s;(*p);++p) {
- switch(*p) {
- case '\t': buf.append("\\t"); break;
- case '\b': buf.append("\\b"); break;
- case '\r': buf.append("\\r"); break;
- case '\n': buf.append("\\n"); break;
- case '\f': buf.append("\\f"); break;
- case '"': buf.append("\\\""); break;
- case '\\': buf.append("\\\\"); break;
- case '/': buf.append("\\/"); break;
- default: buf.push_back(*p); break;
- }
- }
- return buf;
-}
-static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
-
-// Converts an InetAddress to a blob and an int for storage in database
-static void _ipToBlob(const InetAddress &a,char *ipBlob,int &ipVersion) /* blob[16] */
-{
- switch(a.ss_family) {
- case AF_INET:
- memset(ipBlob,0,12);
- memcpy(ipBlob + 12,a.rawIpData(),4);
- ipVersion = 4;
- break;
- case AF_INET6:
- memcpy(ipBlob,a.rawIpData(),16);
- ipVersion = 6;
- break;
- }
-}
-
-// Member.recentHistory is stored in a BLOB as an array of strings containing JSON objects.
-// This is kind of hacky but efficient and quick to parse and send to the client.
-class MemberRecentHistory : public std::list<std::string>
-{
-public:
- inline std::string toBlob() const
- {
- std::string b;
- for(const_iterator i(begin());i!=end();++i) {
- b.append(*i);
- b.push_back((char)0);
- }
- return b;
- }
-
- inline void fromBlob(const char *blob,unsigned int len)
- {
- for(unsigned int i=0,k=0;i<len;++i) {
- if (!blob[i]) {
- push_back(std::string(blob + k,i - k));
- k = i + 1;
- }
- }
- }
-};
-
-struct MemberRecord
-{
- sqlite3_int64 rowid;
- char nodeId[16];
- bool authorized;
- bool activeBridge;
- uint64_t lastRequestTime;
- MemberRecentHistory recentHistory;
-
- MemberRecord() :
- rowid(0),
- authorized(false),
- activeBridge(false),
- lastRequestTime(0)
- {
- nodeId[0] = (char)0;
- }
-};
-
-struct NetworkRecord
-{
- char id[24];
- const char *name;
- int flags;
- bool isPrivate;
- bool enableBroadcast;
- bool allowPassiveBridging;
- int multicastLimit;
- uint64_t creationTime;
- uint64_t revision;
- uint64_t memberRevisionCounter;
-
- NetworkRecord() :
- name((const char *)0),
- flags(0),
- isPrivate(true),
- enableBroadcast(false),
- allowPassiveBridging(false),
- multicastLimit(0),
- creationTime(0),
- revision(0),
- memberRevisionCounter(0)
- {
- id[0] = (char)0;
- }
-};
-
-#ifdef ZT_NETCONF_SQLITE_TRACE
-static void sqliteTraceFunc(void *ptr,const char *s)
-{
- fprintf(stderr,"SQLITE: %s\n",s);
-}
-#endif
-
-} // anonymous namespace
-
-SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath) :
- _node(node),
- _backupThreadRun(true),
- _backupNeeded(true),
- _dbPath(dbPath),
- _circuitTestPath(circuitTestPath),
- _db((sqlite3 *)0)
-{
- if (sqlite3_open_v2(dbPath,&_db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK)
- throw std::runtime_error("SqliteNetworkController cannot open database file");
- sqlite3_busy_timeout(_db,10000);
-
- sqlite3_exec(_db,"PRAGMA synchronous = OFF",0,0,0);
- sqlite3_exec(_db,"PRAGMA journal_mode = MEMORY",0,0,0);
-
- sqlite3_stmt *s = (sqlite3_stmt *)0;
- if ((sqlite3_prepare_v2(_db,"SELECT v FROM Config WHERE k = 'schemaVersion';",-1,&s,(const char **)0) == SQLITE_OK)&&(s)) {
- int schemaVersion = -1234;
- if (sqlite3_step(s) == SQLITE_ROW) {
- schemaVersion = sqlite3_column_int(s,0);
- }
-
- sqlite3_finalize(s);
-
- if (schemaVersion == -1234) {
- sqlite3_close(_db);
- throw std::runtime_error("SqliteNetworkController schemaVersion not found in Config table (init failure?)");
- }
-
- if (schemaVersion < 2) {
- // Create NodeHistory table to upgrade from version 1 to version 2
- if (sqlite3_exec(_db,
- "CREATE TABLE NodeHistory (\n"
- " nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"
- " networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"
- " networkVisitCounter INTEGER NOT NULL DEFAULT(0),\n"
- " networkRequestAuthorized INTEGER NOT NULL DEFAULT(0),\n"
- " requestTime INTEGER NOT NULL DEFAULT(0),\n"
- " clientMajorVersion INTEGER NOT NULL DEFAULT(0),\n"
- " clientMinorVersion INTEGER NOT NULL DEFAULT(0),\n"
- " clientRevision INTEGER NOT NULL DEFAULT(0),\n"
- " networkRequestMetaData VARCHAR(1024),\n"
- " fromAddress VARCHAR(128)\n"
- ");\n"
- "CREATE INDEX NodeHistory_nodeId ON NodeHistory (nodeId);\n"
- "CREATE INDEX NodeHistory_networkId ON NodeHistory (networkId);\n"
- "CREATE INDEX NodeHistory_requestTime ON NodeHistory (requestTime);\n"
- "UPDATE \"Config\" SET \"v\" = 2 WHERE \"k\" = 'schemaVersion';\n"
- ,0,0,0) != SQLITE_OK) {
- char err[1024];
- Utils::snprintf(err,sizeof(err),"SqliteNetworkController cannot upgrade the database to version 2: %s",sqlite3_errmsg(_db));
- sqlite3_close(_db);
- throw std::runtime_error(err);
- } else {
- schemaVersion = 2;
- }
- }
-
- if (schemaVersion < 3) {
- // Create Route table to upgrade from version 2 to version 3 and migrate old
- // data. Also delete obsolete Gateway table that was never actually used, and
- // migrate Network flags to a bitwise flags field instead of ASCII cruft.
- if (sqlite3_exec(_db,
- "DROP TABLE Gateway;\n"
- "CREATE TABLE Route (\n"
- " networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"
- " target blob(16) NOT NULL,\n"
- " via blob(16),\n"
- " targetNetmaskBits integer NOT NULL,\n"
- " ipVersion integer NOT NULL,\n"
- " flags integer NOT NULL,\n"
- " metric integer NOT NULL\n"
- ");\n"
- "CREATE INDEX Route_networkId ON Route (networkId);\n"
- "INSERT INTO Route SELECT DISTINCT networkId,\"ip\" AS \"target\",NULL AS \"via\",ipNetmaskBits AS targetNetmaskBits,ipVersion,0 AS \"flags\",0 AS \"metric\" FROM IpAssignment WHERE nodeId IS NULL AND \"type\" = 1;\n"
- "ALTER TABLE Network ADD COLUMN \"flags\" integer NOT NULL DEFAULT(0);\n"
- "UPDATE Network SET \"flags\" = (\"flags\" | 1) WHERE v4AssignMode = 'zt';\n"
- "UPDATE Network SET \"flags\" = (\"flags\" | 2) WHERE v6AssignMode = 'rfc4193';\n"
- "UPDATE Network SET \"flags\" = (\"flags\" | 4) WHERE v6AssignMode = '6plane';\n"
- "ALTER TABLE Member ADD COLUMN \"flags\" integer NOT NULL DEFAULT(0);\n"
- "DELETE FROM IpAssignment WHERE nodeId IS NULL AND \"type\" = 1;\n"
- "UPDATE \"Config\" SET \"v\" = 3 WHERE \"k\" = 'schemaVersion';\n"
- ,0,0,0) != SQLITE_OK) {
- char err[1024];
- Utils::snprintf(err,sizeof(err),"SqliteNetworkController cannot upgrade the database to version 3: %s",sqlite3_errmsg(_db));
- sqlite3_close(_db);
- throw std::runtime_error(err);
- } else {
- schemaVersion = 3;
- }
- }
-
- if (schemaVersion < 4) {
- // Turns out this was overkill and a huge performance drag. Will be revisiting this
- // more later but for now a brief snapshot of recent history stored in Member is fine.
- // Also prepare for implementation of proof of work requests.
- if (sqlite3_exec(_db,
- "DROP TABLE NodeHistory;\n"
- "ALTER TABLE Member ADD COLUMN lastRequestTime integer NOT NULL DEFAULT(0);\n"
- "ALTER TABLE Member ADD COLUMN lastPowDifficulty integer NOT NULL DEFAULT(0);\n"
- "ALTER TABLE Member ADD COLUMN lastPowTime integer NOT NULL DEFAULT(0);\n"
- "ALTER TABLE Member ADD COLUMN recentHistory blob;\n"
- "CREATE INDEX Member_networkId_lastRequestTime ON Member(networkId, lastRequestTime);\n"
- "UPDATE \"Config\" SET \"v\" = 4 WHERE \"k\" = 'schemaVersion';\n"
- ,0,0,0) != SQLITE_OK) {
- char err[1024];
- Utils::snprintf(err,sizeof(err),"SqliteNetworkController cannot upgrade the database to version 3: %s",sqlite3_errmsg(_db));
- sqlite3_close(_db);
- throw std::runtime_error(err);
- } else {
- schemaVersion = 4;
- }
- }
-
- if (schemaVersion != ZT_NETCONF_SQLITE_SCHEMA_VERSION) {
- sqlite3_close(_db);
- throw std::runtime_error("SqliteNetworkController database schema version mismatch");
- }
- } else {
- // Prepare statement will fail if Config table doesn't exist, which means our DB
- // needs to be initialized.
- if (sqlite3_exec(_db,ZT_NETCONF_SCHEMA_SQL"INSERT INTO Config (k,v) VALUES ('schemaVersion',"ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR");",0,0,0) != SQLITE_OK) {
- char err[1024];
- Utils::snprintf(err,sizeof(err),"SqliteNetworkController cannot initialize database and/or insert schemaVersion into Config table: %s",sqlite3_errmsg(_db));
- sqlite3_close(_db);
- throw std::runtime_error(err);
- }
- }
-
- if (
-
- /* Network */
- (sqlite3_prepare_v2(_db,"SELECT name,private,enableBroadcast,allowPassiveBridging,\"flags\",multicastLimit,creationTime,revision,memberRevisionCounter,(SELECT COUNT(1) FROM Member WHERE Member.networkId = Network.id AND Member.authorized > 0) FROM Network WHERE id = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT revision FROM Network WHERE id = ?",-1,&_sGetNetworkRevision,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"UPDATE Network SET revision = ? WHERE id = ?",-1,&_sSetNetworkRevision,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT INTO Network (id,name,creationTime,revision) VALUES (?,?,?,1)",-1,&_sCreateNetwork,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM Network WHERE id = ?",-1,&_sDeleteNetwork,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT id FROM Network ORDER BY id ASC",-1,&_sListNetworks,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"UPDATE Network SET memberRevisionCounter = (memberRevisionCounter + 1) WHERE id = ?",-1,&_sIncrementMemberRevisionCounter,(const char **)0) != SQLITE_OK)
-
- /* Node */
- ||(sqlite3_prepare_v2(_db,"SELECT identity FROM Node WHERE id = ?",-1,&_sGetNodeIdentity,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateOrReplaceNode,(const char **)0) != SQLITE_OK)
-
- /* Rule */
- ||(sqlite3_prepare_v2(_db,"SELECT etherType FROM Rule WHERE networkId = ? AND \"action\" = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT INTO Rule (networkId,ruleNo,nodeId,sourcePort,destPort,vlanId,vlanPcP,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,flags,invFlags,\"action\") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",-1,&_sCreateRule,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT ruleNo,nodeId,sourcePort,destPort,vlanId,vlanPcp,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"flags\",invFlags,\"action\" FROM Rule WHERE networkId = ? ORDER BY ruleNo ASC",-1,&_sListRules,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM Rule WHERE networkId = ?",-1,&_sDeleteRulesForNetwork,(const char **)0) != SQLITE_OK)
-
- /* IpAssignmentPool */
- ||(sqlite3_prepare_v2(_db,"SELECT ipRangeStart,ipRangeEnd FROM IpAssignmentPool WHERE networkId = ? AND ipVersion = ?",-1,&_sGetIpAssignmentPools,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT ipRangeStart,ipRangeEnd,ipVersion FROM IpAssignmentPool WHERE networkId = ? ORDER BY ipRangeStart ASC",-1,&_sGetIpAssignmentPools2,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignmentPool (networkId,ipRangeStart,ipRangeEnd,ipVersion) VALUES (?,?,?,?)",-1,&_sCreateIpAssignmentPool,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignmentPool WHERE networkId = ?",-1,&_sDeleteIpAssignmentPoolsForNetwork,(const char **)0) != SQLITE_OK)
-
- /* IpAssignment */
- ||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND \"type\" = 0 ORDER BY ip ASC",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT 1 FROM IpAssignment WHERE networkId = ? AND ip = ? AND ipVersion = ? AND \"type\" = ?",-1,&_sCheckIfIpIsAllocated,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignment (networkId,nodeId,\"type\",ip,ipNetmaskBits,ipVersion) VALUES (?,?,?,?,?,?)",-1,&_sAllocateIp,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND \"type\" = ?",-1,&_sDeleteIpAllocations,(const char **)0) != SQLITE_OK)
-
- /* Relay */
- ||(sqlite3_prepare_v2(_db,"SELECT \"address\",\"phyAddress\" FROM Relay WHERE \"networkId\" = ? ORDER BY \"address\" ASC",-1,&_sGetRelays,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM Relay WHERE networkId = ?",-1,&_sDeleteRelaysForNetwork,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT INTO Relay (\"networkId\",\"address\",\"phyAddress\") VALUES (?,?,?)",-1,&_sCreateRelay,(const char **)0) != SQLITE_OK)
-
- /* Member */
- ||(sqlite3_prepare_v2(_db,"SELECT rowid,authorized,activeBridge,memberRevision,\"flags\",lastRequestTime,recentHistory FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sGetMember,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT m.authorized,m.activeBridge,m.memberRevision,n.identity,m.flags,m.lastRequestTime,m.recentHistory FROM Member AS m LEFT OUTER JOIN Node AS n ON n.id = m.nodeId WHERE m.networkId = ? AND m.nodeId = ?",-1,&_sGetMember2,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT INTO Member (networkId,nodeId,authorized,activeBridge,memberRevision) VALUES (?,?,?,0,(SELECT memberRevisionCounter FROM Network WHERE id = ?))",-1,&_sCreateMember,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT nodeId FROM Member WHERE networkId = ? AND activeBridge > 0 AND authorized > 0",-1,&_sGetActiveBridges,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT m.nodeId,m.memberRevision FROM Member AS m WHERE m.networkId = ? ORDER BY m.nodeId ASC",-1,&_sListNetworkMembers,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberAuthorized,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberActiveBridge,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"UPDATE Member SET \"lastRequestTime\" = ?, \"recentHistory\" = ? WHERE rowid = ?",-1,&_sUpdateMemberHistory,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ?",-1,&_sDeleteAllNetworkMembers,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT nodeId,recentHistory FROM Member WHERE networkId = ? AND lastRequestTime >= ?",-1,&_sGetActiveNodesOnNetwork,(const char **)0) != SQLITE_OK)
-
- /* Route */
- ||(sqlite3_prepare_v2(_db,"INSERT INTO Route (networkId,target,via,targetNetmaskBits,ipVersion,flags,metric) VALUES (?,?,?,?,?,?,?)",-1,&_sCreateRoute,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"SELECT DISTINCT target,via,targetNetmaskBits,ipVersion,flags,metric FROM \"Route\" WHERE networkId = ? ORDER BY ipVersion,target,via",-1,&_sGetRoutes,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"DELETE FROM \"Route\" WHERE networkId = ?",-1,&_sDeleteRoutes,(const char **)0) != SQLITE_OK)
-
- /* Config */
- ||(sqlite3_prepare_v2(_db,"SELECT \"v\" FROM \"Config\" WHERE \"k\" = ?",-1,&_sGetConfig,(const char **)0) != SQLITE_OK)
- ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO \"Config\" (\"k\",\"v\") VALUES (?,?)",-1,&_sSetConfig,(const char **)0) != SQLITE_OK)
-
- ) {
- std::string err(std::string("SqliteNetworkController unable to initialize one or more prepared statements: ") + sqlite3_errmsg(_db));
- sqlite3_close(_db);
- throw std::runtime_error(err);
- }
-
- /* Generate a 128-bit / 32-character "instance ID" if one isn't already
- * defined. Clients can use this to determine if this is the same controller
- * database they know and love. */
- sqlite3_reset(_sGetConfig);
- sqlite3_bind_text(_sGetConfig,1,"instanceId",10,SQLITE_STATIC);
- if (sqlite3_step(_sGetConfig) != SQLITE_ROW) {
- unsigned char sr[32];
- Utils::getSecureRandom(sr,32);
- for(unsigned int i=0;i<32;++i)
- _instanceId.push_back("0123456789abcdef"[(unsigned int)sr[i] & 0xf]);
-
- sqlite3_reset(_sSetConfig);
- sqlite3_bind_text(_sSetConfig,1,"instanceId",10,SQLITE_STATIC);
- sqlite3_bind_text(_sSetConfig,2,_instanceId.c_str(),-1,SQLITE_STATIC);
- if (sqlite3_step(_sSetConfig) != SQLITE_DONE)
- throw std::runtime_error("SqliteNetworkController unable to read or initialize instanceId");
- } else {
- const char *iid = reinterpret_cast<const char *>(sqlite3_column_text(_sGetConfig,0));
- if (!iid)
- throw std::runtime_error("SqliteNetworkController unable to read instanceId (it's NULL)");
- _instanceId = iid;
- }
-
-#ifdef ZT_NETCONF_SQLITE_TRACE
- sqlite3_trace(_db,sqliteTraceFunc,(void *)0);
-#endif
-
- _backupThread = Thread::start(this);
-}
-
-SqliteNetworkController::~SqliteNetworkController()
-{
- _backupThreadRun = false;
- Thread::join(_backupThread);
-
- Mutex::Lock _l(_lock);
- if (_db) {
- sqlite3_finalize(_sGetNetworkById);
- sqlite3_finalize(_sGetMember);
- sqlite3_finalize(_sCreateMember);
- sqlite3_finalize(_sGetNodeIdentity);
- sqlite3_finalize(_sCreateOrReplaceNode);
- sqlite3_finalize(_sGetEtherTypesFromRuleTable);
- sqlite3_finalize(_sGetActiveBridges);
- sqlite3_finalize(_sGetIpAssignmentsForNode);
- sqlite3_finalize(_sGetIpAssignmentPools);
- sqlite3_finalize(_sCheckIfIpIsAllocated);
- sqlite3_finalize(_sAllocateIp);
- sqlite3_finalize(_sDeleteIpAllocations);
- sqlite3_finalize(_sGetRelays);
- sqlite3_finalize(_sListNetworks);
- sqlite3_finalize(_sListNetworkMembers);
- sqlite3_finalize(_sGetMember2);
- sqlite3_finalize(_sGetIpAssignmentPools2);
- sqlite3_finalize(_sListRules);
- sqlite3_finalize(_sCreateRule);
- sqlite3_finalize(_sCreateNetwork);
- sqlite3_finalize(_sGetNetworkRevision);
- sqlite3_finalize(_sSetNetworkRevision);
- sqlite3_finalize(_sDeleteRelaysForNetwork);
- sqlite3_finalize(_sCreateRelay);
- sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork);
- sqlite3_finalize(_sDeleteRulesForNetwork);
- sqlite3_finalize(_sCreateIpAssignmentPool);
- sqlite3_finalize(_sUpdateMemberAuthorized);
- sqlite3_finalize(_sUpdateMemberActiveBridge);
- sqlite3_finalize(_sUpdateMemberHistory);
- sqlite3_finalize(_sDeleteMember);
- sqlite3_finalize(_sDeleteAllNetworkMembers);
- sqlite3_finalize(_sGetActiveNodesOnNetwork);
- sqlite3_finalize(_sDeleteNetwork);
- sqlite3_finalize(_sCreateRoute);
- sqlite3_finalize(_sGetRoutes);
- sqlite3_finalize(_sDeleteRoutes);
- sqlite3_finalize(_sIncrementMemberRevisionCounter);
- sqlite3_finalize(_sGetConfig);
- sqlite3_finalize(_sSetConfig);
- sqlite3_close(_db);
- }
-}
-
-NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,NetworkConfig &nc)
-{
- if (((!signingId)||(!signingId.hasPrivate()))||(signingId.address().toInt() != (nwid >> 24))) {
- return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
-
- const uint64_t now = OSUtils::now();
-
- NetworkRecord network;
- Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid);
-
- MemberRecord member;
- Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt());
-
- { // begin lock
- Mutex::Lock _l(_lock);
-
- // Check rate limit circuit breaker to prevent flooding
- {
- uint64_t &lrt = _lastRequestTime[std::pair<uint64_t,uint64_t>(identity.address().toInt(),nwid)];
- if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
- return NetworkController::NETCONF_QUERY_IGNORE;
- lrt = now;
- }
-
- _backupNeeded = true;
-
- // Create Node record or do full identity check if we already have one
-
- sqlite3_reset(_sGetNodeIdentity);
- sqlite3_bind_text(_sGetNodeIdentity,1,member.nodeId,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) {
- try {
- Identity alreadyKnownIdentity((const char *)sqlite3_column_text(_sGetNodeIdentity,0));
- if (alreadyKnownIdentity != identity)
- return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
- } catch ( ... ) { // identity stored in database is not valid or is NULL
- return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
- }
- } else {
- std::string idstr(identity.toString(false));
- sqlite3_reset(_sCreateOrReplaceNode);
- sqlite3_bind_text(_sCreateOrReplaceNode,1,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr.c_str(),-1,SQLITE_STATIC);
- if (sqlite3_step(_sCreateOrReplaceNode) != SQLITE_DONE) {
- return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
- }
-
- // Fetch Network record
-
- sqlite3_reset(_sGetNetworkById);
- sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC);
- if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
- network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0);
- network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0);
- network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0);
- network.allowPassiveBridging = (sqlite3_column_int(_sGetNetworkById,3) > 0);
- network.flags = sqlite3_column_int(_sGetNetworkById,4);
- network.multicastLimit = sqlite3_column_int(_sGetNetworkById,5);
- network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,6);
- network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7);
- network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8);
- } else {
- return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND;
- }
-
- // Fetch or create Member record
-
- sqlite3_reset(_sGetMember);
- sqlite3_bind_text(_sGetMember,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetMember,2,member.nodeId,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetMember) == SQLITE_ROW) {
- member.rowid = sqlite3_column_int64(_sGetMember,0);
- member.authorized = (sqlite3_column_int(_sGetMember,1) > 0);
- member.activeBridge = (sqlite3_column_int(_sGetMember,2) > 0);
- member.lastRequestTime = (uint64_t)sqlite3_column_int64(_sGetMember,5);
- const char *rhblob = (const char *)sqlite3_column_blob(_sGetMember,6);
- if (rhblob)
- member.recentHistory.fromBlob(rhblob,(unsigned int)sqlite3_column_bytes(_sGetMember,6));
- } else {
- member.authorized = (network.isPrivate ? false : true);
- member.activeBridge = false;
- sqlite3_reset(_sCreateMember);
- sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 1 : 0));
- sqlite3_bind_text(_sCreateMember,4,network.id,16,SQLITE_STATIC);
- if (sqlite3_step(_sCreateMember) != SQLITE_DONE) {
- return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
- member.rowid = sqlite3_last_insert_rowid(_db);
-
- sqlite3_reset(_sIncrementMemberRevisionCounter);
- sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC);
- sqlite3_step(_sIncrementMemberRevisionCounter);
- }
-
- // Update Member.history
-
- {
- char mh[1024];
- Utils::snprintf(mh,sizeof(mh),
- "{\"ts\":%llu,\"authorized\":%s,\"clientMajorVersion\":%u,\"clientMinorVersion\":%u,\"clientRevision\":%u,\"fromAddr\":",
- (unsigned long long)now,
- ((member.authorized) ? "true" : "false"),
- metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0),
- metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0),
- metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0));
- member.recentHistory.push_front(std::string(mh));
- if (fromAddr) {
- member.recentHistory.front().push_back('"');
- member.recentHistory.front().append(_jsonEscape(fromAddr.toString()));
- member.recentHistory.front().append("\"}");
- } else {
- member.recentHistory.front().append("null}");
- }
-
- while (member.recentHistory.size() > ZT_NETCONF_DB_MEMBER_HISTORY_LENGTH)
- member.recentHistory.pop_back();
- std::string rhblob(member.recentHistory.toBlob());
-
- sqlite3_reset(_sUpdateMemberHistory);
- sqlite3_clear_bindings(_sUpdateMemberHistory);
- sqlite3_bind_int64(_sUpdateMemberHistory,1,(sqlite3_int64)now);
- sqlite3_bind_blob(_sUpdateMemberHistory,2,(const void *)rhblob.data(),(int)rhblob.length(),SQLITE_STATIC);
- sqlite3_bind_int64(_sUpdateMemberHistory,3,member.rowid);
- sqlite3_step(_sUpdateMemberHistory);
- }
-
- // Don't proceed if member is not authorized! ---------------------------
-
- if (!member.authorized)
- return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
-
- // Create network configuration -- we create both legacy and new types and send both for backward compatibility
-
- // New network config structure
- nc.networkId = Utils::hexStrToU64(network.id);
- nc.type = network.isPrivate ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
- nc.timestamp = now;
- nc.revision = network.revision;
- nc.issuedTo = member.nodeId;
- if (network.enableBroadcast) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
- if (network.allowPassiveBridging) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
- memcpy(nc.name,network.name,std::min((unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH,(unsigned int)strlen(network.name)));
-
- { // TODO: right now only etherTypes are supported in rules
- std::vector<int> allowedEtherTypes;
- sqlite3_reset(_sGetEtherTypesFromRuleTable);
- sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC);
- while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) {
- if (sqlite3_column_type(_sGetEtherTypesFromRuleTable,0) == SQLITE_NULL) {
- allowedEtherTypes.clear();
- allowedEtherTypes.push_back(0); // NULL 'allow' matches ANY
- break;
- } else {
- int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0);
- if ((et >= 0)&&(et <= 0xffff))
- allowedEtherTypes.push_back(et);
- }
- }
- std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end());
- allowedEtherTypes.erase(std::unique(allowedEtherTypes.begin(),allowedEtherTypes.end()),allowedEtherTypes.end());
-
- for(long i=0;i<(long)allowedEtherTypes.size();++i) {
- if ((nc.ruleCount + 2) > ZT_MAX_NETWORK_RULES)
- break;
- if (allowedEtherTypes[i] > 0) {
- nc.rules[nc.ruleCount].t = ZT_NETWORK_RULE_MATCH_ETHERTYPE;
- nc.rules[nc.ruleCount].v.etherType = (uint16_t)allowedEtherTypes[i];
- ++nc.ruleCount;
- }
- nc.rules[nc.ruleCount++].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
- }
- }
-
- nc.multicastLimit = network.multicastLimit;
-
- bool amActiveBridge = false;
- {
- sqlite3_reset(_sGetActiveBridges);
- sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC);
- while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) {
- const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0);
- if ((ab)&&(strlen(ab) == 10)) {
- const uint64_t ab2 = Utils::hexStrToU64(ab);
- nc.addSpecialist(Address(ab2),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
- if (!strcmp(member.nodeId,ab))
- amActiveBridge = true;
- }
- }
- }
-
- // Do not send relays to 1.1.0 since it had a serious bug in using them
- // 1.1.0 will still work, it'll just fall back to roots instead of using network preferred relays
- if (!((metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0) == 1)&&(metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0) == 1)&&(metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0) == 0))) {
- sqlite3_reset(_sGetRelays);
- sqlite3_bind_text(_sGetRelays,1,network.id,16,SQLITE_STATIC);
- while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
- const char *n = (const char *)sqlite3_column_text(_sGetRelays,0);
- const char *a = (const char *)sqlite3_column_text(_sGetRelays,1);
- if ((n)&&(a)) {
- Address node(n);
- InetAddress addr(a);
- if (node)
- nc.addSpecialist(node,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
- }
- }
- }
-
- sqlite3_reset(_sGetRoutes);
- sqlite3_bind_text(_sGetRoutes,1,network.id,16,SQLITE_STATIC);
- while ((sqlite3_step(_sGetRoutes) == SQLITE_ROW)&&(nc.routeCount < ZT_MAX_NETWORK_ROUTES)) {
- ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]);
- memset(r,0,sizeof(ZT_VirtualNetworkRoute));
- switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
- case 4:
- *(reinterpret_cast<InetAddress *>(&(r->target))) = InetAddress((const void *)((const char *)sqlite3_column_blob(_sGetRoutes,0) + 12),4,(unsigned int)sqlite3_column_int(_sGetRoutes,2));
- break;
- case 6:
- *(reinterpret_cast<InetAddress *>(&(r->target))) = InetAddress((const void *)sqlite3_column_blob(_sGetRoutes,0),16,(unsigned int)sqlite3_column_int(_sGetRoutes,2));
- break;
- default:
- continue;
- }
- if (sqlite3_column_type(_sGetRoutes,1) != SQLITE_NULL) {
- switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
- case 4:
- *(reinterpret_cast<InetAddress *>(&(r->via))) = InetAddress((const void *)((const char *)sqlite3_column_blob(_sGetRoutes,1) + 12),4,0);
- break;
- case 6:
- *(reinterpret_cast<InetAddress *>(&(r->via))) = InetAddress((const void *)sqlite3_column_blob(_sGetRoutes,1),16,0);
- break;
- default:
- continue;
- }
- }
- r->flags = (uint16_t)sqlite3_column_int(_sGetRoutes,4);
- r->metric = (uint16_t)sqlite3_column_int(_sGetRoutes,5);
- ++nc.routeCount;
- }
-
- // Assign special IPv6 addresses if these are enabled
- if (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193) != 0)&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
- nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());
- nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
- }
- if (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE) != 0)&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
- nc.staticIps[nc.staticIpCount++] = InetAddress::makeIpv66plane(nwid,identity.address().toInt());
- nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
- }
-
- // Get managed addresses that are assigned to this member
- bool haveManagedIpv4AutoAssignment = false;
- bool haveManagedIpv6AutoAssignment = false; // "special" NDP-emulated address types do not count
- sqlite3_reset(_sGetIpAssignmentsForNode);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC);
- while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
- const unsigned char *const ipbytes = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,0);
- if ((!ipbytes)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,0) != 16))
- continue;
- //const int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,1);
- const int ipVersion = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
-
- InetAddress ip;
- if (ipVersion == 4)
- ip = InetAddress(ipbytes + 12,4,0);
- else if (ipVersion == 6)
- ip = InetAddress(ipbytes,16,0);
- else continue;
-
- // IP assignments are only pushed if there is a corresponding local route. We also now get the netmask bits from
- // this route, ignoring the netmask bits field of the assigned IP itself. Using that was worthless and a source
- // of user error / poor UX.
- int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ( (!nc.routes[rk].via.ss_family) && (reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->containsAddress(ip)) )
- routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->netmaskBits();
- }
-
- if (routedNetmaskBits > 0) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- ip.setPort(routedNetmaskBits);
- nc.staticIps[nc.staticIpCount++] = ip;
- }
- if (ipVersion == 4)
- haveManagedIpv4AutoAssignment = true;
- else if (ipVersion == 6)
- haveManagedIpv6AutoAssignment = true;
- }
- }
-
- // Auto-assign IPv6 address if auto-assignment is enabled and it's needed
- if ( ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN) != 0) && (!haveManagedIpv6AutoAssignment) && (!amActiveBridge) ) {
- sqlite3_reset(_sGetIpAssignmentPools);
- sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentPools,2,6); // 6 == IPv6
- while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
- const uint8_t *const ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
- const uint8_t *const ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
- if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
- continue;
-
- uint64_t s[2],e[2],x[2],xx[2];
- memcpy(s,ipRangeStartB,16);
- memcpy(e,ipRangeEndB,16);
- s[0] = Utils::ntoh(s[0]);
- s[1] = Utils::ntoh(s[1]);
- e[0] = Utils::ntoh(e[0]);
- e[1] = Utils::ntoh(e[1]);
- x[0] = s[0];
- x[1] = s[1];
-
- for(unsigned int trialCount=0;trialCount<1000;++trialCount) {
- if ((trialCount == 0)&&(e[1] > s[1])&&((e[1] - s[1]) >= 0xffffffffffULL)) {
- // First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that.
- xx[0] = Utils::hton(x[0]);
- xx[1] = Utils::hton(x[1] + identity.address().toInt());
- } else {
- // Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway
- Utils::getSecureRandom((void *)xx,16);
- if ((e[0] > s[0]))
- xx[0] %= (e[0] - s[0]);
- else xx[0] = 0;
- if ((e[1] > s[1]))
- xx[1] %= (e[1] - s[1]);
- else xx[1] = 0;
- xx[0] = Utils::hton(x[0] + xx[0]);
- xx[1] = Utils::hton(x[1] + xx[1]);
- }
-
- InetAddress ip6((const void *)xx,16,0);
-
- // Check if this IP is within a local-to-Ethernet routed network
- int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ( (!nc.routes[rk].via.ss_family) && (nc.routes[rk].target.ss_family == AF_INET6) && (reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->containsAddress(ip6)) )
- routedNetmaskBits = reinterpret_cast<const InetAddress *>(&(nc.routes[rk].target))->netmaskBits();
- }
-
- // If it's routed, then try to claim and assign it and if successful end loop
- if (routedNetmaskBits > 0) {
- sqlite3_reset(_sCheckIfIpIsAllocated);
- sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ip6.rawIpData(),16,SQLITE_STATIC);
- sqlite3_bind_int(_sCheckIfIpIsAllocated,3,6); // 6 == IPv6
- sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
- // No rows returned, so the IP is available
- sqlite3_reset(_sAllocateIp);
- sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- sqlite3_bind_blob(_sAllocateIp,4,(const void *)ip6.rawIpData(),16,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
- sqlite3_bind_int(_sAllocateIp,6,6); // 6 == IPv6
- if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
- ip6.setPort(routedNetmaskBits);
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)
- nc.staticIps[nc.staticIpCount++] = ip6;
- break;
- }
- }
- }
- }
- }
- }
-
- // Auto-assign IPv4 address if auto-assignment is enabled and it's needed
- if ( ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN) != 0) && (!haveManagedIpv4AutoAssignment) && (!amActiveBridge) ) {
- sqlite3_reset(_sGetIpAssignmentPools);
- sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4
- while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
- const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
- const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
- if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
- continue;
-
- uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12)));
- uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12)));
- if ((ipRangeEnd <= ipRangeStart)||(ipRangeStart == 0))
- continue;
- uint32_t ipRangeLen = ipRangeEnd - ipRangeStart;
-
- // Start with the LSB of the member's address
- uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff);
-
- for(uint32_t k=ipRangeStart,trialCount=0;(k<=ipRangeEnd)&&(trialCount < 1000);++k,++trialCount) {
- uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart;
- ++ipTrialCounter;
- if ((ip & 0x000000ff) == 0x000000ff)
- continue; // don't allow addresses that end in .255
-
- // Check if this IP is within a local-to-Ethernet routed network
- int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ((!nc.routes[rk].via.ss_family)&&(nc.routes[rk].target.ss_family == AF_INET)) {
- uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_addr.s_addr));
- int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_port));
- if ((ip & (0xffffffff << (32 - targetBits))) == targetIp) {
- routedNetmaskBits = targetBits;
- break;
- }
- }
- }
-
- // If it's routed, then try to claim and assign it and if successful end loop
- if (routedNetmaskBits > 0) {
- uint32_t ipBlob[4]; // actually a 16-byte blob, we put IPv4s in the last 4 bytes
- ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip);
- sqlite3_reset(_sCheckIfIpIsAllocated);
- sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4
- sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
- // No rows returned, so the IP is available
- sqlite3_reset(_sAllocateIp);
- sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
- sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4
- if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
- v4ip->sin_family = AF_INET;
- v4ip->sin_port = Utils::hton((uint16_t)routedNetmaskBits);
- v4ip->sin_addr.s_addr = Utils::hton(ip);
- }
- break;
- }
- }
- }
- }
- }
- }
- } // end lock
-
- // Perform signing outside lock to enable concurrency
- if (network.isPrivate) {
- CertificateOfMembership com(now,ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA,nwid,identity.address());
- if (com.sign(signingId)) {
- nc.com = com;
- } else {
- return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
- }
-
- return NetworkController::NETCONF_QUERY_OK;
-}
-
-unsigned int SqliteNetworkController::handleControlPlaneHttpGET(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType)
-{
- Mutex::Lock _l(_lock);
- return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType);
-}
-
-unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType)
-{
- if (path.empty())
- return 404;
- Mutex::Lock _l(_lock);
-
- _backupNeeded = true;
-
- if (path[0] == "network") {
-
- if ((path.size() >= 2)&&(path[1].length() == 16)) {
- uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
- char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
-
- int64_t revision = 0;
- sqlite3_reset(_sGetNetworkRevision);
- sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC);
- bool networkExists = false;
- if (sqlite3_step(_sGetNetworkRevision) == SQLITE_ROW) {
- networkExists = true;
- revision = sqlite3_column_int64(_sGetNetworkRevision,0);
- }
-
- if (path.size() >= 3) {
-
- if (!networkExists)
- return 404;
-
- if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
- uint64_t address = Utils::hexStrToU64(path[3].c_str());
- char addrs[24];
- Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
-
- int64_t addToNetworkRevision = 0;
-
- int64_t memberRowId = 0;
- sqlite3_reset(_sGetMember);
- sqlite3_bind_text(_sGetMember,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetMember,2,addrs,10,SQLITE_STATIC);
- bool memberExists = false;
- if (sqlite3_step(_sGetMember) == SQLITE_ROW) {
- memberExists = true;
- memberRowId = sqlite3_column_int64(_sGetMember,0);
- }
-
- if (!memberExists) {
- sqlite3_reset(_sCreateMember);
- sqlite3_bind_text(_sCreateMember,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateMember,2,addrs,10,SQLITE_STATIC);
- sqlite3_bind_int(_sCreateMember,3,0);
- sqlite3_bind_text(_sCreateMember,4,nwids,16,SQLITE_STATIC);
- if (sqlite3_step(_sCreateMember) != SQLITE_DONE)
- return 500;
- memberRowId = (int64_t)sqlite3_last_insert_rowid(_db);
-
- sqlite3_reset(_sIncrementMemberRevisionCounter);
- sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sIncrementMemberRevisionCounter);
- addToNetworkRevision = 1;
- }
-
- json_value *j = json_parse(body.c_str(),body.length());
- if (j) {
- if (j->type == json_object) {
- for(unsigned int k=0;k<j->u.object.length;++k) {
-
- if (!strcmp(j->u.object.values[k].name,"authorized")) {
- if (j->u.object.values[k].value->type == json_boolean) {
- sqlite3_reset(_sUpdateMemberAuthorized);
- sqlite3_bind_int(_sUpdateMemberAuthorized,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
- sqlite3_bind_text(_sUpdateMemberAuthorized,2,nwids,16,SQLITE_STATIC);
- sqlite3_bind_int64(_sUpdateMemberAuthorized,3,memberRowId);
- if (sqlite3_step(_sUpdateMemberAuthorized) != SQLITE_DONE)
- return 500;
-
- sqlite3_reset(_sIncrementMemberRevisionCounter);
- sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sIncrementMemberRevisionCounter);
- addToNetworkRevision = 1;
- }
- } else if (!strcmp(j->u.object.values[k].name,"activeBridge")) {
- if (j->u.object.values[k].value->type == json_boolean) {
- sqlite3_reset(_sUpdateMemberActiveBridge);
- sqlite3_bind_int(_sUpdateMemberActiveBridge,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
- sqlite3_bind_text(_sUpdateMemberActiveBridge,2,nwids,16,SQLITE_STATIC);
- sqlite3_bind_int64(_sUpdateMemberActiveBridge,3,memberRowId);
- if (sqlite3_step(_sUpdateMemberActiveBridge) != SQLITE_DONE)
- return 500;
-
- sqlite3_reset(_sIncrementMemberRevisionCounter);
- sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sIncrementMemberRevisionCounter);
- addToNetworkRevision = 1;
- }
- } else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) {
- if (j->u.object.values[k].value->type == json_array) {
- sqlite3_reset(_sDeleteIpAllocations);
- sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC);
- sqlite3_bind_int(_sDeleteIpAllocations,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- if (sqlite3_step(_sDeleteIpAllocations) != SQLITE_DONE)
- return 500;
- for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
- json_value *ipalloc = j->u.object.values[k].value->u.array.values[kk];
- if (ipalloc->type == json_string) {
- InetAddress a(ipalloc->u.string.ptr);
- char ipBlob[16];
- int ipVersion = 0;
- _ipToBlob(a,ipBlob,ipVersion);
- if (ipVersion > 0) {
- sqlite3_reset(_sAllocateIp);
- sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sAllocateIp,2,addrs,10,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,5,(int)a.netmaskBits()); // NOTE: this field is now ignored but set it anyway
- sqlite3_bind_int(_sAllocateIp,6,ipVersion);
- if (sqlite3_step(_sAllocateIp) != SQLITE_DONE)
- return 500;
- }
- }
- }
- addToNetworkRevision = 1;
- }
- } else if (!strcmp(j->u.object.values[k].name,"identity")) {
- // Identity is technically an immutable field, but if the member's Node has
- // no identity we allow it to be populated. This is primarily for migrating
- // node data from another controller.
- json_value *idstr = j->u.object.values[k].value;
- if (idstr->type == json_string) {
- bool alreadyHaveIdentity = false;
-
- sqlite3_reset(_sGetNodeIdentity);
- sqlite3_bind_text(_sGetNodeIdentity,1,addrs,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) {
- const char *tmp2 = (const char *)sqlite3_column_text(_sGetNodeIdentity,0);
- if ((tmp2)&&(tmp2[0]))
- alreadyHaveIdentity = true;
- }
-
- if (!alreadyHaveIdentity) {
- try {
- Identity id2(idstr->u.string.ptr);
- if (id2) {
- std::string idstr2(id2.toString(false)); // object must persist until after sqlite3_step() for SQLITE_STATIC
- sqlite3_reset(_sCreateOrReplaceNode);
- sqlite3_bind_text(_sCreateOrReplaceNode,1,addrs,10,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr2.c_str(),-1,SQLITE_STATIC);
- sqlite3_step(_sCreateOrReplaceNode);
- }
- } catch ( ... ) {} // ignore invalid identities
- }
- }
- }
-
- }
- }
- json_value_free(j);
- }
-
- if ((addToNetworkRevision > 0)&&(revision > 0)) {
- sqlite3_reset(_sSetNetworkRevision);
- sqlite3_bind_int64(_sSetNetworkRevision,1,revision + addToNetworkRevision);
- sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sSetNetworkRevision);
- }
-
- return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType);
- } else if ((path.size() == 3)&&(path[2] == "test")) {
- ZT_CircuitTest *test = (ZT_CircuitTest *)malloc(sizeof(ZT_CircuitTest));
- memset(test,0,sizeof(ZT_CircuitTest));
-
- Utils::getSecureRandom(&(test->testId),sizeof(test->testId));
- test->credentialNetworkId = nwid;
- test->ptr = (void *)this;
-
- json_value *j = json_parse(body.c_str(),body.length());
- if (j) {
- if (j->type == json_object) {
- for(unsigned int k=0;k<j->u.object.length;++k) {
-
- if (!strcmp(j->u.object.values[k].name,"hops")) {
- if (j->u.object.values[k].value->type == json_array) {
- for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
- json_value *hop = j->u.object.values[k].value->u.array.values[kk];
- if (hop->type == json_array) {
- for(unsigned int kkk=0;kkk<hop->u.array.length;++kkk) {
- if (hop->u.array.values[kkk]->type == json_string) {
- test->hops[test->hopCount].addresses[test->hops[test->hopCount].breadth++] = Utils::hexStrToU64(hop->u.array.values[kkk]->u.string.ptr) & 0xffffffffffULL;
- }
- }
- ++test->hopCount;
- }
- }
- }
- } else if (!strcmp(j->u.object.values[k].name,"reportAtEveryHop")) {
- if (j->u.object.values[k].value->type == json_boolean)
- test->reportAtEveryHop = (j->u.object.values[k].value->u.boolean == 0) ? 0 : 1;
- }
-
- }
- }
- json_value_free(j);
- }
-
- if (!test->hopCount) {
- ::free((void *)test);
- return 500;
- }
-
- test->timestamp = OSUtils::now();
-
- _CircuitTestEntry &te = _circuitTests[test->testId];
- te.test = test;
- te.jsonResults = "";
-
- _node->circuitTestBegin(test,&(SqliteNetworkController::_circuitTestCallback));
-
- char json[1024];
- Utils::snprintf(json,sizeof(json),"{\"testId\":\"%.16llx\"}",test->testId);
- responseBody = json;
- responseContentType = "application/json";
-
- return 200;
- } // else 404
-
- } else {
- std::vector<std::string> path_copy(path);
-
- if (!networkExists) {
- if (path[1].substr(10) == "______") {
- // A special POST /network/##########______ feature lets users create a network
- // with an arbitrary unused network number at this controller.
- nwid = 0;
-
- uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL;
- uint64_t nwidPostfix = 0;
- Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix));
- uint64_t nwidOriginalPostfix = nwidPostfix;
- do {
- uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL);
- if (!nwidPostfix)
- tryNwid |= 1;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)tryNwid);
-
- sqlite3_reset(_sGetNetworkRevision);
- sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC);
- if (sqlite3_step(_sGetNetworkRevision) != SQLITE_ROW) {
- nwid = tryNwid;
- break;
- }
-
- ++nwidPostfix;
- } while (nwidPostfix != nwidOriginalPostfix);
-
- // 503 means we have no more free IDs for this prefix. You shouldn't host anywhere
- // near 16 million networks on the same controller, so shouldn't happen.
- if (!nwid)
- return 503;
- }
-
- sqlite3_reset(_sCreateNetwork);
- sqlite3_bind_text(_sCreateNetwork,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateNetwork,2,"",0,SQLITE_STATIC);
- sqlite3_bind_int64(_sCreateNetwork,3,(long long)OSUtils::now());
- if (sqlite3_step(_sCreateNetwork) != SQLITE_DONE)
- return 500;
- path_copy[1].assign(nwids);
- }
-
- json_value *j = json_parse(body.c_str(),body.length());
- if (j) {
- if (j->type == json_object) {
- for(unsigned int k=0;k<j->u.object.length;++k) {
- sqlite3_stmt *stmt = (sqlite3_stmt *)0;
-
- if (!strcmp(j->u.object.values[k].name,"name")) {
- if ((j->u.object.values[k].value->type == json_string)&&(j->u.object.values[k].value->u.string.ptr[0])) {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"name\" = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
- }
- } else if (!strcmp(j->u.object.values[k].name,"private")) {
- if (j->u.object.values[k].value->type == json_boolean) {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"private\" = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
- }
- } else if (!strcmp(j->u.object.values[k].name,"enableBroadcast")) {
- if (j->u.object.values[k].value->type == json_boolean) {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET enableBroadcast = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
- }
- } else if (!strcmp(j->u.object.values[k].name,"allowPassiveBridging")) {
- if (j->u.object.values[k].value->type == json_boolean) {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET allowPassiveBridging = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
- }
- } else if (!strcmp(j->u.object.values[k].name,"v4AssignMode")) {
- if ((j->u.object.values[k].value->type == json_string)&&(!strcmp(j->u.object.values[k].value->u.string.ptr,"zt"))) {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"flags\" = (\"flags\" | ?) WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(int)ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN);
- } else {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"flags\" = (\"flags\" & ?) WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(int)(ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN ^ 0xfffffff));
- }
- } else if (!strcmp(j->u.object.values[k].name,"v6AssignMode")) {
- int fl = 0;
- if (j->u.object.values[k].value->type == json_string) {
- char *saveptr = (char *)0;
- for(char *f=Utils::stok(j->u.object.values[k].value->u.string.ptr,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
- if (!strcmp(f,"rfc4193"))
- fl |= ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193;
- else if (!strcmp(f,"6plane"))
- fl |= ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE;
- else if (!strcmp(f,"zt"))
- fl |= ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN;
- }
- }
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"flags\" = ((\"flags\" & " ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_MASK_S ") | ?) WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,fl);
- } else if (!strcmp(j->u.object.values[k].name,"multicastLimit")) {
- if (j->u.object.values[k].value->type == json_integer) {
- if (sqlite3_prepare_v2(_db,"UPDATE Network SET multicastLimit = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK)
- sqlite3_bind_int(stmt,1,(int)j->u.object.values[k].value->u.integer);
- }
- } else if (!strcmp(j->u.object.values[k].name,"relays")) {
- if (j->u.object.values[k].value->type == json_array) {
- std::map<Address,InetAddress> nodeIdToPhyAddress;
- for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
- json_value *relay = j->u.object.values[k].value->u.array.values[kk];
- const char *address = (const char *)0;
- const char *phyAddress = (const char *)0;
- if ((relay)&&(relay->type == json_object)) {
- for(unsigned int rk=0;rk<relay->u.object.length;++rk) {
- if ((!strcmp(relay->u.object.values[rk].name,"address"))&&(relay->u.object.values[rk].value->type == json_string))
- address = relay->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(relay->u.object.values[rk].name,"phyAddress"))&&(relay->u.object.values[rk].value->type == json_string))
- phyAddress = relay->u.object.values[rk].value->u.string.ptr;
- }
- }
- if ((address)&&(phyAddress))
- nodeIdToPhyAddress[Address(address)] = InetAddress(phyAddress);
- }
-
- sqlite3_reset(_sDeleteRelaysForNetwork);
- sqlite3_bind_text(_sDeleteRelaysForNetwork,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sDeleteRelaysForNetwork);
-
- for(std::map<Address,InetAddress>::iterator rl(nodeIdToPhyAddress.begin());rl!=nodeIdToPhyAddress.end();++rl) {
- sqlite3_reset(_sCreateRelay);
- sqlite3_bind_text(_sCreateRelay,1,nwids,16,SQLITE_STATIC);
- std::string a(rl->first.toString()),b(rl->second.toString()); // don't destroy strings until sqlite3_step()
- sqlite3_bind_text(_sCreateRelay,2,a.c_str(),-1,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateRelay,3,b.c_str(),-1,SQLITE_STATIC);
- sqlite3_step(_sCreateRelay);
- }
- }
- } else if (!strcmp(j->u.object.values[k].name,"routes")) {
- sqlite3_reset(_sDeleteRoutes);
- sqlite3_bind_text(_sDeleteRoutes,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sDeleteRoutes);
- if (j->u.object.values[k].value->type == json_array) {
- for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
- json_value *r = j->u.object.values[k].value->u.array.values[kk];
- if ((r)&&(r->type == json_object)) {
- InetAddress r_target,r_via;
- int r_flags = 0;
- int r_metric = 0;
- for(unsigned int rk=0;rk<r->u.object.length;++rk) {
- if ((!strcmp(r->u.object.values[rk].name,"target"))&&(r->u.object.values[rk].value->type == json_string))
- r_target = InetAddress(std::string(r->u.object.values[rk].value->u.string.ptr));
- else if ((!strcmp(r->u.object.values[rk].name,"via"))&&(r->u.object.values[rk].value->type == json_string))
- r_via = InetAddress(std::string(r->u.object.values[rk].value->u.string.ptr),0);
- else if ((!strcmp(r->u.object.values[rk].name,"flags"))&&(r->u.object.values[rk].value->type == json_integer))
- r_flags = (int)(r->u.object.values[rk].value->u.integer & 0xffff);
- else if ((!strcmp(r->u.object.values[rk].name,"metric"))&&(r->u.object.values[rk].value->type == json_integer))
- r_metric = (int)(r->u.object.values[rk].value->u.integer & 0xffff);
- }
- if ((r_target)&&((!r_via)||(r_via.ss_family == r_target.ss_family))) {
- int r_ipVersion = 0;
- char r_targetBlob[16];
- char r_viaBlob[16];
- _ipToBlob(r_target,r_targetBlob,r_ipVersion);
- if (r_ipVersion) {
- int r_targetNetmaskBits = r_target.netmaskBits();
- if ((r_ipVersion == 4)&&(r_targetNetmaskBits > 32)) r_targetNetmaskBits = 32;
- else if ((r_ipVersion == 6)&&(r_targetNetmaskBits > 128)) r_targetNetmaskBits = 128;
- sqlite3_reset(_sCreateRoute);
- sqlite3_bind_text(_sCreateRoute,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCreateRoute,2,(const void *)r_targetBlob,16,SQLITE_STATIC);
- if (r_via) {
- _ipToBlob(r_via,r_viaBlob,r_ipVersion);
- sqlite3_bind_blob(_sCreateRoute,3,(const void *)r_viaBlob,16,SQLITE_STATIC);
- } else {
- sqlite3_bind_null(_sCreateRoute,3);
- }
- sqlite3_bind_int(_sCreateRoute,4,r_targetNetmaskBits);
- sqlite3_bind_int(_sCreateRoute,5,r_ipVersion);
- sqlite3_bind_int(_sCreateRoute,6,r_flags);
- sqlite3_bind_int(_sCreateRoute,7,r_metric);
- sqlite3_step(_sCreateRoute);
- }
- }
- }
- }
- }
- } else if (!strcmp(j->u.object.values[k].name,"ipAssignmentPools")) {
- if (j->u.object.values[k].value->type == json_array) {
- std::vector< std::pair<InetAddress,InetAddress> > pools;
- for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
- json_value *pool = j->u.object.values[k].value->u.array.values[kk];
- const char *iprs = (const char *)0;
- const char *ipre = (const char *)0;
- if ((pool)&&(pool->type == json_object)) {
- for(unsigned int rk=0;rk<pool->u.object.length;++rk) {
- if ((!strcmp(pool->u.object.values[rk].name,"ipRangeStart"))&&(pool->u.object.values[rk].value->type == json_string))
- iprs = pool->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(pool->u.object.values[rk].name,"ipRangeEnd"))&&(pool->u.object.values[rk].value->type == json_string))
- ipre = pool->u.object.values[rk].value->u.string.ptr;
- }
- }
- if ((iprs)&&(ipre)) {
- InetAddress iprs2(iprs);
- InetAddress ipre2(ipre);
- if (iprs2.ss_family == ipre2.ss_family) {
- iprs2.setPort(0);
- ipre2.setPort(0);
- pools.push_back(std::pair<InetAddress,InetAddress>(iprs2,ipre2));
- }
- }
- }
- std::sort(pools.begin(),pools.end());
- pools.erase(std::unique(pools.begin(),pools.end()),pools.end());
-
- sqlite3_reset(_sDeleteIpAssignmentPoolsForNetwork);
- sqlite3_bind_text(_sDeleteIpAssignmentPoolsForNetwork,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sDeleteIpAssignmentPoolsForNetwork);
-
- for(std::vector< std::pair<InetAddress,InetAddress> >::const_iterator p(pools.begin());p!=pools.end();++p) {
- char ipBlob1[16],ipBlob2[16];
- sqlite3_reset(_sCreateIpAssignmentPool);
- sqlite3_bind_text(_sCreateIpAssignmentPool,1,nwids,16,SQLITE_STATIC);
- if (p->first.ss_family == AF_INET) {
- memset(ipBlob1,0,12);
- memcpy(ipBlob1 + 12,p->first.rawIpData(),4);
- memset(ipBlob2,0,12);
- memcpy(ipBlob2 + 12,p->second.rawIpData(),4);
- sqlite3_bind_blob(_sCreateIpAssignmentPool,2,(const void *)ipBlob1,16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCreateIpAssignmentPool,3,(const void *)ipBlob2,16,SQLITE_STATIC);
- sqlite3_bind_int(_sCreateIpAssignmentPool,4,4);
- } else if (p->first.ss_family == AF_INET6) {
- sqlite3_bind_blob(_sCreateIpAssignmentPool,2,p->first.rawIpData(),16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCreateIpAssignmentPool,3,p->second.rawIpData(),16,SQLITE_STATIC);
- sqlite3_bind_int(_sCreateIpAssignmentPool,4,6);
- } else continue;
- sqlite3_step(_sCreateIpAssignmentPool);
- }
- }
- } else if (!strcmp(j->u.object.values[k].name,"rules")) {
- if (j->u.object.values[k].value->type == json_array) {
- sqlite3_reset(_sDeleteRulesForNetwork);
- sqlite3_bind_text(_sDeleteRulesForNetwork,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sDeleteRulesForNetwork);
-
- for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
- json_value *rj = j->u.object.values[k].value->u.array.values[kk];
- if ((rj)&&(rj->type == json_object)) {
- struct { // NULL pointers indicate missing or NULL -- wildcards
- const json_int_t *ruleNo;
- const char *nodeId;
- const char *sourcePort;
- const char *destPort;
- const json_int_t *vlanId;
- const json_int_t *vlanPcp;
- const json_int_t *etherType;
- const char *macSource;
- const char *macDest;
- const char *ipSource;
- const char *ipDest;
- const json_int_t *ipTos;
- const json_int_t *ipProtocol;
- const json_int_t *ipSourcePort;
- const json_int_t *ipDestPort;
- const json_int_t *flags;
- const json_int_t *invFlags;
- const char *action;
- } rule;
- memset(&rule,0,sizeof(rule));
-
- for(unsigned int rk=0;rk<rj->u.object.length;++rk) {
- if ((!strcmp(rj->u.object.values[rk].name,"ruleNo"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.ruleNo = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"nodeId"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.nodeId = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"sourcePort"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.sourcePort = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"destPort"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.destPort = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"vlanId"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.vlanId = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"vlanPcp"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.vlanPcp = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"etherType"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.etherType = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"macSource"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.macSource = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"macDest"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.macDest = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"ipSource"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.ipSource = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"ipDest"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.ipDest = rj->u.object.values[rk].value->u.string.ptr;
- else if ((!strcmp(rj->u.object.values[rk].name,"ipTos"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.ipTos = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"ipProtocol"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.ipProtocol = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"ipSourcePort"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.ipSourcePort = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"ipDestPort"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.ipDestPort = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"flags"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.flags = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"invFlags"))&&(rj->u.object.values[rk].value->type == json_integer))
- rule.invFlags = &(rj->u.object.values[rk].value->u.integer);
- else if ((!strcmp(rj->u.object.values[rk].name,"action"))&&(rj->u.object.values[rk].value->type == json_string))
- rule.action = rj->u.object.values[rk].value->u.string.ptr;
- }
-
- if ((rule.ruleNo)&&(rule.action)&&(rule.action[0])) {
- char mactmp1[16],mactmp2[16];
- sqlite3_reset(_sCreateRule);
- sqlite3_bind_text(_sCreateRule,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_int64(_sCreateRule,2,*rule.ruleNo);
-
- // Optional values: null by default
- for(int i=3;i<=18;++i)
- sqlite3_bind_null(_sCreateRule,i);
- if ((rule.nodeId)&&(strlen(rule.nodeId) == 10)) sqlite3_bind_text(_sCreateRule,3,rule.nodeId,10,SQLITE_STATIC);
- if ((rule.sourcePort)&&(strlen(rule.sourcePort) == 10)) sqlite3_bind_text(_sCreateRule,4,rule.sourcePort,10,SQLITE_STATIC);
- if ((rule.destPort)&&(strlen(rule.destPort) == 10)) sqlite3_bind_text(_sCreateRule,5,rule.destPort,10,SQLITE_STATIC);
- if (rule.vlanId) sqlite3_bind_int(_sCreateRule,6,(int)*rule.vlanId);
- if (rule.vlanPcp) sqlite3_bind_int(_sCreateRule,7,(int)*rule.vlanPcp);
- if (rule.etherType) sqlite3_bind_int(_sCreateRule,8,(int)*rule.etherType & (int)0xffff);
- if (rule.macSource) {
- MAC m(rule.macSource);
- Utils::snprintf(mactmp1,sizeof(mactmp1),"%.12llx",(unsigned long long)m.toInt());
- sqlite3_bind_text(_sCreateRule,9,mactmp1,-1,SQLITE_STATIC);
- }
- if (rule.macDest) {
- MAC m(rule.macDest);
- Utils::snprintf(mactmp2,sizeof(mactmp2),"%.12llx",(unsigned long long)m.toInt());
- sqlite3_bind_text(_sCreateRule,10,mactmp2,-1,SQLITE_STATIC);
- }
- if (rule.ipSource) sqlite3_bind_text(_sCreateRule,11,rule.ipSource,-1,SQLITE_STATIC);
- if (rule.ipDest) sqlite3_bind_text(_sCreateRule,12,rule.ipDest,-1,SQLITE_STATIC);
- if (rule.ipTos) sqlite3_bind_int(_sCreateRule,13,(int)*rule.ipTos);
- if (rule.ipProtocol) sqlite3_bind_int(_sCreateRule,14,(int)*rule.ipProtocol);
- if (rule.ipSourcePort) sqlite3_bind_int(_sCreateRule,15,(int)*rule.ipSourcePort & (int)0xffff);
- if (rule.ipDestPort) sqlite3_bind_int(_sCreateRule,16,(int)*rule.ipDestPort & (int)0xffff);
- if (rule.flags) sqlite3_bind_int64(_sCreateRule,17,(int64_t)*rule.flags);
- if (rule.invFlags) sqlite3_bind_int64(_sCreateRule,18,(int64_t)*rule.invFlags);
-
- sqlite3_bind_text(_sCreateRule,19,rule.action,-1,SQLITE_STATIC);
- sqlite3_step(_sCreateRule);
- }
- }
- }
- }
- }
-
- if (stmt) {
- sqlite3_bind_text(stmt,2,nwids,16,SQLITE_STATIC);
- sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- }
- }
- }
- json_value_free(j);
- }
-
- sqlite3_reset(_sSetNetworkRevision);
- sqlite3_bind_int64(_sSetNetworkRevision,1,revision += 1);
- sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sSetNetworkRevision);
-
- return _doCPGet(path_copy,urlArgs,headers,body,responseBody,responseContentType);
- }
-
- } // else 404
-
- } // else 404
-
- return 404;
-}
-
-unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType)
-{
- if (path.empty())
- return 404;
- Mutex::Lock _l(_lock);
-
- _backupNeeded = true;
-
- if (path[0] == "network") {
-
- if ((path.size() >= 2)&&(path[1].length() == 16)) {
- uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
- char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
-
- sqlite3_reset(_sGetNetworkById);
- sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC);
- if (sqlite3_step(_sGetNetworkById) != SQLITE_ROW)
- return 404;
-
- if (path.size() >= 3) {
-
- if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
- uint64_t address = Utils::hexStrToU64(path[3].c_str());
- char addrs[24];
- Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
-
- sqlite3_reset(_sGetMember);
- sqlite3_bind_text(_sGetMember,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetMember,2,addrs,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetMember) != SQLITE_ROW)
- return 404;
-
- sqlite3_reset(_sDeleteIpAllocations);
- sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC);
- sqlite3_bind_int(_sDeleteIpAllocations,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- if (sqlite3_step(_sDeleteIpAllocations) == SQLITE_DONE) {
- sqlite3_reset(_sDeleteMember);
- sqlite3_bind_text(_sDeleteMember,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sDeleteMember,2,addrs,10,SQLITE_STATIC);
- if (sqlite3_step(_sDeleteMember) != SQLITE_DONE)
- return 500;
- } else return 500;
-
- return 200;
- }
-
- } else {
-
- sqlite3_reset(_sDeleteNetwork);
- sqlite3_bind_text(_sDeleteNetwork,1,nwids,16,SQLITE_STATIC);
- if (sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) {
- sqlite3_reset(_sDeleteAllNetworkMembers);
- sqlite3_bind_text(_sDeleteAllNetworkMembers,1,nwids,16,SQLITE_STATIC);
- sqlite3_step(_sDeleteAllNetworkMembers);
- return 200;
- } else return 500;
-
- }
- } // else 404
-
- } // else 404
-
- return 404;
-}
-
-void SqliteNetworkController::threadMain()
- throw()
-{
- uint64_t lastBackupTime = OSUtils::now();
- uint64_t lastCleanupTime = OSUtils::now();
-
- while (_backupThreadRun) {
- if ((OSUtils::now() - lastCleanupTime) >= 5000) {
- const uint64_t now = OSUtils::now();
- lastCleanupTime = now;
-
- Mutex::Lock _l(_lock);
-
- // Clean out really old circuit tests to prevent memory build-up
- for(std::map< uint64_t,_CircuitTestEntry >::iterator ct(_circuitTests.begin());ct!=_circuitTests.end();) {
- if (!ct->second.test) {
- _circuitTests.erase(ct++);
- } else if ((now - ct->second.test->timestamp) >= ZT_SQLITENETWORKCONTROLLER_CIRCUIT_TEST_TIMEOUT) {
- _node->circuitTestEnd(ct->second.test);
- ::free((void *)ct->second.test);
- _circuitTests.erase(ct++);
- } else ++ct;
- }
- }
-
- if (((OSUtils::now() - lastBackupTime) >= ZT_NETCONF_BACKUP_PERIOD)&&(_backupNeeded)) {
- lastBackupTime = OSUtils::now();
-
- char backupPath[4096],backupPath2[4096];
- Utils::snprintf(backupPath,sizeof(backupPath),"%s.backupInProgress",_dbPath.c_str());
- Utils::snprintf(backupPath2,sizeof(backupPath),"%s.backup",_dbPath.c_str());
- OSUtils::rm(backupPath); // delete any unfinished backups
-
- sqlite3 *bakdb = (sqlite3 *)0;
- sqlite3_backup *bak = (sqlite3_backup *)0;
- if (sqlite3_open_v2(backupPath,&bakdb,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) {
- fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_open_v2()"ZT_EOL_S);
- continue;
- }
- bak = sqlite3_backup_init(bakdb,"main",_db,"main");
- if (!bak) {
- sqlite3_close(bakdb);
- OSUtils::rm(backupPath); // delete any unfinished backups
- fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_backup_init()"ZT_EOL_S);
- continue;
- }
-
- int rc = SQLITE_OK;
- for(;;) {
- if (!_backupThreadRun) {
- sqlite3_backup_finish(bak);
- sqlite3_close(bakdb);
- OSUtils::rm(backupPath);
- return;
- }
- _lock.lock();
- rc = sqlite3_backup_step(bak,64);
- _lock.unlock();
- if ((rc == SQLITE_OK)||(rc == SQLITE_LOCKED)||(rc == SQLITE_BUSY))
- Thread::sleep(50);
- else break;
- }
-
- sqlite3_backup_finish(bak);
- sqlite3_close(bakdb);
-
- OSUtils::rm(backupPath2);
- ::rename(backupPath,backupPath2);
-
- _backupNeeded = false;
- }
-
- Thread::sleep(250);
- }
-}
-
-unsigned int SqliteNetworkController::_doCPGet(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType)
-{
- // Assumes _lock is locked
- char json[65536];
-
- if ((path.size() > 0)&&(path[0] == "network")) {
-
- if ((path.size() >= 2)&&(path[1].length() == 16)) {
- uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
- char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
-
- if (path.size() >= 3) {
- // /network/<nwid>/...
-
- if (path[2] == "member") {
-
- if (path.size() >= 4) {
- // Get specific member info
-
- uint64_t address = Utils::hexStrToU64(path[3].c_str());
- char addrs[24];
- Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
-
- sqlite3_reset(_sGetMember2);
- sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetMember2) == SQLITE_ROW) {
- const char *memberIdStr = (const char *)sqlite3_column_text(_sGetMember2,3);
-
- Utils::snprintf(json,sizeof(json),
- "{\n"
- "\t\"nwid\": \"%s\",\n"
- "\t\"address\": \"%s\",\n"
- "\t\"controllerInstanceId\": \"%s\",\n"
- "\t\"authorized\": %s,\n"
- "\t\"activeBridge\": %s,\n"
- "\t\"memberRevision\": %llu,\n"
- "\t\"clock\": %llu,\n"
- "\t\"identity\": \"%s\",\n"
- "\t\"ipAssignments\": [",
- nwids,
- addrs,
- _instanceId.c_str(),
- (sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false",
- (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false",
- (unsigned long long)sqlite3_column_int64(_sGetMember2,2),
- (unsigned long long)OSUtils::now(),
- _jsonEscape(memberIdStr).c_str());
- responseBody = json;
-
- sqlite3_reset(_sGetIpAssignmentsForNode);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,2,addrs,10,SQLITE_STATIC);
- bool firstIp = true;
- while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
- int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
- char ipBlob[16];
- memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode,0),16);
- InetAddress ip(
- (const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]),
- (ipversion == 6 ? 16 : 4),
- (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode,1)
- );
- responseBody.append(firstIp ? "\"" : ",\"");
- responseBody.append(_jsonEscape(ip.toIpString()));
- responseBody.push_back('"');
- firstIp = false;
- }
-
- responseBody.append("],\n\t\"recentLog\": [");
-
- const void *histb = sqlite3_column_blob(_sGetMember2,6);
- if (histb) {
- MemberRecentHistory rh;
- rh.fromBlob((const char *)histb,sqlite3_column_bytes(_sGetMember2,6));
- for(MemberRecentHistory::const_iterator i(rh.begin());i!=rh.end();++i) {
- if (i != rh.begin())
- responseBody.push_back(',');
- responseBody.append(*i);
- }
- }
-
- responseBody.append("]\n}\n");
-
- responseContentType = "application/json";
- return 200;
- } // else 404
-
- } else {
- // List members
-
- sqlite3_reset(_sListNetworkMembers);
- sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC);
- responseBody.push_back('{');
- bool firstMember = true;
- while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) {
- responseBody.append(firstMember ? "\"" : ",\"");
- firstMember = false;
- responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0));
- responseBody.append("\":");
- responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,1));
- }
- responseBody.push_back('}');
- responseContentType = "application/json";
- return 200;
-
- }
-
- } else if ((path[2] == "active")&&(path.size() == 3)) {
-
- sqlite3_reset(_sGetActiveNodesOnNetwork);
- sqlite3_bind_text(_sGetActiveNodesOnNetwork,1,nwids,16,SQLITE_STATIC);
- sqlite3_bind_int64(_sGetActiveNodesOnNetwork,2,(int64_t)(OSUtils::now() - ZT_NETCONF_NODE_ACTIVE_THRESHOLD));
-
- responseBody.push_back('{');
- bool firstActiveMember = true;
- while (sqlite3_step(_sGetActiveNodesOnNetwork) == SQLITE_ROW) {
- const char *nodeId = (const char *)sqlite3_column_text(_sGetActiveNodesOnNetwork,0);
- const char *rhblob = (const char *)sqlite3_column_blob(_sGetActiveNodesOnNetwork,1);
- if ((nodeId)&&(rhblob)) {
- MemberRecentHistory rh;
- rh.fromBlob(rhblob,sqlite3_column_bytes(_sGetActiveNodesOnNetwork,1));
- if (rh.size() > 0) {
- if (firstActiveMember) {
- firstActiveMember = false;
- } else {
- responseBody.push_back(',');
- }
- responseBody.push_back('"');
- responseBody.append(nodeId);
- responseBody.append("\":");
- responseBody.append(rh.front());
- }
- }
- }
- responseBody.push_back('}');
-
- responseContentType = "application/json";
- return 200;
-
- } else if ((path[2] == "test")&&(path.size() >= 4)) {
-
- std::map< uint64_t,_CircuitTestEntry >::iterator cte(_circuitTests.find(Utils::hexStrToU64(path[3].c_str())));
- if ((cte != _circuitTests.end())&&(cte->second.test)) {
-
- responseBody = "[";
- responseBody.append(cte->second.jsonResults);
- responseBody.push_back(']');
- responseContentType = "application/json";
-
- return 200;
-
- } // else 404
-
- } // else 404
-
- } else {
-
- sqlite3_reset(_sGetNetworkById);
- sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC);
- if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
- unsigned int fl = (unsigned int)sqlite3_column_int(_sGetNetworkById,4);
- std::string v6modes;
- if ((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193) != 0)
- v6modes.append("rfc4193");
- if ((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE) != 0) {
- if (v6modes.length() > 0)
- v6modes.push_back(',');
- v6modes.append("6plane");
- }
- if ((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_AUTO_ASSIGN) != 0) {
- if (v6modes.length() > 0)
- v6modes.push_back(',');
- v6modes.append("zt");
- }
-
- Utils::snprintf(json,sizeof(json),
- "{\n"
- "\t\"nwid\": \"%s\",\n"
- "\t\"controllerInstanceId\": \"%s\",\n"
- "\t\"clock\": %llu,\n"
- "\t\"name\": \"%s\",\n"
- "\t\"private\": %s,\n"
- "\t\"enableBroadcast\": %s,\n"
- "\t\"allowPassiveBridging\": %s,\n"
- "\t\"v4AssignMode\": \"%s\",\n"
- "\t\"v6AssignMode\": \"%s\",\n"
- "\t\"multicastLimit\": %d,\n"
- "\t\"creationTime\": %llu,\n"
- "\t\"revision\": %llu,\n"
- "\t\"memberRevisionCounter\": %llu,\n"
- "\t\"authorizedMemberCount\": %llu,\n"
- "\t\"relays\": [",
- nwids,
- _instanceId.c_str(),
- (unsigned long long)OSUtils::now(),
- _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(),
- (sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false",
- (sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false",
- (sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false",
- (((fl & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4_AUTO_ASSIGN) != 0) ? "zt" : ""),
- v6modes.c_str(),
- sqlite3_column_int(_sGetNetworkById,5),
- (unsigned long long)sqlite3_column_int64(_sGetNetworkById,6),
- (unsigned long long)sqlite3_column_int64(_sGetNetworkById,7),
- (unsigned long long)sqlite3_column_int64(_sGetNetworkById,8),
- (unsigned long long)sqlite3_column_int64(_sGetNetworkById,9));
- responseBody = json;
-
- sqlite3_reset(_sGetRelays);
- sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC);
- bool firstRelay = true;
- while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
- responseBody.append(firstRelay ? "\n\t\t" : ",\n\t\t");
- firstRelay = false;
- responseBody.append("{\"address\":\"");
- responseBody.append((const char *)sqlite3_column_text(_sGetRelays,0));
- responseBody.append("\",\"phyAddress\":\"");
- responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1)));
- responseBody.append("\"}");
- }
-
- responseBody.append("],\n\t\"routes\": [");
-
- sqlite3_reset(_sGetRoutes);
- sqlite3_bind_text(_sGetRoutes,1,nwids,16,SQLITE_STATIC);
- bool firstRoute = true;
- while (sqlite3_step(_sGetRoutes) == SQLITE_ROW) {
- responseBody.append(firstRoute ? "\n\t\t" : ",\n\t\t");
- firstRoute = false;
- responseBody.append("{\"target\":");
- char tmp[128];
- const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetRoutes,0);
- switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
- case 4:
- Utils::snprintf(tmp,sizeof(tmp),"\"%d.%d.%d.%d/%d\"",(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15],sqlite3_column_int(_sGetRoutes,2));
- break;
- case 6:
- Utils::snprintf(tmp,sizeof(tmp),"\"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d\"",(int)ip[0],(int)ip[1],(int)ip[2],(int)ip[3],(int)ip[4],(int)ip[5],(int)ip[6],(int)ip[7],(int)ip[8],(int)ip[9],(int)ip[10],(int)ip[11],(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15],sqlite3_column_int(_sGetRoutes,2));
- break;
- }
- responseBody.append(tmp);
- if (sqlite3_column_type(_sGetRoutes,1) == SQLITE_NULL) {
- responseBody.append(",\"via\":null");
- } else {
- responseBody.append(",\"via\":");
- ip = (const unsigned char *)sqlite3_column_blob(_sGetRoutes,1);
- switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
- case 4:
- Utils::snprintf(tmp,sizeof(tmp),"\"%d.%d.%d.%d\"",(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15]);
- break;
- case 6:
- Utils::snprintf(tmp,sizeof(tmp),"\"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x\"",(int)ip[0],(int)ip[1],(int)ip[2],(int)ip[3],(int)ip[4],(int)ip[5],(int)ip[6],(int)ip[7],(int)ip[8],(int)ip[9],(int)ip[10],(int)ip[11],(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15]);
- break;
- }
- responseBody.append(tmp);
- }
- responseBody.append(",\"flags\":");
- responseBody.append((const char *)sqlite3_column_text(_sGetRoutes,4));
- responseBody.append(",\"metric\":");
- responseBody.append((const char *)sqlite3_column_text(_sGetRoutes,5));
- responseBody.push_back('}');
- }
-
- responseBody.append("],\n\t\"ipAssignmentPools\": [");
-
- sqlite3_reset(_sGetIpAssignmentPools2);
- sqlite3_bind_text(_sGetIpAssignmentPools2,1,nwids,16,SQLITE_STATIC);
- bool firstIpAssignmentPool = true;
- while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
- const char *ipRangeStartB = reinterpret_cast<const char *>(sqlite3_column_blob(_sGetIpAssignmentPools2,0));
- const char *ipRangeEndB = reinterpret_cast<const char *>(sqlite3_column_blob(_sGetIpAssignmentPools2,1));
- if ((ipRangeStartB)&&(ipRangeEndB)) {
- InetAddress ipps,ippe;
- int ipVersion = sqlite3_column_int(_sGetIpAssignmentPools2,2);
- if (ipVersion == 4) {
- ipps.set((const void *)(ipRangeStartB + 12),4,0);
- ippe.set((const void *)(ipRangeEndB + 12),4,0);
- } else if (ipVersion == 6) {
- ipps.set((const void *)ipRangeStartB,16,0);
- ippe.set((const void *)ipRangeEndB,16,0);
- }
- if (ipps) {
- responseBody.append(firstIpAssignmentPool ? "\n\t\t" : ",\n\t\t");
- firstIpAssignmentPool = false;
- Utils::snprintf(json,sizeof(json),"{\"ipRangeStart\":\"%s\",\"ipRangeEnd\":\"%s\"}",
- _jsonEscape(ipps.toIpString()).c_str(),
- _jsonEscape(ippe.toIpString()).c_str());
- responseBody.append(json);
- }
- }
- }
-
- responseBody.append("],\n\t\"rules\": [");
-
- sqlite3_reset(_sListRules);
- sqlite3_bind_text(_sListRules,1,nwids,16,SQLITE_STATIC);
- bool firstRule = true;
- while (sqlite3_step(_sListRules) == SQLITE_ROW) {
- responseBody.append(firstRule ? "\n\t{\n" : ",{\n");
- firstRule = false;
- Utils::snprintf(json,sizeof(json),"\t\t\"ruleNo\": %lld,\n",sqlite3_column_int64(_sListRules,0));
- responseBody.append(json);
- if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"nodeId\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,2) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"sourcePort\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,2));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,3) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"destPort\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,3));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,4) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"vlanId\": %d,\n",sqlite3_column_int(_sListRules,4));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,5) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"vlanPcp\": %d,\n",sqlite3_column_int(_sListRules,5));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,6) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"etherType\": %d,\n",sqlite3_column_int(_sListRules,6));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,7) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"macSource\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,7)).toString().c_str());
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,8) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"macDest\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,8)).toString().c_str());
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,9) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"ipSource\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,9)).c_str());
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,10) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"ipDest\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,10)).c_str());
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,11) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"ipTos\": %d,\n",sqlite3_column_int(_sListRules,11));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,12) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"ipProtocol\": %d,\n",sqlite3_column_int(_sListRules,12));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,13) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"ipSourcePort\": %d,\n",sqlite3_column_int(_sListRules,13));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,14) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"ipDestPort\": %d,\n",sqlite3_column_int(_sListRules,14));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,15) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"flags\": %lu,\n",(unsigned long)sqlite3_column_int64(_sListRules,15));
- responseBody.append(json);
- }
- if (sqlite3_column_type(_sListRules,16) != SQLITE_NULL) {
- Utils::snprintf(json,sizeof(json),"\t\t\"invFlags\": %lu,\n",(unsigned long)sqlite3_column_int64(_sListRules,16));
- responseBody.append(json);
- }
- responseBody.append("\t\t\"action\": \"");
- responseBody.append(_jsonEscape( (sqlite3_column_type(_sListRules,17) == SQLITE_NULL) ? "drop" : (const char *)sqlite3_column_text(_sListRules,17) ));
- responseBody.append("\"\n\t}");
- }
-
- responseBody.append("]\n}\n");
- responseContentType = "application/json";
- return 200;
- } // else 404
- }
- } else if (path.size() == 1) {
- // list networks
- sqlite3_reset(_sListNetworks);
- responseContentType = "application/json";
- responseBody = "[";
- bool first = true;
- while (sqlite3_step(_sListNetworks) == SQLITE_ROW) {
- if (first) {
- first = false;
- responseBody.push_back('"');
- } else responseBody.append(",\"");
- responseBody.append((const char *)sqlite3_column_text(_sListNetworks,0));
- responseBody.push_back('"');
- }
- responseBody.push_back(']');
- return 200;
- } // else 404
-
- } else {
- // GET /controller returns status and API version if controller is supported
- Utils::snprintf(json,sizeof(json),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"instanceId\": \"%s\"\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now(),_instanceId.c_str());
- responseBody = json;
- responseContentType = "application/json";
- return 200;
- }
-
- return 404;
-}
-
-void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report)
-{
- char tmp[65535];
- SqliteNetworkController *const self = reinterpret_cast<SqliteNetworkController *>(test->ptr);
-
- if (!test)
- return;
- if (!report)
- return;
-
- Mutex::Lock _l(self->_lock);
- std::map< uint64_t,_CircuitTestEntry >::iterator cte(self->_circuitTests.find(test->testId));
-
- if (cte == self->_circuitTests.end()) { // sanity check: a circuit test we didn't launch?
- self->_node->circuitTestEnd(test);
- ::free((void *)test);
- return;
- }
-
- Utils::snprintf(tmp,sizeof(tmp),
- "%s{\n"
- "\t\"timestamp\": %llu,"ZT_EOL_S
- "\t\"testId\": \"%.16llx\","ZT_EOL_S
- "\t\"upstream\": \"%.10llx\","ZT_EOL_S
- "\t\"current\": \"%.10llx\","ZT_EOL_S
- "\t\"receivedTimestamp\": %llu,"ZT_EOL_S
- "\t\"remoteTimestamp\": %llu,"ZT_EOL_S
- "\t\"sourcePacketId\": \"%.16llx\","ZT_EOL_S
- "\t\"flags\": %llu,"ZT_EOL_S
- "\t\"sourcePacketHopCount\": %u,"ZT_EOL_S
- "\t\"errorCode\": %u,"ZT_EOL_S
- "\t\"vendor\": %d,"ZT_EOL_S
- "\t\"protocolVersion\": %u,"ZT_EOL_S
- "\t\"majorVersion\": %u,"ZT_EOL_S
- "\t\"minorVersion\": %u,"ZT_EOL_S
- "\t\"revision\": %u,"ZT_EOL_S
- "\t\"platform\": %d,"ZT_EOL_S
- "\t\"architecture\": %d,"ZT_EOL_S
- "\t\"receivedOnLocalAddress\": \"%s\","ZT_EOL_S
- "\t\"receivedFromRemoteAddress\": \"%s\""ZT_EOL_S
- "}",
- ((cte->second.jsonResults.length() > 0) ? ",\n" : ""),
- (unsigned long long)report->timestamp,
- (unsigned long long)test->testId,
- (unsigned long long)report->upstream,
- (unsigned long long)report->current,
- (unsigned long long)OSUtils::now(),
- (unsigned long long)report->remoteTimestamp,
- (unsigned long long)report->sourcePacketId,
- (unsigned long long)report->flags,
- report->sourcePacketHopCount,
- report->errorCode,
- (int)report->vendor,
- report->protocolVersion,
- report->majorVersion,
- report->minorVersion,
- report->revision,
- (int)report->platform,
- (int)report->architecture,
- reinterpret_cast<const InetAddress *>(&(report->receivedOnLocalAddress))->toString().c_str(),
- reinterpret_cast<const InetAddress *>(&(report->receivedFromRemoteAddress))->toString().c_str());
-
- cte->second.jsonResults.append(tmp);
-}
-
-} // namespace ZeroTier
diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp
deleted file mode 100644
index 145788c7..00000000
--- a/controller/SqliteNetworkController.hpp
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, 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 <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifndef ZT_SQLITENETWORKCONTROLLER_HPP
-#define ZT_SQLITENETWORKCONTROLLER_HPP
-
-#include <stdint.h>
-
-#include <sqlite3.h>
-
-#include <string>
-#include <map>
-#include <vector>
-
-#include "../node/Constants.hpp"
-#include "../node/NetworkController.hpp"
-#include "../node/Mutex.hpp"
-#include "../osdep/Thread.hpp"
-
-// Number of in-memory last log entries to maintain per user
-#define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32
-
-// How long do circuit tests last before they're forgotten?
-#define ZT_SQLITENETWORKCONTROLLER_CIRCUIT_TEST_TIMEOUT 60000
-
-namespace ZeroTier {
-
-class Node;
-
-class SqliteNetworkController : public NetworkController
-{
-public:
- SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath);
- virtual ~SqliteNetworkController();
-
- virtual NetworkController::ResultCode doNetworkConfigRequest(
- const InetAddress &fromAddr,
- const Identity &signingId,
- const Identity &identity,
- uint64_t nwid,
- const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,
- NetworkConfig &nc);
-
- unsigned int handleControlPlaneHttpGET(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType);
- unsigned int handleControlPlaneHttpPOST(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType);
- unsigned int handleControlPlaneHttpDELETE(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType);
-
- // threadMain() for backup thread -- do not call directly
- void threadMain()
- throw();
-
-private:
- /* deprecated
- enum IpAssignmentType {
- // IP assignment is a static IP address
- ZT_IP_ASSIGNMENT_TYPE_ADDRESS = 0,
- // IP assignment is a network -- a route via this interface, not an address
- ZT_IP_ASSIGNMENT_TYPE_NETWORK = 1
- };
- */
-
- unsigned int _doCPGet(
- const std::vector<std::string> &path,
- const std::map<std::string,std::string> &urlArgs,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType);
-
- static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report);
-
- Node *_node;
- Thread _backupThread;
- volatile bool _backupThreadRun;
- volatile bool _backupNeeded;
- std::string _dbPath;
- std::string _circuitTestPath;
- std::string _instanceId;
-
- // Circuit tests outstanding
- struct _CircuitTestEntry
- {
- ZT_CircuitTest *test;
- std::string jsonResults;
- };
- std::map< uint64_t,_CircuitTestEntry > _circuitTests;
-
- // Last request time by address, for rate limitation
- std::map< std::pair<uint64_t,uint64_t>,uint64_t > _lastRequestTime;
-
- sqlite3 *_db;
-
- sqlite3_stmt *_sGetNetworkById;
- sqlite3_stmt *_sGetMember;
- sqlite3_stmt *_sCreateMember;
- sqlite3_stmt *_sGetNodeIdentity;
- sqlite3_stmt *_sCreateOrReplaceNode;
- sqlite3_stmt *_sGetEtherTypesFromRuleTable;
- sqlite3_stmt *_sGetActiveBridges;
- sqlite3_stmt *_sGetIpAssignmentsForNode;
- sqlite3_stmt *_sGetIpAssignmentPools;
- sqlite3_stmt *_sCheckIfIpIsAllocated;
- sqlite3_stmt *_sAllocateIp;
- sqlite3_stmt *_sDeleteIpAllocations;
- sqlite3_stmt *_sGetRelays;
- sqlite3_stmt *_sListNetworks;
- sqlite3_stmt *_sListNetworkMembers;
- sqlite3_stmt *_sGetMember2;
- sqlite3_stmt *_sGetIpAssignmentPools2;
- sqlite3_stmt *_sListRules;
- sqlite3_stmt *_sCreateRule;
- sqlite3_stmt *_sCreateNetwork;
- sqlite3_stmt *_sGetNetworkRevision;
- sqlite3_stmt *_sSetNetworkRevision;
- sqlite3_stmt *_sDeleteRelaysForNetwork;
- sqlite3_stmt *_sCreateRelay;
- sqlite3_stmt *_sDeleteIpAssignmentPoolsForNetwork;
- sqlite3_stmt *_sDeleteRulesForNetwork;
- sqlite3_stmt *_sCreateIpAssignmentPool;
- sqlite3_stmt *_sUpdateMemberAuthorized;
- sqlite3_stmt *_sUpdateMemberActiveBridge;
- sqlite3_stmt *_sUpdateMemberHistory;
- sqlite3_stmt *_sDeleteMember;
- sqlite3_stmt *_sDeleteAllNetworkMembers;
- sqlite3_stmt *_sGetActiveNodesOnNetwork;
- sqlite3_stmt *_sDeleteNetwork;
- sqlite3_stmt *_sCreateRoute;
- sqlite3_stmt *_sGetRoutes;
- sqlite3_stmt *_sDeleteRoutes;
- sqlite3_stmt *_sIncrementMemberRevisionCounter;
- sqlite3_stmt *_sGetConfig;
- sqlite3_stmt *_sSetConfig;
-
- Mutex _lock;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/controller/schema.sql b/controller/schema.sql
deleted file mode 100644
index 105db924..00000000
--- a/controller/schema.sql
+++ /dev/null
@@ -1,119 +0,0 @@
-CREATE TABLE Config (
- k varchar(16) PRIMARY KEY NOT NULL,
- v varchar(1024) NOT NULL
-);
-
-CREATE TABLE Network (
- id char(16) PRIMARY KEY NOT NULL,
- name varchar(128) NOT NULL,
- private integer NOT NULL DEFAULT(1),
- enableBroadcast integer NOT NULL DEFAULT(1),
- allowPassiveBridging integer NOT NULL DEFAULT(0),
- multicastLimit integer NOT NULL DEFAULT(32),
- creationTime integer NOT NULL DEFAULT(0),
- revision integer NOT NULL DEFAULT(1),
- memberRevisionCounter integer NOT NULL DEFAULT(1),
- flags integer NOT NULL DEFAULT(0)
-);
-
-CREATE TABLE AuthToken (
- id integer PRIMARY KEY NOT NULL,
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- authMode integer NOT NULL DEFAULT(1),
- useCount integer NOT NULL DEFAULT(0),
- maxUses integer NOT NULL DEFAULT(0),
- expiresAt integer NOT NULL DEFAULT(0),
- token varchar(256) NOT NULL
-);
-
-CREATE INDEX AuthToken_networkId_token ON AuthToken(networkId,token);
-
-CREATE TABLE Node (
- id char(10) PRIMARY KEY NOT NULL,
- identity varchar(4096) NOT NULL
-);
-
-CREATE TABLE IpAssignment (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- nodeId char(10) REFERENCES Node(id) ON DELETE CASCADE,
- type integer NOT NULL DEFAULT(0),
- ip blob(16) NOT NULL,
- ipNetmaskBits integer NOT NULL DEFAULT(0),
- ipVersion integer NOT NULL DEFAULT(4)
-);
-
-CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);
-
-CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);
-
-CREATE TABLE IpAssignmentPool (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- ipRangeStart blob(16) NOT NULL,
- ipRangeEnd blob(16) NOT NULL,
- ipVersion integer NOT NULL DEFAULT(4)
-);
-
-CREATE UNIQUE INDEX IpAssignmentPool_networkId_ipRangeStart ON IpAssignmentPool (networkId,ipRangeStart);
-
-CREATE TABLE Member (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,
- authorized integer NOT NULL DEFAULT(0),
- activeBridge integer NOT NULL DEFAULT(0),
- memberRevision integer NOT NULL DEFAULT(0),
- flags integer NOT NULL DEFAULT(0),
- lastRequestTime integer NOT NULL DEFAULT(0),
- lastPowDifficulty integer NOT NULL DEFAULT(0),
- lastPowTime integer NOT NULL DEFAULT(0),
- recentHistory blob,
- PRIMARY KEY (networkId, nodeId)
-);
-
-CREATE INDEX Member_networkId_nodeId ON Member(networkId,nodeId);
-CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);
-CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);
-CREATE INDEX Member_networkId_lastRequestTime ON Member(networkId, lastRequestTime);
-
-CREATE TABLE Route (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- target blob(16) NOT NULL,
- via blob(16),
- targetNetmaskBits integer NOT NULL,
- ipVersion integer NOT NULL,
- flags integer NOT NULL,
- metric integer NOT NULL
-);
-
-CREATE INDEX Route_networkId ON Route (networkId);
-
-CREATE TABLE Relay (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- address char(10) NOT NULL,
- phyAddress varchar(64) NOT NULL
-);
-
-CREATE UNIQUE INDEX Relay_networkId_address ON Relay (networkId,address);
-
-CREATE TABLE Rule (
- networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
- ruleNo integer NOT NULL,
- nodeId char(10) REFERENCES Node(id),
- sourcePort char(10),
- destPort char(10),
- vlanId integer,
- vlanPcp integer,
- etherType integer,
- macSource char(12),
- macDest char(12),
- ipSource varchar(64),
- ipDest varchar(64),
- ipTos integer,
- ipProtocol integer,
- ipSourcePort integer,
- ipDestPort integer,
- flags integer,
- invFlags integer,
- "action" varchar(4096) NOT NULL DEFAULT('accept')
-);
-
-CREATE UNIQUE INDEX Rule_networkId_ruleNo ON Rule (networkId, ruleNo);
diff --git a/controller/schema.sql.c b/controller/schema.sql.c
deleted file mode 100644
index dab34138..00000000
--- a/controller/schema.sql.c
+++ /dev/null
@@ -1,121 +0,0 @@
-#define ZT_NETCONF_SCHEMA_SQL \
-"CREATE TABLE Config (\n"\
-" k varchar(16) PRIMARY KEY NOT NULL,\n"\
-" v varchar(1024) NOT NULL\n"\
-");\n"\
-"\n"\
-"CREATE TABLE Network (\n"\
-" id char(16) PRIMARY KEY NOT NULL,\n"\
-" name varchar(128) NOT NULL,\n"\
-" private integer NOT NULL DEFAULT(1),\n"\
-" enableBroadcast integer NOT NULL DEFAULT(1),\n"\
-" allowPassiveBridging integer NOT NULL DEFAULT(0),\n"\
-" multicastLimit integer NOT NULL DEFAULT(32),\n"\
-" creationTime integer NOT NULL DEFAULT(0),\n"\
-" revision integer NOT NULL DEFAULT(1),\n"\
-" memberRevisionCounter integer NOT NULL DEFAULT(1),\n"\
-" flags integer NOT NULL DEFAULT(0)\n"\
-");\n"\
-"\n"\
-"CREATE TABLE AuthToken (\n"\
-" id integer PRIMARY KEY NOT NULL,\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" authMode integer NOT NULL DEFAULT(1),\n"\
-" useCount integer NOT NULL DEFAULT(0),\n"\
-" maxUses integer NOT NULL DEFAULT(0),\n"\
-" expiresAt integer NOT NULL DEFAULT(0),\n"\
-" token varchar(256) NOT NULL\n"\
-");\n"\
-"\n"\
-"CREATE INDEX AuthToken_networkId_token ON AuthToken(networkId,token);\n"\
-"\n"\
-"CREATE TABLE Node (\n"\
-" id char(10) PRIMARY KEY NOT NULL,\n"\
-" identity varchar(4096) NOT NULL\n"\
-");\n"\
-"\n"\
-"CREATE TABLE IpAssignment (\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" nodeId char(10) REFERENCES Node(id) ON DELETE CASCADE,\n"\
-" type integer NOT NULL DEFAULT(0),\n"\
-" ip blob(16) NOT NULL,\n"\
-" ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\
-" ipVersion integer NOT NULL DEFAULT(4)\n"\
-");\n"\
-"\n"\
-"CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\
-"\n"\
-"CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\
-"\n"\
-"CREATE TABLE IpAssignmentPool (\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" ipRangeStart blob(16) NOT NULL,\n"\
-" ipRangeEnd blob(16) NOT NULL,\n"\
-" ipVersion integer NOT NULL DEFAULT(4)\n"\
-");\n"\
-"\n"\
-"CREATE UNIQUE INDEX IpAssignmentPool_networkId_ipRangeStart ON IpAssignmentPool (networkId,ipRangeStart);\n"\
-"\n"\
-"CREATE TABLE Member (\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" nodeId char(10) NOT NULL REFERENCES Node(id) ON DELETE CASCADE,\n"\
-" authorized integer NOT NULL DEFAULT(0),\n"\
-" activeBridge integer NOT NULL DEFAULT(0),\n"\
-" memberRevision integer NOT NULL DEFAULT(0),\n"\
-" flags integer NOT NULL DEFAULT(0),\n"\
-" lastRequestTime integer NOT NULL DEFAULT(0),\n"\
-" lastPowDifficulty integer NOT NULL DEFAULT(0),\n"\
-" lastPowTime integer NOT NULL DEFAULT(0),\n"\
-" recentHistory blob,\n"\
-" PRIMARY KEY (networkId, nodeId)\n"\
-");\n"\
-"\n"\
-"CREATE INDEX Member_networkId_nodeId ON Member(networkId,nodeId);\n"\
-"CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\
-"CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);\n"\
-"CREATE INDEX Member_networkId_lastRequestTime ON Member(networkId, lastRequestTime);\n"\
-"\n"\
-"CREATE TABLE Route (\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" target blob(16) NOT NULL,\n"\
-" via blob(16),\n"\
-" targetNetmaskBits integer NOT NULL,\n"\
-" ipVersion integer NOT NULL,\n"\
-" flags integer NOT NULL,\n"\
-" metric integer NOT NULL\n"\
-");\n"\
-"\n"\
-"CREATE INDEX Route_networkId ON Route (networkId);\n"\
-"\n"\
-"CREATE TABLE Relay (\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" address char(10) NOT NULL,\n"\
-" phyAddress varchar(64) NOT NULL\n"\
-");\n"\
-"\n"\
-"CREATE UNIQUE INDEX Relay_networkId_address ON Relay (networkId,address);\n"\
-"\n"\
-"CREATE TABLE Rule (\n"\
-" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
-" ruleNo integer NOT NULL,\n"\
-" nodeId char(10) REFERENCES Node(id),\n"\
-" sourcePort char(10),\n"\
-" destPort char(10),\n"\
-" vlanId integer,\n"\
-" vlanPcp integer,\n"\
-" etherType integer,\n"\
-" macSource char(12),\n"\
-" macDest char(12),\n"\
-" ipSource varchar(64),\n"\
-" ipDest varchar(64),\n"\
-" ipTos integer,\n"\
-" ipProtocol integer,\n"\
-" ipSourcePort integer,\n"\
-" ipDestPort integer,\n"\
-" flags integer,\n"\
-" invFlags integer,\n"\
-" \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\
-");\n"\
-"\n"\
-"CREATE UNIQUE INDEX Rule_networkId_ruleNo ON Rule (networkId, ruleNo);\n"\
-""
diff --git a/controller/schema2c.sh b/controller/schema2c.sh
deleted file mode 100755
index 4f4f1647..00000000
--- a/controller/schema2c.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-# Run this file to package the .sql file into a .c file whenever the SQL changes.
-
-rm -f schema.sql.c
-echo '#define ZT_NETCONF_SCHEMA_SQL \' >schema.sql.c
-cat schema.sql | sed 's/"/\\"/g' | sed 's/^/"/' | sed 's/$/\\n"\\/' >>schema.sql.c
-echo '""' >>schema.sql.c
diff --git a/debian/changelog b/debian/changelog
index aa2fb534..b13e179a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,27 @@
+zerotier-one (1.2.6) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 17 Apr 2018 01:00:00 -0700
+
+zerotier-one (1.2.4) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Mon, 24 Mar 2017 01:00:00 -0700
+
+zerotier-one (1.2.2) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Fri, 17 Mar 2017 01:00:00 -0700
+
+zerotier-one (1.2.0) unstable; urgency=medium
+
+ * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com> Tue, 14 Mar 2017 09:08:00 -0700
+
zerotier-one (1.1.14) unstable; urgency=medium
* See https://github.com/zerotier/ZeroTierOne for release notes.
diff --git a/debian/control b/debian/control
index 46b8307f..a9554f19 100644
--- a/debian/control
+++ b/debian/control
@@ -3,14 +3,14 @@ Maintainer: Adam Ierymenko <adam.ierymenko@zerotier.com>
Section: net
Priority: optional
Standards-Version: 3.9.6
-Build-Depends: debhelper (>= 9), liblz4-dev, libnatpmp-dev, dh-systemd, ruby-ronn
+Build-Depends: debhelper (>= 9), dh-systemd
Vcs-Git: git://github.com/zerotier/ZeroTierOne
Vcs-Browser: https://github.com/zerotier/ZeroTierOne
Homepage: https://www.zerotier.com/
Package: zerotier-one
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, liblz4-1, libnatpmp1, iproute2
+Depends: ${shlibs:Depends}, ${misc:Depends}, iproute2, adduser, libstdc++6
Homepage: https://www.zerotier.com/
Description: ZeroTier network virtualization service
ZeroTier One lets you join ZeroTier virtual networks and
diff --git a/debian/control.wheezy b/debian/control.wheezy
index 0cbd151b..f14c8760 100644
--- a/debian/control.wheezy
+++ b/debian/control.wheezy
@@ -3,14 +3,14 @@ Maintainer: Adam Ierymenko <adam.ierymenko@zerotier.com>
Section: net
Priority: optional
Standards-Version: 3.9.4
-Build-Depends: debhelper (>= 9), ruby-ronn
+Build-Depends: debhelper (>= 9)
Vcs-Git: git://github.com/zerotier/ZeroTierOne
Vcs-Browser: https://github.com/zerotier/ZeroTierOne
Homepage: https://www.zerotier.com/
Package: zerotier-one
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, iproute
+Depends: ${shlibs:Depends}, ${misc:Depends}, iproute, libstdc++6
Homepage: https://www.zerotier.com/
Description: ZeroTier network virtualization service
ZeroTier One lets you join ZeroTier virtual networks and
diff --git a/debian/postinst b/debian/postinst
new file mode 100644
index 00000000..ecd148a4
--- /dev/null
+++ b/debian/postinst
@@ -0,0 +1,9 @@
+#!/bin/sh -e
+
+case "$1" in
+ configure)
+ adduser --system --group --home /var/lib/zerotier-one --no-create-home zerotier-one
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/debian/rules b/debian/rules
index cf0b04ff..e6644d8e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -7,7 +7,7 @@ CXXFLAGS=-O3 -fstack-protector-strong
dh $@ --with systemd
override_dh_auto_build:
- make ZT_USE_MINIUPNPC=1 -j 2
+ make -j 4
override_dh_systemd_start:
dh_systemd_start --restart-after-upgrade
diff --git a/debian/rules.static b/debian/rules.static
new file mode 100644
index 00000000..72c52955
--- /dev/null
+++ b/debian/rules.static
@@ -0,0 +1,16 @@
+#!/usr/bin/make -f
+
+CFLAGS=-O3 -fstack-protector-strong
+CXXFLAGS=-O3 -fstack-protector-strong
+
+%:
+ dh $@ --with systemd
+
+override_dh_auto_build:
+# make -j 2
+
+override_dh_systemd_start:
+ dh_systemd_start --restart-after-upgrade
+
+override_dh_installinit:
+ dh_installinit --name=zerotier-one -- defaults
diff --git a/debian/rules.wheezy b/debian/rules.wheezy
index e51d794e..55e2647a 100755
--- a/debian/rules.wheezy
+++ b/debian/rules.wheezy
@@ -7,5 +7,5 @@ CXXFLAGS=-O3 -fstack-protector
dh $@
override_dh_auto_build:
- make ZT_USE_MINIUPNPC=1 -j 2
+ make -j 2
diff --git a/debian/rules.wheezy.static b/debian/rules.wheezy.static
new file mode 100644
index 00000000..0165be37
--- /dev/null
+++ b/debian/rules.wheezy.static
@@ -0,0 +1,11 @@
+#!/usr/bin/make -f
+
+CFLAGS=-O3 -fstack-protector
+CXXFLAGS=-O3 -fstack-protector
+
+%:
+ dh $@
+
+override_dh_auto_build:
+# make -j 2
+
diff --git a/debian/format b/debian/source/format
index 46ebe026..46ebe026 100644
--- a/debian/format
+++ b/debian/source/format
diff --git a/doc/zerotier-cli.1 b/doc/zerotier-cli.1
new file mode 100644
index 00000000..167109ec
--- /dev/null
+++ b/doc/zerotier-cli.1
@@ -0,0 +1,83 @@
+.TH "ZEROTIER\-CLI" "1" "December 2016" "" ""
+.SH "NAME"
+\fBzerotier-cli\fR \- control local ZeroTier virtual network service
+.SH SYNOPSIS
+.P
+\fBzerotier\-cli\fP [\-switches] <command> [arguments]
+.SH DESCRIPTION
+.P
+\fBzerotier\-cli\fR provides a simple command line interface to the local JSON API of the ZeroTier virtual network endpoint service zerotier\-one(8)\.
+.P
+By default \fBzerotier\-cli\fR must be run as root or with \fBsudo\fP\|\. If you want to allow an unprivileged user to use \fBzerotier\-cli\fR to control the system ZeroTier service, you can create a local copy of the ZeroTier service authorization token in the user's home directory:
+.P
+.RS 2
+.nf
+sudo cp /var/lib/zerotier\-one/authtoken\.secret /home/user/\.zeroTierOneAuthToken
+chown user /home/user/\.zeroTierOneAuthToken
+chmod 0600 /home/user/\.zeroTierOneAuthToken
+.fi
+.RE
+.P
+(The location of ZeroTier's service home may differ by platform\. See zerotier\-one(8)\.)
+.P
+Note that this gives the user the power to connect or disconnect the system to or from any virtual network, which is a significant permission\.
+.P
+\fBzerotier\-cli\fR has several command line arguments that are visible in \fBhelp\fP output\. The two most commonly used are \fB\-j\fP for raw JSON output and \fB\-D<path>\fP to specify an alternative ZeroTier service working directory\. Raw JSON output is easier to parse in scripts and also contains verbose details not present in the tabular output\. The \fB\-D<path>\fP option specifies where the service's zerotier\-one\.port and authtoken\.secret files are located if the service is not running at the default location for your system\.
+.SH COMMANDS
+.RS 0
+.IP \(bu 2
+\fBhelp\fP:
+Displays \fBzerotier\-cli\fR help\.
+.IP \(bu 2
+\fBinfo\fP:
+Shows information about this device including its 10\-digit ZeroTier address and apparent connection status\. Use \fB\-j\fP for more verbose output\.
+.IP \(bu 2
+\fBlistpeers\fP:
+This command lists the ZeroTier VL1 (virtual layer 1, the peer to peer network) peers this service knows about and has recently (within the past 30 minutes or so) communicated with\. These are not necessarily all the devices on your virtual network(s), and may also include a few devices not on any virtual network you've joined\. These are typically either root servers or network controllers\.
+.IP \(bu 2
+\fBlistnetworks\fP:
+This lists the networks your system belongs to and some information about them, such as any ZeroTier\-managed IP addresses you have been assigned\. (IP addresses assigned manually to ZeroTier interfaces will not be listed here\. Use the standard network interface commands to see these\.)
+.IP \(bu 2
+\fBjoin\fP:
+To join a network just use \fBjoin\fP and its 16\-digit hex network ID\. That's it\. Then use \fBlistnetworks\fP to see the status\. You'll either get a reply from the network controller with a certificate and other info such as IP assignments, or you'll get "access denied\." In this case you'll need the administrator of this network to authorize your device by its 10\-digit device ID (visible with \fBinfo\fP) on the network's controller\.
+.IP \(bu 2
+\fBleave\fP:
+Leaving a network is as easy as joining it\. This disconnects from the network and deletes its interface from the system\. Note that peers on the network may hang around in \fBlistpeers\fP for up to 30 minutes until they time out due to lack of traffic\. But if they no longer share a network with you, they can't actually communicate with you in any meaningful way\.
+
+.RE
+.SH EXAMPLES
+.P
+Join "Earth," ZeroTier's big public party line network:
+.P
+.RS 2
+.nf
+$ sudo zerotier\-cli join 8056c2e21c000001
+$ sudo zerotier\-cli listnetworks
+( wait until you get an Earth IP )
+$ ping earth\.zerotier\.net
+( you should now be able to ping our Earth test IP )
+.fi
+.RE
+.P
+Leave "Earth":
+.P
+.RS 2
+.nf
+$ sudo zerotier\-cli leave 8056c2e21c000001
+.fi
+.RE
+.P
+List VL1 peers:
+.P
+.RS 2
+.nf
+$ sudo zerotier\-cli listpeers
+.fi
+.RE
+.SH COPYRIGHT
+.P
+(c)2011\-2016 ZeroTier, Inc\. \-\- https://www\.zerotier\.com/ \-\- https://github\.com/zerotier
+.SH SEE ALSO
+.P
+zerotier\-one(8), zerotier\-idtool(1)
+
diff --git a/doc/zerotier-idtool.1 b/doc/zerotier-idtool.1
new file mode 100644
index 00000000..fbc367a6
--- /dev/null
+++ b/doc/zerotier-idtool.1
@@ -0,0 +1,84 @@
+.TH "ZEROTIER\-IDTOOL" "1" "December 2016" "" ""
+.SH "NAME"
+\fBzerotier-idtool\fR \- tool for creating and manipulating ZeroTier identities
+.SH SYNOPSIS
+.P
+\fBzerotier\-idtool\fP <command> [args]
+.SH DESCRIPTION
+.P
+\fBzerotier\-idtool\fR is a command line utility for doing things with ZeroTier identities\. A ZeroTier identity consists of a public/private key pair (or just the public if it's only an identity\.public) and a 10\-digit hexadecimal ZeroTier address derived from the public key by way of a proof of work based hash function\.
+.SH COMMANDS
+.P
+When command arguments call for a public or secret (full) identity, the identity can be specified as a path to a file or directly on the command line\.
+.RS 0
+.IP \(bu 2
+\fBhelp\fP:
+Display help\. (Also running with no command does this\.)
+.IP \(bu 2
+\fBgenerate\fP [secret file] [public file] [vanity]:
+Generate a new ZeroTier identity\. If a secret file is specified, the full identity including the private key will be written to this file\. If the public file is specified, the public portion will be written there\. If no file paths are specified the full secret identity is output to STDOUT\. The vanity prefix is a series of hexadecimal digits that the generated identity's address should start with\. Typically this isn't used, and if it's specified generation can take a very long time due to the intrinsic cost of generating identities with their proof of work function\. Generating an identity with a known 16\-bit (4 digit) prefix on a 2\.8ghz Core i5 (using one core) takes an average of two hours\.
+.IP \(bu 2
+\fBvalidate\fP <identity, only public part required>:
+Locally validate an identity's key and proof of work function correspondence\.
+.IP \(bu 2
+\fBgetpublic\fP <full identity with secret>:
+Extract the public portion of an identity\.secret and print to STDOUT\.
+.IP \(bu 2
+\fBsign\fP <full identity with secret> <file to sign>:
+Sign a file's contents with SHA512+ECC\-256 (ed25519)\. The signature is output in hex to STDOUT\.
+.IP \(bu 2
+\fBverify\fP <identity, only public part required> <file to check> <signature in hex>:
+Verify a signature created with \fBsign\fP\|\.
+.IP \(bu 2
+\fBmkcom\fP <full identity with secret> [id,value,maxdelta] [\|\.\.\.]:
+Create and sign a network membership certificate\. This is not generally useful since network controllers do this automatically and is included mostly for testing purposes\.
+
+.RE
+.SH EXAMPLES
+.P
+Generate and dump a new identity:
+.P
+.RS 2
+.nf
+$ zerotier\-idtool generate
+.fi
+.RE
+.P
+Generate and write a new identity, both secret and public parts:
+.P
+.RS 2
+.nf
+$ zerotier\-idtool generate identity\.secret identity\.public
+.fi
+.RE
+.P
+Generate a vanity address that begins with the hex digits "beef" (this will take a while!):
+.P
+.RS 2
+.nf
+$ zerotier\-idtool generate beef\.secret beef\.public beef
+.fi
+.RE
+.P
+Sign a file with an identity's secret key:
+.P
+.RS 2
+.nf
+$ zerotier\-idtool sign identity\.secret last_will_and_testament\.txt
+.fi
+.RE
+.P
+Verify a file's signature with a public key:
+.P
+.RS 2
+.nf
+$ zerotier\-idtool verify identity\.public last_will_and_testament\.txt
+.fi
+.RE
+.SH COPYRIGHT
+.P
+(c)2011\-2016 ZeroTier, Inc\. \-\- https://www\.zerotier\.com/ \-\- https://github\.com/zerotier
+.SH SEE ALSO
+.P
+zerotier\-one(8), zerotier\-cli(1)
+
diff --git a/doc/zerotier-one.8 b/doc/zerotier-one.8
new file mode 100644
index 00000000..4ad7a15d
--- /dev/null
+++ b/doc/zerotier-one.8
@@ -0,0 +1,104 @@
+.TH "ZEROTIER\-ONE" "8" "December 2016" "" ""
+.SH "NAME"
+\fBzerotier-one\fR \- ZeroTier virtual network endpoint service
+.SH SYNOPSIS
+.P
+\fBzerotier\-one\fP [\-switches] [working directory]
+.SH DESCRIPTION
+.P
+\fBzerotier\-one\fR is the service/daemon responsible for connecting a Unix (Linux/BSD/OSX) system to one or more ZeroTier virtual networks and presenting those networks to the system as virtual network ports\. You can think of it as a peer to peer VPN client\.
+.P
+It's typically run by init systems like systemd (Linux) or launchd (Mac) rather than directly by the user, and it must be run as root unless you give it the \fB\-U\fP switch and don't plan on actually joining networks (e\.g\. to run a network controller microservice only)\.
+.P
+The \fBzerotier\-one\fR service keeps its state and other files in a working directory\. If this directory is not specified at launch it defaults to "/var/lib/zerotier\-one" on Linux, "/Library/Application Support/ZeroTier/One" on Mac, and "/var/db/zerotier\-one" on FreeBSD and other similar BSDs\. The working directory should persist\. It shouldn't be automatically cleaned by system cleanup daemons or stored in a volatile location\. Loss of its identity\.secret file results in loss of this system's unique 10\-digit ZeroTier address and key\.
+.P
+Multiple instances of \fBzerotier\-one\fR can be run on the same system as long as they are run with different primary ports (see switches) and a different working directory\. But since a single service can join any number of networks, typically there's no point in doing this\.
+.P
+The \fBzerotier\-one\fR service is controlled via a JSON API available at 127\.0\.0\.1:<primary port> with the default primary port being 9993\. Access to this API requires an authorization token normally found in the authtoken\.secret file in the service's working directory\. On some platforms access may be guarded by other measures such as socket peer UID/GID lookup if additional security options are enabled (this is not the default)\.
+.P
+The first time the service is started in a fresh working directory, it generates a ZeroTier identity\. On slow systems this process can take ten seconds or more due to an anti\-DDOS/anti\-counterfeit proof of work function used by ZeroTier in address generation\. This only happens once, and once generated the result is saved in identity\.secret in the working directory\. This file represents and defines/claims your ZeroTier address and associated ECC\-256 key pair\.
+.SH SWITCHES
+.RS 0
+.IP \(bu 2
+\fB\-h\fP:
+Display help\.
+.IP \(bu 2
+\fB\-v\fP:
+Display ZeroTier One version\.
+.IP \(bu 2
+\fB\-U\fP:
+Skip privilege check and allow to be run by non\-privileged user\. This is typically used when \fBzerotier\-one\fR is built with the network controller option included\. In this case the ZeroTier service might only be acting as a network controller and might never actually join networks, in which case it does not require elevated system permissions\.
+.IP \(bu 2
+\fB\-p<port>\fP:
+Specify a different primary port\. If this is not given the default is 9993\. If zero is given a random port is chosen each time\.
+.IP \(bu 2
+\fB\-d\fP:
+Fork and run as a daemon\.
+.IP \(bu 2
+\fB\-i\fP:
+Invoke the \fBzerotier\-idtool\fR personality, in which case the binary behaves like zerotier\-idtool(1)\. This happens automatically if the name of the binary (or a symlink to it) is zerotier\-idtool\.
+.IP \(bu 2
+\fB\-q\fP:
+Invoke the \fBzerotier\-cli\fR personality, in which case the binary behaves like zerotier\-cli(1)\. This happens automatically if the name of the binary (or a symlink to it) is zerotier\-cli\.
+
+.RE
+.SH EXAMPLES
+.P
+Run as daemon with OS default working directory and default port:
+.P
+.RS 2
+.nf
+$ sudo zerotier\-one \-d
+.fi
+.RE
+.P
+Run as daemon with a different working directory and port:
+.P
+.RS 2
+.nf
+$ sudo zerotier\-one \-d \-p12345 /tmp/zerotier\-working\-directory\-test
+.fi
+.RE
+.SH FILES
+.P
+These are found in the service's working directory\.
+.RS 0
+.IP \(bu 2
+\fBidentity\.public\fP:
+The public portion of your ZeroTier identity, which is your 10\-digit hex address and the associated public key\.
+.IP \(bu 2
+\fBidentity\.secret\fP:
+Your full ZeroTier identity including its private key\. This file identifies the system on the network, which means you can move a ZeroTier address around by copying this file and you should back up this file if you want to save your system's static ZeroTier address\. This file must be protected, since theft of its secret key will allow anyone to impersonate your device on any network and decrypt traffic\. For network controllers this file is particularly sensitive since it constitutes the private key for a certificate authority for the controller's networks\.
+.IP \(bu 2
+\fBauthtoken\.secret\fP:
+The secret token used to authenticate requests to the service's local JSON API\. If it does not exist it is generated from a secure random source on service start\. To use, send it in the "X\-ZT1\-Auth" header with HTTP requests to 127\.0\.0\.1:<primary port>\|\.
+.IP \(bu 2
+\fBdevicemap\fP:
+Remembers mappings of zt# interface numbers to ZeroTier networks so they'll persist across restarts\. On some systems that support longer interface names that can encode the network ID (such as FreeBSD) this file may not be present\.
+.IP \(bu 2
+\fBzerotier\-one\.pid\fP:
+ZeroTier's PID\. This file is deleted on normal shutdown\.
+.IP \(bu 2
+\fBzerotier\-one\.port\fP:
+ZeroTier's primary port, which is also where its JSON API is found at 127\.0\.0\.1:<this port>\|\. This file is created on startup and is read by zerotier\-cli(1) to determine where it should find the control API\.
+.IP \(bu 2
+\fBcontroller\.db\fP:
+If the ZeroTier One service is built with the network controller enabled, this file contains the controller's SQLite3 database\.
+.IP \(bu 2
+\fBcontroller\.db\.backup\fP:
+If the ZeroTier One service is built with the network controller enabled, it periodically backs up its controller\.db database in this file (currently every 5 minutes if there have been changes)\. Since this file is not a currently in use SQLite3 database it's safer to back up without corruption\. On new backups the file is rotated out rather than being rewritten in place\.
+.IP \(bu 2
+\fBiddb\.d/\fP (directory):
+Caches the public identity of every peer ZeroTier has spoken with in the last 60 days\. This directory and its contents can be deleted, but this may result in slower connection initations since it will require that we go out and re\-fetch full identities for peers we're speaking to\.
+.IP \(bu 2
+\fBnetworks\.d\fP (directory):
+This caches network configurations and certificate information for networks you belong to\. ZeroTier scans this directory for <network ID>\|\.conf files on startup to recall its networks, so "touch"ing an empty <network ID>\|\.conf file in this directory is a way of pre\-configuring ZeroTier to join a specific network on startup without using the API\. If the config file is empty ZeroTIer will just fetch it from the network's controller\.
+
+.RE
+.SH COPYRIGHT
+.P
+(c)2011\-2016 ZeroTier, Inc\. \-\- https://www\.zerotier\.com/ \-\- https://github\.com/zerotier
+.SH SEE ALSO
+.P
+zerotier\-cli(1), zerotier\-idtool(1)
+
diff --git a/ext/arm32-neon-salsa2012-asm/README.md b/ext/arm32-neon-salsa2012-asm/README.md
new file mode 100644
index 00000000..54fc6f5f
--- /dev/null
+++ b/ext/arm32-neon-salsa2012-asm/README.md
@@ -0,0 +1,6 @@
+ARM NEON (32-bit) ASM implementation of Salsa20/12
+======
+
+This is from [supercop](http://bench.cr.yp.to/supercop.html) and was originally written by Daniel J. Bernstein. Code is in the public domain like the rest of Salsa20. It's much faster than the naive implementation.
+
+It's included automatically in 32-bit Linux ARM builds. It likely will not work on 64-bit ARM, so it'll need to be ported at least. That will unfortunately keep it out of mobile versions for now since those are all going 64-bit.
diff --git a/ext/arm32-neon-salsa2012-asm/salsa2012.h b/ext/arm32-neon-salsa2012-asm/salsa2012.h
new file mode 100644
index 00000000..95b247f2
--- /dev/null
+++ b/ext/arm32-neon-salsa2012-asm/salsa2012.h
@@ -0,0 +1,23 @@
+#ifndef ZT_SALSA2012_ARM32NEON_ASM
+#define ZT_SALSA2012_ARM32NEON_ASM
+
+#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+#include <sys/auxv.h>
+#include <asm/hwcap.h>
+#define zt_arm_has_neon() ((getauxval(AT_HWCAP) & HWCAP_NEON) != 0)
+#else
+#define zt_arm_has_neon() (true)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// ciphertext buffer, message/NULL, length, nonce (8 bytes), key (32 bytes)
+extern int zt_salsa2012_armneon3_xor(unsigned char *c,const unsigned char *m,unsigned long long len,const unsigned char *n,const unsigned char *k);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ext/arm32-neon-salsa2012-asm/salsa2012.s b/ext/arm32-neon-salsa2012-asm/salsa2012.s
new file mode 100644
index 00000000..9e5989cd
--- /dev/null
+++ b/ext/arm32-neon-salsa2012-asm/salsa2012.s
@@ -0,0 +1,2231 @@
+
+# qhasm: int32 input_0
+
+# qhasm: int32 input_1
+
+# qhasm: int32 input_2
+
+# qhasm: int32 input_3
+
+# qhasm: stack32 input_4
+
+# qhasm: stack32 input_5
+
+# qhasm: stack32 input_6
+
+# qhasm: stack32 input_7
+
+# qhasm: int32 caller_r4
+
+# qhasm: int32 caller_r5
+
+# qhasm: int32 caller_r6
+
+# qhasm: int32 caller_r7
+
+# qhasm: int32 caller_r8
+
+# qhasm: int32 caller_r9
+
+# qhasm: int32 caller_r10
+
+# qhasm: int32 caller_r11
+
+# qhasm: int32 caller_r14
+
+# qhasm: reg128 caller_q4
+
+# qhasm: reg128 caller_q5
+
+# qhasm: reg128 caller_q6
+
+# qhasm: reg128 caller_q7
+
+# qhasm: startcode
+.fpu neon
+.text
+
+# qhasm: constant sigma:
+.align 2
+sigma:
+
+# qhasm: const32 1634760805
+.word 1634760805
+
+# qhasm: const32 857760878
+.word 857760878
+
+# qhasm: const32 2036477234
+.word 2036477234
+
+# qhasm: const32 1797285236
+.word 1797285236
+
+# qhasm: int128 abab
+
+# qhasm: int128 diag0
+
+# qhasm: int128 diag1
+
+# qhasm: int128 diag2
+
+# qhasm: int128 diag3
+
+# qhasm: int128 a0
+
+# qhasm: int128 a1
+
+# qhasm: int128 a2
+
+# qhasm: int128 a3
+
+# qhasm: int128 b0
+
+# qhasm: int128 b1
+
+# qhasm: int128 b2
+
+# qhasm: int128 b3
+
+# qhasm: int128 next_diag0
+
+# qhasm: int128 next_diag1
+
+# qhasm: int128 next_diag2
+
+# qhasm: int128 next_diag3
+
+# qhasm: int128 next_a0
+
+# qhasm: int128 next_a1
+
+# qhasm: int128 next_a2
+
+# qhasm: int128 next_a3
+
+# qhasm: int128 next_b0
+
+# qhasm: int128 next_b1
+
+# qhasm: int128 next_b2
+
+# qhasm: int128 next_b3
+
+# qhasm: int128 x0x5x10x15
+
+# qhasm: int128 x12x1x6x11
+
+# qhasm: int128 x8x13x2x7
+
+# qhasm: int128 x4x9x14x3
+
+# qhasm: int128 x0x1x10x11
+
+# qhasm: int128 x12x13x6x7
+
+# qhasm: int128 x8x9x2x3
+
+# qhasm: int128 x4x5x14x15
+
+# qhasm: int128 x0x1x2x3
+
+# qhasm: int128 x4x5x6x7
+
+# qhasm: int128 x8x9x10x11
+
+# qhasm: int128 x12x13x14x15
+
+# qhasm: int128 m0m1m2m3
+
+# qhasm: int128 m4m5m6m7
+
+# qhasm: int128 m8m9m10m11
+
+# qhasm: int128 m12m13m14m15
+
+# qhasm: int128 start0
+
+# qhasm: int128 start1
+
+# qhasm: int128 start2
+
+# qhasm: int128 start3
+
+# qhasm: stack128 stack_start3
+
+# qhasm: stack128 next_start2
+
+# qhasm: stack128 next_start3
+
+# qhasm: int128 k0k1k2k3
+
+# qhasm: int128 k4k5k6k7
+
+# qhasm: int128 k1n1k7k2
+
+# qhasm: int128 n2n3n3n2
+
+# qhasm: int128 k2k3k6k7
+
+# qhasm: int128 nextblock
+
+# qhasm: stack128 stack_q4
+
+# qhasm: stack128 stack_q5
+
+# qhasm: stack128 stack_q6
+
+# qhasm: stack128 stack_q7
+
+# qhasm: stack32 stack_r4
+
+# qhasm: stack128 k2k3k6k7_stack
+
+# qhasm: stack128 k1n1k7k2_stack
+
+# qhasm: stack512 tmp
+
+# qhasm: stack32 savec
+
+# qhasm: int32 i
+
+# qhasm: int32 ci
+
+# qhasm: int32 mi
+
+# qhasm: enter zt_salsa2012_armneon3_xor
+.align 2
+.global _zt_salsa2012_armneon3_xor
+.global zt_salsa2012_armneon3_xor
+.type _zt_salsa2012_armneon3_xor STT_FUNC
+.type zt_salsa2012_armneon3_xor STT_FUNC
+_zt_salsa2012_armneon3_xor:
+zt_salsa2012_armneon3_xor:
+sub sp,sp,#256
+
+# qhasm: new stack_q4
+
+# qhasm: new stack_q5
+
+# qhasm: new stack_q6
+
+# qhasm: new stack_q7
+
+# qhasm: stack_q4 bot = caller_q4 bot
+# asm 1: vstr <caller_q4=reg128#5%bot,<stack_q4=stack128#1
+# asm 2: vstr <caller_q4=d8,<stack_q4=[sp,#96]
+vstr d8,[sp,#96]
+
+# qhasm: stack_q4 top = caller_q4 top
+# asm 1: vstr <caller_q4=reg128#5%top,<stack_q4=stack128#1
+# asm 2: vstr <caller_q4=d9,<stack_q4=[sp,#104]
+vstr d9,[sp,#104]
+
+# qhasm: stack_q5 bot = caller_q5 bot
+# asm 1: vstr <caller_q5=reg128#6%bot,<stack_q5=stack128#2
+# asm 2: vstr <caller_q5=d10,<stack_q5=[sp,#112]
+vstr d10,[sp,#112]
+
+# qhasm: stack_q5 top = caller_q5 top
+# asm 1: vstr <caller_q5=reg128#6%top,<stack_q5=stack128#2
+# asm 2: vstr <caller_q5=d11,<stack_q5=[sp,#120]
+vstr d11,[sp,#120]
+
+# qhasm: stack_q6 bot = caller_q6 bot
+# asm 1: vstr <caller_q6=reg128#7%bot,<stack_q6=stack128#3
+# asm 2: vstr <caller_q6=d12,<stack_q6=[sp,#128]
+vstr d12,[sp,#128]
+
+# qhasm: stack_q6 top = caller_q6 top
+# asm 1: vstr <caller_q6=reg128#7%top,<stack_q6=stack128#3
+# asm 2: vstr <caller_q6=d13,<stack_q6=[sp,#136]
+vstr d13,[sp,#136]
+
+# qhasm: stack_q7 bot = caller_q7 bot
+# asm 1: vstr <caller_q7=reg128#8%bot,<stack_q7=stack128#4
+# asm 2: vstr <caller_q7=d14,<stack_q7=[sp,#144]
+vstr d14,[sp,#144]
+
+# qhasm: stack_q7 top = caller_q7 top
+# asm 1: vstr <caller_q7=reg128#8%top,<stack_q7=stack128#4
+# asm 2: vstr <caller_q7=d15,<stack_q7=[sp,#152]
+vstr d15,[sp,#152]
+
+# qhasm: new stack_r4
+
+# qhasm: stack_r4 = caller_r4
+# asm 1: str <caller_r4=int32#5,>stack_r4=stack32#2
+# asm 2: str <caller_r4=r4,>stack_r4=[sp,#68]
+str r4,[sp,#68]
+
+# qhasm: int32 c
+
+# qhasm: c = input_0
+# asm 1: mov >c=int32#1,<input_0=int32#1
+# asm 2: mov >c=r0,<input_0=r0
+mov r0,r0
+
+# qhasm: int32 m
+
+# qhasm: m = input_1
+# asm 1: mov >m=int32#2,<input_1=int32#2
+# asm 2: mov >m=r1,<input_1=r1
+mov r1,r1
+
+# qhasm: int32 mlenlow
+
+# qhasm: mlenlow = input_2
+# asm 1: mov >mlenlow=int32#3,<input_2=int32#3
+# asm 2: mov >mlenlow=r2,<input_2=r2
+mov r2,r2
+
+# qhasm: int32 mlenhigh
+
+# qhasm: mlenhigh = input_3
+# asm 1: mov >mlenhigh=int32#4,<input_3=int32#4
+# asm 2: mov >mlenhigh=r3,<input_3=r3
+mov r3,r3
+
+# qhasm: int32 n
+
+# qhasm: n = input_4
+# asm 1: ldr >n=int32#5,<input_4=stack32#arg1
+# asm 2: ldr >n=r4,<input_4=[sp,#256]
+ldr r4,[sp,#256]
+
+# qhasm: int32 k
+
+# qhasm: k = input_5
+# asm 1: ldr >k=int32#13,<input_5=stack32#arg2
+# asm 2: ldr >k=r12,<input_5=[sp,#260]
+ldr r12,[sp,#260]
+
+# qhasm: k0k1k2k3 = mem128[k]
+# asm 1: vld1.8 {>k0k1k2k3=reg128#1%bot->k0k1k2k3=reg128#1%top},[<k=int32#13]
+# asm 2: vld1.8 {>k0k1k2k3=d0->k0k1k2k3=d1},[<k=r12]
+vld1.8 {d0-d1},[r12]
+
+# qhasm: k += 16
+# asm 1: add <k=int32#13,<k=int32#13,#16
+# asm 2: add <k=r12,<k=r12,#16
+add r12,r12,#16
+
+# qhasm: k4k5k6k7 = mem128[k]
+# asm 1: vld1.8 {>k4k5k6k7=reg128#2%bot->k4k5k6k7=reg128#2%top},[<k=int32#13]
+# asm 2: vld1.8 {>k4k5k6k7=d2->k4k5k6k7=d3},[<k=r12]
+vld1.8 {d2-d3},[r12]
+
+# qhasm: i = sigma
+# asm 1: ldr >i=int32#13,=sigma
+# asm 2: ldr >i=r12,=sigma
+ldr r12,=sigma
+
+# qhasm: start0 = mem128[i]
+# asm 1: vld1.8 {>start0=reg128#3%bot->start0=reg128#3%top},[<i=int32#13]
+# asm 2: vld1.8 {>start0=d4->start0=d5},[<i=r12]
+vld1.8 {d4-d5},[r12]
+
+# qhasm: 2x start1 = 0
+# asm 1: vmov.i64 >start1=reg128#4,#0
+# asm 2: vmov.i64 >start1=q3,#0
+vmov.i64 q3,#0
+
+# qhasm: start1 bot = mem64[n]
+# asm 1: vld1.8 {<start1=reg128#4%bot},[<n=int32#5]
+# asm 2: vld1.8 {<start1=d6},[<n=r4]
+vld1.8 {d6},[r4]
+
+# qhasm: start1 = start1[1] start1[0] start1[2,3]
+# asm 1: vext.32 <start1=reg128#4%bot,<start1=reg128#4%bot,<start1=reg128#4%bot,#1
+# asm 2: vext.32 <start1=d6,<start1=d6,<start1=d6,#1
+vext.32 d6,d6,d6,#1
+
+# qhasm: start1 = start1[0,1] start1[1] k4k5k6k7[0]
+# asm 1: vext.32 <start1=reg128#4%top,<start1=reg128#4%bot,<k4k5k6k7=reg128#2%bot,#1
+# asm 2: vext.32 <start1=d7,<start1=d6,<k4k5k6k7=d2,#1
+vext.32 d7,d6,d2,#1
+
+# qhasm: new k1n1k7k2
+
+# qhasm: k1n1k7k2 = k0k1k2k3[1] start1[0] k1n1k7k2[2,3]
+# asm 1: vext.32 <k1n1k7k2=reg128#5%bot,<k0k1k2k3=reg128#1%bot,<start1=reg128#4%bot,#1
+# asm 2: vext.32 <k1n1k7k2=d8,<k0k1k2k3=d0,<start1=d6,#1
+vext.32 d8,d0,d6,#1
+
+# qhasm: k1n1k7k2 = k1n1k7k2[0,1] k4k5k6k7[3] k0k1k2k3[2]
+# asm 1: vext.32 <k1n1k7k2=reg128#5%top,<k4k5k6k7=reg128#2%top,<k0k1k2k3=reg128#1%top,#1
+# asm 2: vext.32 <k1n1k7k2=d9,<k4k5k6k7=d3,<k0k1k2k3=d1,#1
+vext.32 d9,d3,d1,#1
+
+# qhasm: k2k3k6k7 = k4k5k6k7
+# asm 1: vmov >k2k3k6k7=reg128#6,<k4k5k6k7=reg128#2
+# asm 2: vmov >k2k3k6k7=q5,<k4k5k6k7=q1
+vmov q5,q1
+
+# qhasm: k2k3k6k7 = k0k1k2k3[2,3] k2k3k6k7[2,3]
+# asm 1: vmov <k2k3k6k7=reg128#6%bot,<k0k1k2k3=reg128#1%top
+# asm 2: vmov <k2k3k6k7=d10,<k0k1k2k3=d1
+vmov d10,d1
+
+# qhasm: start1 = k4k5k6k7[1] k0k1k2k3[0] start1[2,3]
+# asm 1: vext.32 <start1=reg128#4%bot,<k4k5k6k7=reg128#2%bot,<k0k1k2k3=reg128#1%bot,#1
+# asm 2: vext.32 <start1=d6,<k4k5k6k7=d2,<k0k1k2k3=d0,#1
+vext.32 d6,d2,d0,#1
+
+# qhasm: new k2k3k6k7_stack
+
+# qhasm: k2k3k6k7_stack bot = k2k3k6k7 bot
+# asm 1: vstr <k2k3k6k7=reg128#6%bot,<k2k3k6k7_stack=stack128#5
+# asm 2: vstr <k2k3k6k7=d10,<k2k3k6k7_stack=[sp,#160]
+vstr d10,[sp,#160]
+
+# qhasm: k2k3k6k7_stack top = k2k3k6k7 top
+# asm 1: vstr <k2k3k6k7=reg128#6%top,<k2k3k6k7_stack=stack128#5
+# asm 2: vstr <k2k3k6k7=d11,<k2k3k6k7_stack=[sp,#168]
+vstr d11,[sp,#168]
+
+# qhasm: new k1n1k7k2_stack
+
+# qhasm: k1n1k7k2_stack bot = k1n1k7k2 bot
+# asm 1: vstr <k1n1k7k2=reg128#5%bot,<k1n1k7k2_stack=stack128#6
+# asm 2: vstr <k1n1k7k2=d8,<k1n1k7k2_stack=[sp,#176]
+vstr d8,[sp,#176]
+
+# qhasm: k1n1k7k2_stack top = k1n1k7k2 top
+# asm 1: vstr <k1n1k7k2=reg128#5%top,<k1n1k7k2_stack=stack128#6
+# asm 2: vstr <k1n1k7k2=d9,<k1n1k7k2_stack=[sp,#184]
+vstr d9,[sp,#184]
+
+# qhasm: 2x n2n3n3n2 = 0
+# asm 1: vmov.i64 >n2n3n3n2=reg128#1,#0
+# asm 2: vmov.i64 >n2n3n3n2=q0,#0
+vmov.i64 q0,#0
+
+# qhasm: unsigned<? mlenlow - 128
+# asm 1: cmp <mlenlow=int32#3,#128
+# asm 2: cmp <mlenlow=r2,#128
+cmp r2,#128
+
+# qhasm: goto mlenlowbelow128 if unsigned<
+blo ._mlenlowbelow128
+
+# qhasm: mlenatleast128:
+._mlenatleast128:
+
+# qhasm: new k2k3k6k7
+
+# qhasm: k2k3k6k7 bot = k2k3k6k7_stack bot
+# asm 1: vldr <k2k3k6k7=reg128#2%bot,<k2k3k6k7_stack=stack128#5
+# asm 2: vldr <k2k3k6k7=d2,<k2k3k6k7_stack=[sp,#160]
+vldr d2,[sp,#160]
+
+# qhasm: k2k3k6k7 top = k2k3k6k7_stack top
+# asm 1: vldr <k2k3k6k7=reg128#2%top,<k2k3k6k7_stack=stack128#5
+# asm 2: vldr <k2k3k6k7=d3,<k2k3k6k7_stack=[sp,#168]
+vldr d3,[sp,#168]
+
+# qhasm: new k1n1k7k2
+
+# qhasm: k1n1k7k2 bot = k1n1k7k2_stack bot
+# asm 1: vldr <k1n1k7k2=reg128#5%bot,<k1n1k7k2_stack=stack128#6
+# asm 2: vldr <k1n1k7k2=d8,<k1n1k7k2_stack=[sp,#176]
+vldr d8,[sp,#176]
+
+# qhasm: k1n1k7k2 top = k1n1k7k2_stack top
+# asm 1: vldr <k1n1k7k2=reg128#5%top,<k1n1k7k2_stack=stack128#6
+# asm 2: vldr <k1n1k7k2=d9,<k1n1k7k2_stack=[sp,#184]
+vldr d9,[sp,#184]
+
+# qhasm: n2n3n3n2 = n2n3n3n2[0,1] n2n3n3n2[1] n2n3n3n2[0]
+# asm 1: vext.32 <n2n3n3n2=reg128#1%top,<n2n3n3n2=reg128#1%bot,<n2n3n3n2=reg128#1%bot,#1
+# asm 2: vext.32 <n2n3n3n2=d1,<n2n3n3n2=d0,<n2n3n3n2=d0,#1
+vext.32 d1,d0,d0,#1
+
+# qhasm: new diag2
+
+# qhasm: diag2 = diag2[0,1] k1n1k7k2[0,1]
+# asm 1: vmov <diag2=reg128#6%top,<k1n1k7k2=reg128#5%bot
+# asm 2: vmov <diag2=d11,<k1n1k7k2=d8
+vmov d11,d8
+
+# qhasm: diag2 = n2n3n3n2[3] k2k3k6k7[2] diag2[2,3]
+# asm 1: vext.32 <diag2=reg128#6%bot,<n2n3n3n2=reg128#1%top,<k2k3k6k7=reg128#2%top,#1
+# asm 2: vext.32 <diag2=d10,<n2n3n3n2=d1,<k2k3k6k7=d3,#1
+vext.32 d10,d1,d3,#1
+
+# qhasm: new diag3
+
+# qhasm: diag3 = diag3[0,1] k1n1k7k2[2,3]
+# asm 1: vmov <diag3=reg128#7%top,<k1n1k7k2=reg128#5%top
+# asm 2: vmov <diag3=d13,<k1n1k7k2=d9
+vmov d13,d9
+
+# qhasm: diag3 = k2k3k6k7[1] n2n3n3n2[2] diag3[2,3]
+# asm 1: vext.32 <diag3=reg128#7%bot,<k2k3k6k7=reg128#2%bot,<n2n3n3n2=reg128#1%top,#1
+# asm 2: vext.32 <diag3=d12,<k2k3k6k7=d2,<n2n3n3n2=d1,#1
+vext.32 d12,d2,d1,#1
+
+# qhasm: diag0 = start0
+# asm 1: vmov >diag0=reg128#8,<start0=reg128#3
+# asm 2: vmov >diag0=q7,<start0=q2
+vmov q7,q2
+
+# qhasm: diag1 = start1
+# asm 1: vmov >diag1=reg128#9,<start1=reg128#4
+# asm 2: vmov >diag1=q8,<start1=q3
+vmov q8,q3
+
+# qhasm: start2 = diag2
+# asm 1: vmov >start2=reg128#10,<diag2=reg128#6
+# asm 2: vmov >start2=q9,<diag2=q5
+vmov q9,q5
+
+# qhasm: new stack_start3
+
+# qhasm: stack_start3 bot = diag3 bot
+# asm 1: vstr <diag3=reg128#7%bot,<stack_start3=stack128#9
+# asm 2: vstr <diag3=d12,<stack_start3=[sp,#224]
+vstr d12,[sp,#224]
+
+# qhasm: stack_start3 top = diag3 top
+# asm 1: vstr <diag3=reg128#7%top,<stack_start3=stack128#9
+# asm 2: vstr <diag3=d13,<stack_start3=[sp,#232]
+vstr d13,[sp,#232]
+
+# qhasm: 2x nextblock = 0xff
+# asm 1: vmov.i64 >nextblock=reg128#11,#0xff
+# asm 2: vmov.i64 >nextblock=q10,#0xff
+vmov.i64 q10,#0xff
+
+# qhasm: 4x nextblock unsigned>>= 7
+# asm 1: vshr.u32 >nextblock=reg128#11,<nextblock=reg128#11,#7
+# asm 2: vshr.u32 >nextblock=q10,<nextblock=q10,#7
+vshr.u32 q10,q10,#7
+
+# qhasm: 2x n2n3n3n2 += nextblock
+# asm 1: vadd.i64 >n2n3n3n2=reg128#1,<n2n3n3n2=reg128#1,<nextblock=reg128#11
+# asm 2: vadd.i64 >n2n3n3n2=q0,<n2n3n3n2=q0,<nextblock=q10
+vadd.i64 q0,q0,q10
+
+# qhasm: n2n3n3n2 = n2n3n3n2[0,1] n2n3n3n2[1] n2n3n3n2[0]
+# asm 1: vext.32 <n2n3n3n2=reg128#1%top,<n2n3n3n2=reg128#1%bot,<n2n3n3n2=reg128#1%bot,#1
+# asm 2: vext.32 <n2n3n3n2=d1,<n2n3n3n2=d0,<n2n3n3n2=d0,#1
+vext.32 d1,d0,d0,#1
+
+# qhasm: new next_diag2
+
+# qhasm: next_diag2 = next_diag2[0,1] k1n1k7k2[0,1]
+# asm 1: vmov <next_diag2=reg128#12%top,<k1n1k7k2=reg128#5%bot
+# asm 2: vmov <next_diag2=d23,<k1n1k7k2=d8
+vmov d23,d8
+
+# qhasm: next_diag2 = n2n3n3n2[3] k2k3k6k7[2] next_diag2[2,3]
+# asm 1: vext.32 <next_diag2=reg128#12%bot,<n2n3n3n2=reg128#1%top,<k2k3k6k7=reg128#2%top,#1
+# asm 2: vext.32 <next_diag2=d22,<n2n3n3n2=d1,<k2k3k6k7=d3,#1
+vext.32 d22,d1,d3,#1
+
+# qhasm: new next_diag3
+
+# qhasm: next_diag3 = next_diag3[0,1] k1n1k7k2[2,3]
+# asm 1: vmov <next_diag3=reg128#13%top,<k1n1k7k2=reg128#5%top
+# asm 2: vmov <next_diag3=d25,<k1n1k7k2=d9
+vmov d25,d9
+
+# qhasm: next_diag3 = k2k3k6k7[1] n2n3n3n2[2] next_diag3[2,3]
+# asm 1: vext.32 <next_diag3=reg128#13%bot,<k2k3k6k7=reg128#2%bot,<n2n3n3n2=reg128#1%top,#1
+# asm 2: vext.32 <next_diag3=d24,<k2k3k6k7=d2,<n2n3n3n2=d1,#1
+vext.32 d24,d2,d1,#1
+
+# qhasm: 2x n2n3n3n2 += nextblock
+# asm 1: vadd.i64 >n2n3n3n2=reg128#1,<n2n3n3n2=reg128#1,<nextblock=reg128#11
+# asm 2: vadd.i64 >n2n3n3n2=q0,<n2n3n3n2=q0,<nextblock=q10
+vadd.i64 q0,q0,q10
+
+# qhasm: next_diag0 = diag0
+# asm 1: vmov >next_diag0=reg128#2,<diag0=reg128#8
+# asm 2: vmov >next_diag0=q1,<diag0=q7
+vmov q1,q7
+
+# qhasm: next_diag1 = diag1
+# asm 1: vmov >next_diag1=reg128#5,<diag1=reg128#9
+# asm 2: vmov >next_diag1=q4,<diag1=q8
+vmov q4,q8
+
+# qhasm: next_start2 bot = next_diag2 bot
+# asm 1: vstr <next_diag2=reg128#12%bot,<next_start2=stack128#7
+# asm 2: vstr <next_diag2=d22,<next_start2=[sp,#192]
+vstr d22,[sp,#192]
+
+# qhasm: next_start2 top = next_diag2 top
+# asm 1: vstr <next_diag2=reg128#12%top,<next_start2=stack128#7
+# asm 2: vstr <next_diag2=d23,<next_start2=[sp,#200]
+vstr d23,[sp,#200]
+
+# qhasm: next_start3 bot = next_diag3 bot
+# asm 1: vstr <next_diag3=reg128#13%bot,<next_start3=stack128#8
+# asm 2: vstr <next_diag3=d24,<next_start3=[sp,#208]
+vstr d24,[sp,#208]
+
+# qhasm: next_start3 top = next_diag3 top
+# asm 1: vstr <next_diag3=reg128#13%top,<next_start3=stack128#8
+# asm 2: vstr <next_diag3=d25,<next_start3=[sp,#216]
+vstr d25,[sp,#216]
+
+# qhasm: i = 12
+# asm 1: ldr >i=int32#5,=12
+# asm 2: ldr >i=r4,=12
+ldr r4,=12
+
+# qhasm: mainloop2:
+._mainloop2:
+
+# qhasm: 4x a0 = diag1 + diag0
+# asm 1: vadd.i32 >a0=reg128#11,<diag1=reg128#9,<diag0=reg128#8
+# asm 2: vadd.i32 >a0=q10,<diag1=q8,<diag0=q7
+vadd.i32 q10,q8,q7
+
+# qhasm: 4x next_a0 = next_diag1 + next_diag0
+# asm 1: vadd.i32 >next_a0=reg128#14,<next_diag1=reg128#5,<next_diag0=reg128#2
+# asm 2: vadd.i32 >next_a0=q13,<next_diag1=q4,<next_diag0=q1
+vadd.i32 q13,q4,q1
+
+# qhasm: 4x b0 = a0 << 7
+# asm 1: vshl.i32 >b0=reg128#15,<a0=reg128#11,#7
+# asm 2: vshl.i32 >b0=q14,<a0=q10,#7
+vshl.i32 q14,q10,#7
+
+# qhasm: 4x next_b0 = next_a0 << 7
+# asm 1: vshl.i32 >next_b0=reg128#16,<next_a0=reg128#14,#7
+# asm 2: vshl.i32 >next_b0=q15,<next_a0=q13,#7
+vshl.i32 q15,q13,#7
+
+# qhasm: 4x b0 insert= a0 >> 25
+# asm 1: vsri.i32 <b0=reg128#15,<a0=reg128#11,#25
+# asm 2: vsri.i32 <b0=q14,<a0=q10,#25
+vsri.i32 q14,q10,#25
+
+# qhasm: 4x next_b0 insert= next_a0 >> 25
+# asm 1: vsri.i32 <next_b0=reg128#16,<next_a0=reg128#14,#25
+# asm 2: vsri.i32 <next_b0=q15,<next_a0=q13,#25
+vsri.i32 q15,q13,#25
+
+# qhasm: diag3 ^= b0
+# asm 1: veor >diag3=reg128#7,<diag3=reg128#7,<b0=reg128#15
+# asm 2: veor >diag3=q6,<diag3=q6,<b0=q14
+veor q6,q6,q14
+
+# qhasm: next_diag3 ^= next_b0
+# asm 1: veor >next_diag3=reg128#11,<next_diag3=reg128#13,<next_b0=reg128#16
+# asm 2: veor >next_diag3=q10,<next_diag3=q12,<next_b0=q15
+veor q10,q12,q15
+
+# qhasm: 4x a1 = diag0 + diag3
+# asm 1: vadd.i32 >a1=reg128#13,<diag0=reg128#8,<diag3=reg128#7
+# asm 2: vadd.i32 >a1=q12,<diag0=q7,<diag3=q6
+vadd.i32 q12,q7,q6
+
+# qhasm: 4x next_a1 = next_diag0 + next_diag3
+# asm 1: vadd.i32 >next_a1=reg128#14,<next_diag0=reg128#2,<next_diag3=reg128#11
+# asm 2: vadd.i32 >next_a1=q13,<next_diag0=q1,<next_diag3=q10
+vadd.i32 q13,q1,q10
+
+# qhasm: 4x b1 = a1 << 9
+# asm 1: vshl.i32 >b1=reg128#15,<a1=reg128#13,#9
+# asm 2: vshl.i32 >b1=q14,<a1=q12,#9
+vshl.i32 q14,q12,#9
+
+# qhasm: 4x next_b1 = next_a1 << 9
+# asm 1: vshl.i32 >next_b1=reg128#16,<next_a1=reg128#14,#9
+# asm 2: vshl.i32 >next_b1=q15,<next_a1=q13,#9
+vshl.i32 q15,q13,#9
+
+# qhasm: 4x b1 insert= a1 >> 23
+# asm 1: vsri.i32 <b1=reg128#15,<a1=reg128#13,#23
+# asm 2: vsri.i32 <b1=q14,<a1=q12,#23
+vsri.i32 q14,q12,#23
+
+# qhasm: 4x next_b1 insert= next_a1 >> 23
+# asm 1: vsri.i32 <next_b1=reg128#16,<next_a1=reg128#14,#23
+# asm 2: vsri.i32 <next_b1=q15,<next_a1=q13,#23
+vsri.i32 q15,q13,#23
+
+# qhasm: diag2 ^= b1
+# asm 1: veor >diag2=reg128#6,<diag2=reg128#6,<b1=reg128#15
+# asm 2: veor >diag2=q5,<diag2=q5,<b1=q14
+veor q5,q5,q14
+
+# qhasm: next_diag2 ^= next_b1
+# asm 1: veor >next_diag2=reg128#12,<next_diag2=reg128#12,<next_b1=reg128#16
+# asm 2: veor >next_diag2=q11,<next_diag2=q11,<next_b1=q15
+veor q11,q11,q15
+
+# qhasm: 4x a2 = diag3 + diag2
+# asm 1: vadd.i32 >a2=reg128#13,<diag3=reg128#7,<diag2=reg128#6
+# asm 2: vadd.i32 >a2=q12,<diag3=q6,<diag2=q5
+vadd.i32 q12,q6,q5
+
+# qhasm: diag3 = diag3[3] diag3[0,1,2]
+# asm 1: vext.32 >diag3=reg128#7,<diag3=reg128#7,<diag3=reg128#7,#3
+# asm 2: vext.32 >diag3=q6,<diag3=q6,<diag3=q6,#3
+vext.32 q6,q6,q6,#3
+
+# qhasm: 4x next_a2 = next_diag3 + next_diag2
+# asm 1: vadd.i32 >next_a2=reg128#14,<next_diag3=reg128#11,<next_diag2=reg128#12
+# asm 2: vadd.i32 >next_a2=q13,<next_diag3=q10,<next_diag2=q11
+vadd.i32 q13,q10,q11
+
+# qhasm: 4x b2 = a2 << 13
+# asm 1: vshl.i32 >b2=reg128#15,<a2=reg128#13,#13
+# asm 2: vshl.i32 >b2=q14,<a2=q12,#13
+vshl.i32 q14,q12,#13
+
+# qhasm: next_diag3 = next_diag3[3] next_diag3[0,1,2]
+# asm 1: vext.32 >next_diag3=reg128#11,<next_diag3=reg128#11,<next_diag3=reg128#11,#3
+# asm 2: vext.32 >next_diag3=q10,<next_diag3=q10,<next_diag3=q10,#3
+vext.32 q10,q10,q10,#3
+
+# qhasm: 4x next_b2 = next_a2 << 13
+# asm 1: vshl.i32 >next_b2=reg128#16,<next_a2=reg128#14,#13
+# asm 2: vshl.i32 >next_b2=q15,<next_a2=q13,#13
+vshl.i32 q15,q13,#13
+
+# qhasm: 4x b2 insert= a2 >> 19
+# asm 1: vsri.i32 <b2=reg128#15,<a2=reg128#13,#19
+# asm 2: vsri.i32 <b2=q14,<a2=q12,#19
+vsri.i32 q14,q12,#19
+
+# qhasm: 4x next_b2 insert= next_a2 >> 19
+# asm 1: vsri.i32 <next_b2=reg128#16,<next_a2=reg128#14,#19
+# asm 2: vsri.i32 <next_b2=q15,<next_a2=q13,#19
+vsri.i32 q15,q13,#19
+
+# qhasm: diag1 ^= b2
+# asm 1: veor >diag1=reg128#9,<diag1=reg128#9,<b2=reg128#15
+# asm 2: veor >diag1=q8,<diag1=q8,<b2=q14
+veor q8,q8,q14
+
+# qhasm: next_diag1 ^= next_b2
+# asm 1: veor >next_diag1=reg128#5,<next_diag1=reg128#5,<next_b2=reg128#16
+# asm 2: veor >next_diag1=q4,<next_diag1=q4,<next_b2=q15
+veor q4,q4,q15
+
+# qhasm: 4x a3 = diag2 + diag1
+# asm 1: vadd.i32 >a3=reg128#13,<diag2=reg128#6,<diag1=reg128#9
+# asm 2: vadd.i32 >a3=q12,<diag2=q5,<diag1=q8
+vadd.i32 q12,q5,q8
+
+# qhasm: diag2 = diag2[2,3] diag2[0,1]
+# asm 1: vswp <diag2=reg128#6%bot,<diag2=reg128#6%top
+# asm 2: vswp <diag2=d10,<diag2=d11
+vswp d10,d11
+
+# qhasm: 4x next_a3 = next_diag2 + next_diag1
+# asm 1: vadd.i32 >next_a3=reg128#14,<next_diag2=reg128#12,<next_diag1=reg128#5
+# asm 2: vadd.i32 >next_a3=q13,<next_diag2=q11,<next_diag1=q4
+vadd.i32 q13,q11,q4
+
+# qhasm: 4x b3 = a3 << 18
+# asm 1: vshl.i32 >b3=reg128#15,<a3=reg128#13,#18
+# asm 2: vshl.i32 >b3=q14,<a3=q12,#18
+vshl.i32 q14,q12,#18
+
+# qhasm: next_diag2 = next_diag2[2,3] next_diag2[0,1]
+# asm 1: vswp <next_diag2=reg128#12%bot,<next_diag2=reg128#12%top
+# asm 2: vswp <next_diag2=d22,<next_diag2=d23
+vswp d22,d23
+
+# qhasm: 4x next_b3 = next_a3 << 18
+# asm 1: vshl.i32 >next_b3=reg128#16,<next_a3=reg128#14,#18
+# asm 2: vshl.i32 >next_b3=q15,<next_a3=q13,#18
+vshl.i32 q15,q13,#18
+
+# qhasm: 4x b3 insert= a3 >> 14
+# asm 1: vsri.i32 <b3=reg128#15,<a3=reg128#13,#14
+# asm 2: vsri.i32 <b3=q14,<a3=q12,#14
+vsri.i32 q14,q12,#14
+
+# qhasm: diag1 = diag1[1,2,3] diag1[0]
+# asm 1: vext.32 >diag1=reg128#9,<diag1=reg128#9,<diag1=reg128#9,#1
+# asm 2: vext.32 >diag1=q8,<diag1=q8,<diag1=q8,#1
+vext.32 q8,q8,q8,#1
+
+# qhasm: 4x next_b3 insert= next_a3 >> 14
+# asm 1: vsri.i32 <next_b3=reg128#16,<next_a3=reg128#14,#14
+# asm 2: vsri.i32 <next_b3=q15,<next_a3=q13,#14
+vsri.i32 q15,q13,#14
+
+# qhasm: diag0 ^= b3
+# asm 1: veor >diag0=reg128#8,<diag0=reg128#8,<b3=reg128#15
+# asm 2: veor >diag0=q7,<diag0=q7,<b3=q14
+veor q7,q7,q14
+
+# qhasm: next_diag1 = next_diag1[1,2,3] next_diag1[0]
+# asm 1: vext.32 >next_diag1=reg128#5,<next_diag1=reg128#5,<next_diag1=reg128#5,#1
+# asm 2: vext.32 >next_diag1=q4,<next_diag1=q4,<next_diag1=q4,#1
+vext.32 q4,q4,q4,#1
+
+# qhasm: next_diag0 ^= next_b3
+# asm 1: veor >next_diag0=reg128#2,<next_diag0=reg128#2,<next_b3=reg128#16
+# asm 2: veor >next_diag0=q1,<next_diag0=q1,<next_b3=q15
+veor q1,q1,q15
+
+# qhasm: 4x a0 = diag3 + diag0
+# asm 1: vadd.i32 >a0=reg128#13,<diag3=reg128#7,<diag0=reg128#8
+# asm 2: vadd.i32 >a0=q12,<diag3=q6,<diag0=q7
+vadd.i32 q12,q6,q7
+
+# qhasm: 4x next_a0 = next_diag3 + next_diag0
+# asm 1: vadd.i32 >next_a0=reg128#14,<next_diag3=reg128#11,<next_diag0=reg128#2
+# asm 2: vadd.i32 >next_a0=q13,<next_diag3=q10,<next_diag0=q1
+vadd.i32 q13,q10,q1
+
+# qhasm: 4x b0 = a0 << 7
+# asm 1: vshl.i32 >b0=reg128#15,<a0=reg128#13,#7
+# asm 2: vshl.i32 >b0=q14,<a0=q12,#7
+vshl.i32 q14,q12,#7
+
+# qhasm: 4x next_b0 = next_a0 << 7
+# asm 1: vshl.i32 >next_b0=reg128#16,<next_a0=reg128#14,#7
+# asm 2: vshl.i32 >next_b0=q15,<next_a0=q13,#7
+vshl.i32 q15,q13,#7
+
+# qhasm: 4x b0 insert= a0 >> 25
+# asm 1: vsri.i32 <b0=reg128#15,<a0=reg128#13,#25
+# asm 2: vsri.i32 <b0=q14,<a0=q12,#25
+vsri.i32 q14,q12,#25
+
+# qhasm: 4x next_b0 insert= next_a0 >> 25
+# asm 1: vsri.i32 <next_b0=reg128#16,<next_a0=reg128#14,#25
+# asm 2: vsri.i32 <next_b0=q15,<next_a0=q13,#25
+vsri.i32 q15,q13,#25
+
+# qhasm: diag1 ^= b0
+# asm 1: veor >diag1=reg128#9,<diag1=reg128#9,<b0=reg128#15
+# asm 2: veor >diag1=q8,<diag1=q8,<b0=q14
+veor q8,q8,q14
+
+# qhasm: next_diag1 ^= next_b0
+# asm 1: veor >next_diag1=reg128#5,<next_diag1=reg128#5,<next_b0=reg128#16
+# asm 2: veor >next_diag1=q4,<next_diag1=q4,<next_b0=q15
+veor q4,q4,q15
+
+# qhasm: 4x a1 = diag0 + diag1
+# asm 1: vadd.i32 >a1=reg128#13,<diag0=reg128#8,<diag1=reg128#9
+# asm 2: vadd.i32 >a1=q12,<diag0=q7,<diag1=q8
+vadd.i32 q12,q7,q8
+
+# qhasm: 4x next_a1 = next_diag0 + next_diag1
+# asm 1: vadd.i32 >next_a1=reg128#14,<next_diag0=reg128#2,<next_diag1=reg128#5
+# asm 2: vadd.i32 >next_a1=q13,<next_diag0=q1,<next_diag1=q4
+vadd.i32 q13,q1,q4
+
+# qhasm: 4x b1 = a1 << 9
+# asm 1: vshl.i32 >b1=reg128#15,<a1=reg128#13,#9
+# asm 2: vshl.i32 >b1=q14,<a1=q12,#9
+vshl.i32 q14,q12,#9
+
+# qhasm: 4x next_b1 = next_a1 << 9
+# asm 1: vshl.i32 >next_b1=reg128#16,<next_a1=reg128#14,#9
+# asm 2: vshl.i32 >next_b1=q15,<next_a1=q13,#9
+vshl.i32 q15,q13,#9
+
+# qhasm: 4x b1 insert= a1 >> 23
+# asm 1: vsri.i32 <b1=reg128#15,<a1=reg128#13,#23
+# asm 2: vsri.i32 <b1=q14,<a1=q12,#23
+vsri.i32 q14,q12,#23
+
+# qhasm: unsigned>? i -= 2
+# asm 1: subs <i=int32#5,<i=int32#5,#2
+# asm 2: subs <i=r4,<i=r4,#2
+subs r4,r4,#2
+
+# qhasm: 4x next_b1 insert= next_a1 >> 23
+# asm 1: vsri.i32 <next_b1=reg128#16,<next_a1=reg128#14,#23
+# asm 2: vsri.i32 <next_b1=q15,<next_a1=q13,#23
+vsri.i32 q15,q13,#23
+
+# qhasm: diag2 ^= b1
+# asm 1: veor >diag2=reg128#6,<diag2=reg128#6,<b1=reg128#15
+# asm 2: veor >diag2=q5,<diag2=q5,<b1=q14
+veor q5,q5,q14
+
+# qhasm: next_diag2 ^= next_b1
+# asm 1: veor >next_diag2=reg128#12,<next_diag2=reg128#12,<next_b1=reg128#16
+# asm 2: veor >next_diag2=q11,<next_diag2=q11,<next_b1=q15
+veor q11,q11,q15
+
+# qhasm: 4x a2 = diag1 + diag2
+# asm 1: vadd.i32 >a2=reg128#13,<diag1=reg128#9,<diag2=reg128#6
+# asm 2: vadd.i32 >a2=q12,<diag1=q8,<diag2=q5
+vadd.i32 q12,q8,q5
+
+# qhasm: diag1 = diag1[3] diag1[0,1,2]
+# asm 1: vext.32 >diag1=reg128#9,<diag1=reg128#9,<diag1=reg128#9,#3
+# asm 2: vext.32 >diag1=q8,<diag1=q8,<diag1=q8,#3
+vext.32 q8,q8,q8,#3
+
+# qhasm: 4x next_a2 = next_diag1 + next_diag2
+# asm 1: vadd.i32 >next_a2=reg128#14,<next_diag1=reg128#5,<next_diag2=reg128#12
+# asm 2: vadd.i32 >next_a2=q13,<next_diag1=q4,<next_diag2=q11
+vadd.i32 q13,q4,q11
+
+# qhasm: 4x b2 = a2 << 13
+# asm 1: vshl.i32 >b2=reg128#15,<a2=reg128#13,#13
+# asm 2: vshl.i32 >b2=q14,<a2=q12,#13
+vshl.i32 q14,q12,#13
+
+# qhasm: next_diag1 = next_diag1[3] next_diag1[0,1,2]
+# asm 1: vext.32 >next_diag1=reg128#5,<next_diag1=reg128#5,<next_diag1=reg128#5,#3
+# asm 2: vext.32 >next_diag1=q4,<next_diag1=q4,<next_diag1=q4,#3
+vext.32 q4,q4,q4,#3
+
+# qhasm: 4x next_b2 = next_a2 << 13
+# asm 1: vshl.i32 >next_b2=reg128#16,<next_a2=reg128#14,#13
+# asm 2: vshl.i32 >next_b2=q15,<next_a2=q13,#13
+vshl.i32 q15,q13,#13
+
+# qhasm: 4x b2 insert= a2 >> 19
+# asm 1: vsri.i32 <b2=reg128#15,<a2=reg128#13,#19
+# asm 2: vsri.i32 <b2=q14,<a2=q12,#19
+vsri.i32 q14,q12,#19
+
+# qhasm: 4x next_b2 insert= next_a2 >> 19
+# asm 1: vsri.i32 <next_b2=reg128#16,<next_a2=reg128#14,#19
+# asm 2: vsri.i32 <next_b2=q15,<next_a2=q13,#19
+vsri.i32 q15,q13,#19
+
+# qhasm: diag3 ^= b2
+# asm 1: veor >diag3=reg128#7,<diag3=reg128#7,<b2=reg128#15
+# asm 2: veor >diag3=q6,<diag3=q6,<b2=q14
+veor q6,q6,q14
+
+# qhasm: next_diag3 ^= next_b2
+# asm 1: veor >next_diag3=reg128#11,<next_diag3=reg128#11,<next_b2=reg128#16
+# asm 2: veor >next_diag3=q10,<next_diag3=q10,<next_b2=q15
+veor q10,q10,q15
+
+# qhasm: 4x a3 = diag2 + diag3
+# asm 1: vadd.i32 >a3=reg128#13,<diag2=reg128#6,<diag3=reg128#7
+# asm 2: vadd.i32 >a3=q12,<diag2=q5,<diag3=q6
+vadd.i32 q12,q5,q6
+
+# qhasm: diag2 = diag2[2,3] diag2[0,1]
+# asm 1: vswp <diag2=reg128#6%bot,<diag2=reg128#6%top
+# asm 2: vswp <diag2=d10,<diag2=d11
+vswp d10,d11
+
+# qhasm: 4x next_a3 = next_diag2 + next_diag3
+# asm 1: vadd.i32 >next_a3=reg128#14,<next_diag2=reg128#12,<next_diag3=reg128#11
+# asm 2: vadd.i32 >next_a3=q13,<next_diag2=q11,<next_diag3=q10
+vadd.i32 q13,q11,q10
+
+# qhasm: 4x b3 = a3 << 18
+# asm 1: vshl.i32 >b3=reg128#15,<a3=reg128#13,#18
+# asm 2: vshl.i32 >b3=q14,<a3=q12,#18
+vshl.i32 q14,q12,#18
+
+# qhasm: next_diag2 = next_diag2[2,3] next_diag2[0,1]
+# asm 1: vswp <next_diag2=reg128#12%bot,<next_diag2=reg128#12%top
+# asm 2: vswp <next_diag2=d22,<next_diag2=d23
+vswp d22,d23
+
+# qhasm: 4x next_b3 = next_a3 << 18
+# asm 1: vshl.i32 >next_b3=reg128#16,<next_a3=reg128#14,#18
+# asm 2: vshl.i32 >next_b3=q15,<next_a3=q13,#18
+vshl.i32 q15,q13,#18
+
+# qhasm: 4x b3 insert= a3 >> 14
+# asm 1: vsri.i32 <b3=reg128#15,<a3=reg128#13,#14
+# asm 2: vsri.i32 <b3=q14,<a3=q12,#14
+vsri.i32 q14,q12,#14
+
+# qhasm: diag3 = diag3[1,2,3] diag3[0]
+# asm 1: vext.32 >diag3=reg128#7,<diag3=reg128#7,<diag3=reg128#7,#1
+# asm 2: vext.32 >diag3=q6,<diag3=q6,<diag3=q6,#1
+vext.32 q6,q6,q6,#1
+
+# qhasm: 4x next_b3 insert= next_a3 >> 14
+# asm 1: vsri.i32 <next_b3=reg128#16,<next_a3=reg128#14,#14
+# asm 2: vsri.i32 <next_b3=q15,<next_a3=q13,#14
+vsri.i32 q15,q13,#14
+
+# qhasm: diag0 ^= b3
+# asm 1: veor >diag0=reg128#8,<diag0=reg128#8,<b3=reg128#15
+# asm 2: veor >diag0=q7,<diag0=q7,<b3=q14
+veor q7,q7,q14
+
+# qhasm: next_diag3 = next_diag3[1,2,3] next_diag3[0]
+# asm 1: vext.32 >next_diag3=reg128#13,<next_diag3=reg128#11,<next_diag3=reg128#11,#1
+# asm 2: vext.32 >next_diag3=q12,<next_diag3=q10,<next_diag3=q10,#1
+vext.32 q12,q10,q10,#1
+
+# qhasm: next_diag0 ^= next_b3
+# asm 1: veor >next_diag0=reg128#2,<next_diag0=reg128#2,<next_b3=reg128#16
+# asm 2: veor >next_diag0=q1,<next_diag0=q1,<next_b3=q15
+veor q1,q1,q15
+
+# qhasm: goto mainloop2 if unsigned>
+bhi ._mainloop2
+
+# qhasm: 2x abab = 0xffffffff
+# asm 1: vmov.i64 >abab=reg128#11,#0xffffffff
+# asm 2: vmov.i64 >abab=q10,#0xffffffff
+vmov.i64 q10,#0xffffffff
+
+# qhasm: new x4x9x14x3
+
+# qhasm: x4x9x14x3 bot = stack_start3 bot
+# asm 1: vldr <x4x9x14x3=reg128#14%bot,<stack_start3=stack128#9
+# asm 2: vldr <x4x9x14x3=d26,<stack_start3=[sp,#224]
+vldr d26,[sp,#224]
+
+# qhasm: x4x9x14x3 top = stack_start3 top
+# asm 1: vldr <x4x9x14x3=reg128#14%top,<stack_start3=stack128#9
+# asm 2: vldr <x4x9x14x3=d27,<stack_start3=[sp,#232]
+vldr d27,[sp,#232]
+
+# qhasm: 4x x0x5x10x15 = diag0 + start0
+# asm 1: vadd.i32 >x0x5x10x15=reg128#8,<diag0=reg128#8,<start0=reg128#3
+# asm 2: vadd.i32 >x0x5x10x15=q7,<diag0=q7,<start0=q2
+vadd.i32 q7,q7,q2
+
+# qhasm: 4x x12x1x6x11 = diag1 + start1
+# asm 1: vadd.i32 >x12x1x6x11=reg128#9,<diag1=reg128#9,<start1=reg128#4
+# asm 2: vadd.i32 >x12x1x6x11=q8,<diag1=q8,<start1=q3
+vadd.i32 q8,q8,q3
+
+# qhasm: 4x x8x13x2x7 = diag2 + start2
+# asm 1: vadd.i32 >x8x13x2x7=reg128#6,<diag2=reg128#6,<start2=reg128#10
+# asm 2: vadd.i32 >x8x13x2x7=q5,<diag2=q5,<start2=q9
+vadd.i32 q5,q5,q9
+
+# qhasm: 4x x4x9x14x3 += diag3
+# asm 1: vadd.i32 >x4x9x14x3=reg128#7,<x4x9x14x3=reg128#14,<diag3=reg128#7
+# asm 2: vadd.i32 >x4x9x14x3=q6,<x4x9x14x3=q13,<diag3=q6
+vadd.i32 q6,q13,q6
+
+# qhasm: x0x1x10x11 = x0x5x10x15
+# asm 1: vmov >x0x1x10x11=reg128#10,<x0x5x10x15=reg128#8
+# asm 2: vmov >x0x1x10x11=q9,<x0x5x10x15=q7
+vmov q9,q7
+
+# qhasm: x12x13x6x7 = x12x1x6x11
+# asm 1: vmov >x12x13x6x7=reg128#14,<x12x1x6x11=reg128#9
+# asm 2: vmov >x12x13x6x7=q13,<x12x1x6x11=q8
+vmov q13,q8
+
+# qhasm: x8x9x2x3 = x8x13x2x7
+# asm 1: vmov >x8x9x2x3=reg128#15,<x8x13x2x7=reg128#6
+# asm 2: vmov >x8x9x2x3=q14,<x8x13x2x7=q5
+vmov q14,q5
+
+# qhasm: x4x5x14x15 = x4x9x14x3
+# asm 1: vmov >x4x5x14x15=reg128#16,<x4x9x14x3=reg128#7
+# asm 2: vmov >x4x5x14x15=q15,<x4x9x14x3=q6
+vmov q15,q6
+
+# qhasm: x0x1x10x11 = (abab & x0x1x10x11) | (~abab & x12x1x6x11)
+# asm 1: vbif <x0x1x10x11=reg128#10,<x12x1x6x11=reg128#9,<abab=reg128#11
+# asm 2: vbif <x0x1x10x11=q9,<x12x1x6x11=q8,<abab=q10
+vbif q9,q8,q10
+
+# qhasm: x12x13x6x7 = (abab & x12x13x6x7) | (~abab & x8x13x2x7)
+# asm 1: vbif <x12x13x6x7=reg128#14,<x8x13x2x7=reg128#6,<abab=reg128#11
+# asm 2: vbif <x12x13x6x7=q13,<x8x13x2x7=q5,<abab=q10
+vbif q13,q5,q10
+
+# qhasm: x8x9x2x3 = (abab & x8x9x2x3) | (~abab & x4x9x14x3)
+# asm 1: vbif <x8x9x2x3=reg128#15,<x4x9x14x3=reg128#7,<abab=reg128#11
+# asm 2: vbif <x8x9x2x3=q14,<x4x9x14x3=q6,<abab=q10
+vbif q14,q6,q10
+
+# qhasm: x4x5x14x15 = (abab & x4x5x14x15) | (~abab & x0x5x10x15)
+# asm 1: vbif <x4x5x14x15=reg128#16,<x0x5x10x15=reg128#8,<abab=reg128#11
+# asm 2: vbif <x4x5x14x15=q15,<x0x5x10x15=q7,<abab=q10
+vbif q15,q7,q10
+
+# qhasm: x0x1x2x3 = x0x1x10x11
+# asm 1: vmov >x0x1x2x3=reg128#6,<x0x1x10x11=reg128#10
+# asm 2: vmov >x0x1x2x3=q5,<x0x1x10x11=q9
+vmov q5,q9
+
+# qhasm: x4x5x6x7 = x4x5x14x15
+# asm 1: vmov >x4x5x6x7=reg128#7,<x4x5x14x15=reg128#16
+# asm 2: vmov >x4x5x6x7=q6,<x4x5x14x15=q15
+vmov q6,q15
+
+# qhasm: x8x9x10x11 = x8x9x2x3
+# asm 1: vmov >x8x9x10x11=reg128#8,<x8x9x2x3=reg128#15
+# asm 2: vmov >x8x9x10x11=q7,<x8x9x2x3=q14
+vmov q7,q14
+
+# qhasm: x12x13x14x15 = x12x13x6x7
+# asm 1: vmov >x12x13x14x15=reg128#9,<x12x13x6x7=reg128#14
+# asm 2: vmov >x12x13x14x15=q8,<x12x13x6x7=q13
+vmov q8,q13
+
+# qhasm: x0x1x2x3 = x0x1x2x3[0,1] x8x9x2x3[2,3]
+# asm 1: vmov <x0x1x2x3=reg128#6%top,<x8x9x2x3=reg128#15%top
+# asm 2: vmov <x0x1x2x3=d11,<x8x9x2x3=d29
+vmov d11,d29
+
+# qhasm: x4x5x6x7 = x4x5x6x7[0,1] x12x13x6x7[2,3]
+# asm 1: vmov <x4x5x6x7=reg128#7%top,<x12x13x6x7=reg128#14%top
+# asm 2: vmov <x4x5x6x7=d13,<x12x13x6x7=d27
+vmov d13,d27
+
+# qhasm: x8x9x10x11 = x8x9x10x11[0,1] x0x1x10x11[2,3]
+# asm 1: vmov <x8x9x10x11=reg128#8%top,<x0x1x10x11=reg128#10%top
+# asm 2: vmov <x8x9x10x11=d15,<x0x1x10x11=d19
+vmov d15,d19
+
+# qhasm: x12x13x14x15 = x12x13x14x15[0,1] x4x5x14x15[2,3]
+# asm 1: vmov <x12x13x14x15=reg128#9%top,<x4x5x14x15=reg128#16%top
+# asm 2: vmov <x12x13x14x15=d17,<x4x5x14x15=d31
+vmov d17,d31
+
+# qhasm: =? m - 0
+# asm 1: cmp <m=int32#2,#0
+# asm 2: cmp <m=r1,#0
+cmp r1,#0
+
+# qhasm: goto nomessage2 if =
+beq ._nomessage2
+
+# qhasm: m0m1m2m3 = mem128[m]
+# asm 1: vld1.8 {>m0m1m2m3=reg128#10%bot->m0m1m2m3=reg128#10%top},[<m=int32#2]
+# asm 2: vld1.8 {>m0m1m2m3=d18->m0m1m2m3=d19},[<m=r1]
+vld1.8 {d18-d19},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m4m5m6m7 = mem128[m]
+# asm 1: vld1.8 {>m4m5m6m7=reg128#14%bot->m4m5m6m7=reg128#14%top},[<m=int32#2]
+# asm 2: vld1.8 {>m4m5m6m7=d26->m4m5m6m7=d27},[<m=r1]
+vld1.8 {d26-d27},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m8m9m10m11 = mem128[m]
+# asm 1: vld1.8 {>m8m9m10m11=reg128#15%bot->m8m9m10m11=reg128#15%top},[<m=int32#2]
+# asm 2: vld1.8 {>m8m9m10m11=d28->m8m9m10m11=d29},[<m=r1]
+vld1.8 {d28-d29},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m12m13m14m15 = mem128[m]
+# asm 1: vld1.8 {>m12m13m14m15=reg128#16%bot->m12m13m14m15=reg128#16%top},[<m=int32#2]
+# asm 2: vld1.8 {>m12m13m14m15=d30->m12m13m14m15=d31},[<m=r1]
+vld1.8 {d30-d31},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: x0x1x2x3 ^= m0m1m2m3
+# asm 1: veor >x0x1x2x3=reg128#6,<x0x1x2x3=reg128#6,<m0m1m2m3=reg128#10
+# asm 2: veor >x0x1x2x3=q5,<x0x1x2x3=q5,<m0m1m2m3=q9
+veor q5,q5,q9
+
+# qhasm: x4x5x6x7 ^= m4m5m6m7
+# asm 1: veor >x4x5x6x7=reg128#7,<x4x5x6x7=reg128#7,<m4m5m6m7=reg128#14
+# asm 2: veor >x4x5x6x7=q6,<x4x5x6x7=q6,<m4m5m6m7=q13
+veor q6,q6,q13
+
+# qhasm: x8x9x10x11 ^= m8m9m10m11
+# asm 1: veor >x8x9x10x11=reg128#8,<x8x9x10x11=reg128#8,<m8m9m10m11=reg128#15
+# asm 2: veor >x8x9x10x11=q7,<x8x9x10x11=q7,<m8m9m10m11=q14
+veor q7,q7,q14
+
+# qhasm: x12x13x14x15 ^= m12m13m14m15
+# asm 1: veor >x12x13x14x15=reg128#9,<x12x13x14x15=reg128#9,<m12m13m14m15=reg128#16
+# asm 2: veor >x12x13x14x15=q8,<x12x13x14x15=q8,<m12m13m14m15=q15
+veor q8,q8,q15
+
+# qhasm: nomessage2:
+._nomessage2:
+
+# qhasm: mem128[c] = x0x1x2x3
+# asm 1: vst1.8 {<x0x1x2x3=reg128#6%bot-<x0x1x2x3=reg128#6%top},[<c=int32#1]
+# asm 2: vst1.8 {<x0x1x2x3=d10-<x0x1x2x3=d11},[<c=r0]
+vst1.8 {d10-d11},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x4x5x6x7
+# asm 1: vst1.8 {<x4x5x6x7=reg128#7%bot-<x4x5x6x7=reg128#7%top},[<c=int32#1]
+# asm 2: vst1.8 {<x4x5x6x7=d12-<x4x5x6x7=d13},[<c=r0]
+vst1.8 {d12-d13},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x8x9x10x11
+# asm 1: vst1.8 {<x8x9x10x11=reg128#8%bot-<x8x9x10x11=reg128#8%top},[<c=int32#1]
+# asm 2: vst1.8 {<x8x9x10x11=d14-<x8x9x10x11=d15},[<c=r0]
+vst1.8 {d14-d15},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x12x13x14x15
+# asm 1: vst1.8 {<x12x13x14x15=reg128#9%bot-<x12x13x14x15=reg128#9%top},[<c=int32#1]
+# asm 2: vst1.8 {<x12x13x14x15=d16-<x12x13x14x15=d17},[<c=r0]
+vst1.8 {d16-d17},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: new x8x13x2x7
+
+# qhasm: x8x13x2x7 bot = next_start2 bot
+# asm 1: vldr <x8x13x2x7=reg128#6%bot,<next_start2=stack128#7
+# asm 2: vldr <x8x13x2x7=d10,<next_start2=[sp,#192]
+vldr d10,[sp,#192]
+
+# qhasm: x8x13x2x7 top = next_start2 top
+# asm 1: vldr <x8x13x2x7=reg128#6%top,<next_start2=stack128#7
+# asm 2: vldr <x8x13x2x7=d11,<next_start2=[sp,#200]
+vldr d11,[sp,#200]
+
+# qhasm: new x4x9x14x3
+
+# qhasm: x4x9x14x3 bot = next_start3 bot
+# asm 1: vldr <x4x9x14x3=reg128#7%bot,<next_start3=stack128#8
+# asm 2: vldr <x4x9x14x3=d12,<next_start3=[sp,#208]
+vldr d12,[sp,#208]
+
+# qhasm: x4x9x14x3 top = next_start3 top
+# asm 1: vldr <x4x9x14x3=reg128#7%top,<next_start3=stack128#8
+# asm 2: vldr <x4x9x14x3=d13,<next_start3=[sp,#216]
+vldr d13,[sp,#216]
+
+# qhasm: 4x x0x5x10x15 = next_diag0 + start0
+# asm 1: vadd.i32 >x0x5x10x15=reg128#2,<next_diag0=reg128#2,<start0=reg128#3
+# asm 2: vadd.i32 >x0x5x10x15=q1,<next_diag0=q1,<start0=q2
+vadd.i32 q1,q1,q2
+
+# qhasm: 4x x12x1x6x11 = next_diag1 + start1
+# asm 1: vadd.i32 >x12x1x6x11=reg128#5,<next_diag1=reg128#5,<start1=reg128#4
+# asm 2: vadd.i32 >x12x1x6x11=q4,<next_diag1=q4,<start1=q3
+vadd.i32 q4,q4,q3
+
+# qhasm: 4x x8x13x2x7 += next_diag2
+# asm 1: vadd.i32 >x8x13x2x7=reg128#6,<x8x13x2x7=reg128#6,<next_diag2=reg128#12
+# asm 2: vadd.i32 >x8x13x2x7=q5,<x8x13x2x7=q5,<next_diag2=q11
+vadd.i32 q5,q5,q11
+
+# qhasm: 4x x4x9x14x3 += next_diag3
+# asm 1: vadd.i32 >x4x9x14x3=reg128#7,<x4x9x14x3=reg128#7,<next_diag3=reg128#13
+# asm 2: vadd.i32 >x4x9x14x3=q6,<x4x9x14x3=q6,<next_diag3=q12
+vadd.i32 q6,q6,q12
+
+# qhasm: x0x1x10x11 = x0x5x10x15
+# asm 1: vmov >x0x1x10x11=reg128#8,<x0x5x10x15=reg128#2
+# asm 2: vmov >x0x1x10x11=q7,<x0x5x10x15=q1
+vmov q7,q1
+
+# qhasm: x12x13x6x7 = x12x1x6x11
+# asm 1: vmov >x12x13x6x7=reg128#9,<x12x1x6x11=reg128#5
+# asm 2: vmov >x12x13x6x7=q8,<x12x1x6x11=q4
+vmov q8,q4
+
+# qhasm: x8x9x2x3 = x8x13x2x7
+# asm 1: vmov >x8x9x2x3=reg128#10,<x8x13x2x7=reg128#6
+# asm 2: vmov >x8x9x2x3=q9,<x8x13x2x7=q5
+vmov q9,q5
+
+# qhasm: x4x5x14x15 = x4x9x14x3
+# asm 1: vmov >x4x5x14x15=reg128#12,<x4x9x14x3=reg128#7
+# asm 2: vmov >x4x5x14x15=q11,<x4x9x14x3=q6
+vmov q11,q6
+
+# qhasm: x0x1x10x11 = (abab & x0x1x10x11) | (~abab & x12x1x6x11)
+# asm 1: vbif <x0x1x10x11=reg128#8,<x12x1x6x11=reg128#5,<abab=reg128#11
+# asm 2: vbif <x0x1x10x11=q7,<x12x1x6x11=q4,<abab=q10
+vbif q7,q4,q10
+
+# qhasm: x12x13x6x7 = (abab & x12x13x6x7) | (~abab & x8x13x2x7)
+# asm 1: vbif <x12x13x6x7=reg128#9,<x8x13x2x7=reg128#6,<abab=reg128#11
+# asm 2: vbif <x12x13x6x7=q8,<x8x13x2x7=q5,<abab=q10
+vbif q8,q5,q10
+
+# qhasm: x8x9x2x3 = (abab & x8x9x2x3) | (~abab & x4x9x14x3)
+# asm 1: vbif <x8x9x2x3=reg128#10,<x4x9x14x3=reg128#7,<abab=reg128#11
+# asm 2: vbif <x8x9x2x3=q9,<x4x9x14x3=q6,<abab=q10
+vbif q9,q6,q10
+
+# qhasm: x4x5x14x15 = (abab & x4x5x14x15) | (~abab & x0x5x10x15)
+# asm 1: vbif <x4x5x14x15=reg128#12,<x0x5x10x15=reg128#2,<abab=reg128#11
+# asm 2: vbif <x4x5x14x15=q11,<x0x5x10x15=q1,<abab=q10
+vbif q11,q1,q10
+
+# qhasm: x0x1x2x3 = x0x1x10x11
+# asm 1: vmov >x0x1x2x3=reg128#2,<x0x1x10x11=reg128#8
+# asm 2: vmov >x0x1x2x3=q1,<x0x1x10x11=q7
+vmov q1,q7
+
+# qhasm: x4x5x6x7 = x4x5x14x15
+# asm 1: vmov >x4x5x6x7=reg128#5,<x4x5x14x15=reg128#12
+# asm 2: vmov >x4x5x6x7=q4,<x4x5x14x15=q11
+vmov q4,q11
+
+# qhasm: x8x9x10x11 = x8x9x2x3
+# asm 1: vmov >x8x9x10x11=reg128#6,<x8x9x2x3=reg128#10
+# asm 2: vmov >x8x9x10x11=q5,<x8x9x2x3=q9
+vmov q5,q9
+
+# qhasm: x12x13x14x15 = x12x13x6x7
+# asm 1: vmov >x12x13x14x15=reg128#7,<x12x13x6x7=reg128#9
+# asm 2: vmov >x12x13x14x15=q6,<x12x13x6x7=q8
+vmov q6,q8
+
+# qhasm: x0x1x2x3 = x0x1x2x3[0,1] x8x9x2x3[2,3]
+# asm 1: vmov <x0x1x2x3=reg128#2%top,<x8x9x2x3=reg128#10%top
+# asm 2: vmov <x0x1x2x3=d3,<x8x9x2x3=d19
+vmov d3,d19
+
+# qhasm: x4x5x6x7 = x4x5x6x7[0,1] x12x13x6x7[2,3]
+# asm 1: vmov <x4x5x6x7=reg128#5%top,<x12x13x6x7=reg128#9%top
+# asm 2: vmov <x4x5x6x7=d9,<x12x13x6x7=d17
+vmov d9,d17
+
+# qhasm: x8x9x10x11 = x8x9x10x11[0,1] x0x1x10x11[2,3]
+# asm 1: vmov <x8x9x10x11=reg128#6%top,<x0x1x10x11=reg128#8%top
+# asm 2: vmov <x8x9x10x11=d11,<x0x1x10x11=d15
+vmov d11,d15
+
+# qhasm: x12x13x14x15 = x12x13x14x15[0,1] x4x5x14x15[2,3]
+# asm 1: vmov <x12x13x14x15=reg128#7%top,<x4x5x14x15=reg128#12%top
+# asm 2: vmov <x12x13x14x15=d13,<x4x5x14x15=d23
+vmov d13,d23
+
+# qhasm: =? m - 0
+# asm 1: cmp <m=int32#2,#0
+# asm 2: cmp <m=r1,#0
+cmp r1,#0
+
+# qhasm: goto nomessage2next if =
+beq ._nomessage2next
+
+# qhasm: m0m1m2m3 = mem128[m]
+# asm 1: vld1.8 {>m0m1m2m3=reg128#8%bot->m0m1m2m3=reg128#8%top},[<m=int32#2]
+# asm 2: vld1.8 {>m0m1m2m3=d14->m0m1m2m3=d15},[<m=r1]
+vld1.8 {d14-d15},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m4m5m6m7 = mem128[m]
+# asm 1: vld1.8 {>m4m5m6m7=reg128#9%bot->m4m5m6m7=reg128#9%top},[<m=int32#2]
+# asm 2: vld1.8 {>m4m5m6m7=d16->m4m5m6m7=d17},[<m=r1]
+vld1.8 {d16-d17},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m8m9m10m11 = mem128[m]
+# asm 1: vld1.8 {>m8m9m10m11=reg128#10%bot->m8m9m10m11=reg128#10%top},[<m=int32#2]
+# asm 2: vld1.8 {>m8m9m10m11=d18->m8m9m10m11=d19},[<m=r1]
+vld1.8 {d18-d19},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m12m13m14m15 = mem128[m]
+# asm 1: vld1.8 {>m12m13m14m15=reg128#11%bot->m12m13m14m15=reg128#11%top},[<m=int32#2]
+# asm 2: vld1.8 {>m12m13m14m15=d20->m12m13m14m15=d21},[<m=r1]
+vld1.8 {d20-d21},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: x0x1x2x3 ^= m0m1m2m3
+# asm 1: veor >x0x1x2x3=reg128#2,<x0x1x2x3=reg128#2,<m0m1m2m3=reg128#8
+# asm 2: veor >x0x1x2x3=q1,<x0x1x2x3=q1,<m0m1m2m3=q7
+veor q1,q1,q7
+
+# qhasm: x4x5x6x7 ^= m4m5m6m7
+# asm 1: veor >x4x5x6x7=reg128#5,<x4x5x6x7=reg128#5,<m4m5m6m7=reg128#9
+# asm 2: veor >x4x5x6x7=q4,<x4x5x6x7=q4,<m4m5m6m7=q8
+veor q4,q4,q8
+
+# qhasm: x8x9x10x11 ^= m8m9m10m11
+# asm 1: veor >x8x9x10x11=reg128#6,<x8x9x10x11=reg128#6,<m8m9m10m11=reg128#10
+# asm 2: veor >x8x9x10x11=q5,<x8x9x10x11=q5,<m8m9m10m11=q9
+veor q5,q5,q9
+
+# qhasm: x12x13x14x15 ^= m12m13m14m15
+# asm 1: veor >x12x13x14x15=reg128#7,<x12x13x14x15=reg128#7,<m12m13m14m15=reg128#11
+# asm 2: veor >x12x13x14x15=q6,<x12x13x14x15=q6,<m12m13m14m15=q10
+veor q6,q6,q10
+
+# qhasm: nomessage2next:
+._nomessage2next:
+
+# qhasm: mem128[c] = x0x1x2x3
+# asm 1: vst1.8 {<x0x1x2x3=reg128#2%bot-<x0x1x2x3=reg128#2%top},[<c=int32#1]
+# asm 2: vst1.8 {<x0x1x2x3=d2-<x0x1x2x3=d3},[<c=r0]
+vst1.8 {d2-d3},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x4x5x6x7
+# asm 1: vst1.8 {<x4x5x6x7=reg128#5%bot-<x4x5x6x7=reg128#5%top},[<c=int32#1]
+# asm 2: vst1.8 {<x4x5x6x7=d8-<x4x5x6x7=d9},[<c=r0]
+vst1.8 {d8-d9},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x8x9x10x11
+# asm 1: vst1.8 {<x8x9x10x11=reg128#6%bot-<x8x9x10x11=reg128#6%top},[<c=int32#1]
+# asm 2: vst1.8 {<x8x9x10x11=d10-<x8x9x10x11=d11},[<c=r0]
+vst1.8 {d10-d11},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x12x13x14x15
+# asm 1: vst1.8 {<x12x13x14x15=reg128#7%bot-<x12x13x14x15=reg128#7%top},[<c=int32#1]
+# asm 2: vst1.8 {<x12x13x14x15=d12-<x12x13x14x15=d13},[<c=r0]
+vst1.8 {d12-d13},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: carry? mlenlow -= 128
+# asm 1: subs <mlenlow=int32#3,<mlenlow=int32#3,#128
+# asm 2: subs <mlenlow=r2,<mlenlow=r2,#128
+subs r2,r2,#128
+
+# qhasm: mlenhigh -= 0 - carry
+# asm 1: sbc <mlenhigh=int32#4,<mlenhigh=int32#4,#0
+# asm 2: sbc <mlenhigh=r3,<mlenhigh=r3,#0
+sbc r3,r3,#0
+
+# qhasm: unsigned<? mlenlow - 128
+# asm 1: cmp <mlenlow=int32#3,#128
+# asm 2: cmp <mlenlow=r2,#128
+cmp r2,#128
+
+# qhasm: goto mlenatleast128 if !unsigned<
+bhs ._mlenatleast128
+
+# qhasm: mlenlowbelow128:
+._mlenlowbelow128:
+
+# qhasm: unsigned>? mlenhigh - 0
+# asm 1: cmp <mlenhigh=int32#4,#0
+# asm 2: cmp <mlenhigh=r3,#0
+cmp r3,#0
+
+# qhasm: goto mlenatleast128 if unsigned>
+bhi ._mlenatleast128
+
+# qhasm: =? mlenlow - 0
+# asm 1: cmp <mlenlow=int32#3,#0
+# asm 2: cmp <mlenlow=r2,#0
+cmp r2,#0
+
+# qhasm: goto done if =
+beq ._done
+
+# qhasm: mlenatleast1:
+._mlenatleast1:
+
+# qhasm: unsigned<? mlenlow - 64
+# asm 1: cmp <mlenlow=int32#3,#64
+# asm 2: cmp <mlenlow=r2,#64
+cmp r2,#64
+
+# qhasm: goto mlenatleast64 if !unsigned<
+bhs ._mlenatleast64
+
+# qhasm: savec = c
+# asm 1: str <c=int32#1,>savec=stack32#1
+# asm 2: str <c=r0,>savec=[sp,#64]
+str r0,[sp,#64]
+
+# qhasm: c = &tmp
+# asm 1: lea >c=int32#1,<tmp=stack512#1
+# asm 2: lea >c=r0,<tmp=[sp,#0]
+add r0,sp,#0
+
+# qhasm: =? m - 0
+# asm 1: cmp <m=int32#2,#0
+# asm 2: cmp <m=r1,#0
+cmp r1,#0
+
+# qhasm: goto mlenatleast64 if =
+beq ._mlenatleast64
+
+# qhasm: i = 0
+# asm 1: ldr >i=int32#4,=0
+# asm 2: ldr >i=r3,=0
+ldr r3,=0
+
+# qhasm: mcopy:
+._mcopy:
+
+# qhasm: mi = mem8[m + 0]
+# asm 1: ldrb >mi=int32#5,[<m=int32#2,#0]
+# asm 2: ldrb >mi=r4,[<m=r1,#0]
+ldrb r4,[r1,#0]
+
+# qhasm: mem8[c + 0] = mi
+# asm 1: strb <mi=int32#5,[<c=int32#1,#0]
+# asm 2: strb <mi=r4,[<c=r0,#0]
+strb r4,[r0,#0]
+
+# qhasm: m += 1
+# asm 1: add <m=int32#2,<m=int32#2,#1
+# asm 2: add <m=r1,<m=r1,#1
+add r1,r1,#1
+
+# qhasm: c += 1
+# asm 1: add <c=int32#1,<c=int32#1,#1
+# asm 2: add <c=r0,<c=r0,#1
+add r0,r0,#1
+
+# qhasm: i += 1
+# asm 1: add <i=int32#4,<i=int32#4,#1
+# asm 2: add <i=r3,<i=r3,#1
+add r3,r3,#1
+
+# qhasm: unsigned<? i - mlenlow
+# asm 1: cmp <i=int32#4,<mlenlow=int32#3
+# asm 2: cmp <i=r3,<mlenlow=r2
+cmp r3,r2
+
+# qhasm: goto mcopy if unsigned<
+blo ._mcopy
+
+# qhasm: mi = 0
+# asm 1: ldr >mi=int32#2,=0
+# asm 2: ldr >mi=r1,=0
+ldr r1,=0
+
+# qhasm: pad:
+._pad:
+
+# qhasm: mem8[c + 0] = mi
+# asm 1: strb <mi=int32#2,[<c=int32#1,#0]
+# asm 2: strb <mi=r1,[<c=r0,#0]
+strb r1,[r0,#0]
+
+# qhasm: c += 1
+# asm 1: add <c=int32#1,<c=int32#1,#1
+# asm 2: add <c=r0,<c=r0,#1
+add r0,r0,#1
+
+# qhasm: i += 1
+# asm 1: add <i=int32#4,<i=int32#4,#1
+# asm 2: add <i=r3,<i=r3,#1
+add r3,r3,#1
+
+# qhasm: unsigned<? i - 64
+# asm 1: cmp <i=int32#4,#64
+# asm 2: cmp <i=r3,#64
+cmp r3,#64
+
+# qhasm: goto pad if unsigned<
+blo ._pad
+
+# qhasm: c -= 64
+# asm 1: sub <c=int32#1,<c=int32#1,#64
+# asm 2: sub <c=r0,<c=r0,#64
+sub r0,r0,#64
+
+# qhasm: m = &tmp
+# asm 1: lea >m=int32#2,<tmp=stack512#1
+# asm 2: lea >m=r1,<tmp=[sp,#0]
+add r1,sp,#0
+
+# qhasm: mlenatleast64:
+._mlenatleast64:
+
+# qhasm: new k2k3k6k7
+
+# qhasm: k2k3k6k7 bot = k2k3k6k7_stack bot
+# asm 1: vldr <k2k3k6k7=reg128#2%bot,<k2k3k6k7_stack=stack128#5
+# asm 2: vldr <k2k3k6k7=d2,<k2k3k6k7_stack=[sp,#160]
+vldr d2,[sp,#160]
+
+# qhasm: k2k3k6k7 top = k2k3k6k7_stack top
+# asm 1: vldr <k2k3k6k7=reg128#2%top,<k2k3k6k7_stack=stack128#5
+# asm 2: vldr <k2k3k6k7=d3,<k2k3k6k7_stack=[sp,#168]
+vldr d3,[sp,#168]
+
+# qhasm: new k1n1k7k2
+
+# qhasm: k1n1k7k2 bot = k1n1k7k2_stack bot
+# asm 1: vldr <k1n1k7k2=reg128#5%bot,<k1n1k7k2_stack=stack128#6
+# asm 2: vldr <k1n1k7k2=d8,<k1n1k7k2_stack=[sp,#176]
+vldr d8,[sp,#176]
+
+# qhasm: k1n1k7k2 top = k1n1k7k2_stack top
+# asm 1: vldr <k1n1k7k2=reg128#5%top,<k1n1k7k2_stack=stack128#6
+# asm 2: vldr <k1n1k7k2=d9,<k1n1k7k2_stack=[sp,#184]
+vldr d9,[sp,#184]
+
+# qhasm: n2n3n3n2 = n2n3n3n2[0,1] n2n3n3n2[1] n2n3n3n2[0]
+# asm 1: vext.32 <n2n3n3n2=reg128#1%top,<n2n3n3n2=reg128#1%bot,<n2n3n3n2=reg128#1%bot,#1
+# asm 2: vext.32 <n2n3n3n2=d1,<n2n3n3n2=d0,<n2n3n3n2=d0,#1
+vext.32 d1,d0,d0,#1
+
+# qhasm: new start2
+
+# qhasm: start2 = start2[0,1] k1n1k7k2[0,1]
+# asm 1: vmov <start2=reg128#6%top,<k1n1k7k2=reg128#5%bot
+# asm 2: vmov <start2=d11,<k1n1k7k2=d8
+vmov d11,d8
+
+# qhasm: start2 = n2n3n3n2[3] k2k3k6k7[2] start2[2,3]
+# asm 1: vext.32 <start2=reg128#6%bot,<n2n3n3n2=reg128#1%top,<k2k3k6k7=reg128#2%top,#1
+# asm 2: vext.32 <start2=d10,<n2n3n3n2=d1,<k2k3k6k7=d3,#1
+vext.32 d10,d1,d3,#1
+
+# qhasm: new start3
+
+# qhasm: start3 = start3[0,1] k1n1k7k2[2,3]
+# asm 1: vmov <start3=reg128#7%top,<k1n1k7k2=reg128#5%top
+# asm 2: vmov <start3=d13,<k1n1k7k2=d9
+vmov d13,d9
+
+# qhasm: start3 = k2k3k6k7[1] n2n3n3n2[2] start3[2,3]
+# asm 1: vext.32 <start3=reg128#7%bot,<k2k3k6k7=reg128#2%bot,<n2n3n3n2=reg128#1%top,#1
+# asm 2: vext.32 <start3=d12,<k2k3k6k7=d2,<n2n3n3n2=d1,#1
+vext.32 d12,d2,d1,#1
+
+# qhasm: diag0 = start0
+# asm 1: vmov >diag0=reg128#2,<start0=reg128#3
+# asm 2: vmov >diag0=q1,<start0=q2
+vmov q1,q2
+
+# qhasm: diag1 = start1
+# asm 1: vmov >diag1=reg128#5,<start1=reg128#4
+# asm 2: vmov >diag1=q4,<start1=q3
+vmov q4,q3
+
+# qhasm: diag2 = start2
+# asm 1: vmov >diag2=reg128#8,<start2=reg128#6
+# asm 2: vmov >diag2=q7,<start2=q5
+vmov q7,q5
+
+# qhasm: diag3 = start3
+# asm 1: vmov >diag3=reg128#9,<start3=reg128#7
+# asm 2: vmov >diag3=q8,<start3=q6
+vmov q8,q6
+
+# qhasm: 2x nextblock = 0xff
+# asm 1: vmov.i64 >nextblock=reg128#10,#0xff
+# asm 2: vmov.i64 >nextblock=q9,#0xff
+vmov.i64 q9,#0xff
+
+# qhasm: 4x nextblock unsigned>>= 7
+# asm 1: vshr.u32 >nextblock=reg128#10,<nextblock=reg128#10,#7
+# asm 2: vshr.u32 >nextblock=q9,<nextblock=q9,#7
+vshr.u32 q9,q9,#7
+
+# qhasm: 2x n2n3n3n2 += nextblock
+# asm 1: vadd.i64 >n2n3n3n2=reg128#1,<n2n3n3n2=reg128#1,<nextblock=reg128#10
+# asm 2: vadd.i64 >n2n3n3n2=q0,<n2n3n3n2=q0,<nextblock=q9
+vadd.i64 q0,q0,q9
+
+# qhasm: i = 12
+# asm 1: ldr >i=int32#4,=12
+# asm 2: ldr >i=r3,=12
+ldr r3,=12
+
+# qhasm: mainloop1:
+._mainloop1:
+
+# qhasm: 4x a0 = diag1 + diag0
+# asm 1: vadd.i32 >a0=reg128#10,<diag1=reg128#5,<diag0=reg128#2
+# asm 2: vadd.i32 >a0=q9,<diag1=q4,<diag0=q1
+vadd.i32 q9,q4,q1
+
+# qhasm: 4x b0 = a0 << 7
+# asm 1: vshl.i32 >b0=reg128#11,<a0=reg128#10,#7
+# asm 2: vshl.i32 >b0=q10,<a0=q9,#7
+vshl.i32 q10,q9,#7
+
+# qhasm: 4x b0 insert= a0 >> 25
+# asm 1: vsri.i32 <b0=reg128#11,<a0=reg128#10,#25
+# asm 2: vsri.i32 <b0=q10,<a0=q9,#25
+vsri.i32 q10,q9,#25
+
+# qhasm: diag3 ^= b0
+# asm 1: veor >diag3=reg128#9,<diag3=reg128#9,<b0=reg128#11
+# asm 2: veor >diag3=q8,<diag3=q8,<b0=q10
+veor q8,q8,q10
+
+# qhasm: 4x a1 = diag0 + diag3
+# asm 1: vadd.i32 >a1=reg128#10,<diag0=reg128#2,<diag3=reg128#9
+# asm 2: vadd.i32 >a1=q9,<diag0=q1,<diag3=q8
+vadd.i32 q9,q1,q8
+
+# qhasm: 4x b1 = a1 << 9
+# asm 1: vshl.i32 >b1=reg128#11,<a1=reg128#10,#9
+# asm 2: vshl.i32 >b1=q10,<a1=q9,#9
+vshl.i32 q10,q9,#9
+
+# qhasm: 4x b1 insert= a1 >> 23
+# asm 1: vsri.i32 <b1=reg128#11,<a1=reg128#10,#23
+# asm 2: vsri.i32 <b1=q10,<a1=q9,#23
+vsri.i32 q10,q9,#23
+
+# qhasm: diag2 ^= b1
+# asm 1: veor >diag2=reg128#8,<diag2=reg128#8,<b1=reg128#11
+# asm 2: veor >diag2=q7,<diag2=q7,<b1=q10
+veor q7,q7,q10
+
+# qhasm: 4x a2 = diag3 + diag2
+# asm 1: vadd.i32 >a2=reg128#10,<diag3=reg128#9,<diag2=reg128#8
+# asm 2: vadd.i32 >a2=q9,<diag3=q8,<diag2=q7
+vadd.i32 q9,q8,q7
+
+# qhasm: diag3 = diag3[3] diag3[0,1,2]
+# asm 1: vext.32 >diag3=reg128#9,<diag3=reg128#9,<diag3=reg128#9,#3
+# asm 2: vext.32 >diag3=q8,<diag3=q8,<diag3=q8,#3
+vext.32 q8,q8,q8,#3
+
+# qhasm: 4x b2 = a2 << 13
+# asm 1: vshl.i32 >b2=reg128#11,<a2=reg128#10,#13
+# asm 2: vshl.i32 >b2=q10,<a2=q9,#13
+vshl.i32 q10,q9,#13
+
+# qhasm: 4x b2 insert= a2 >> 19
+# asm 1: vsri.i32 <b2=reg128#11,<a2=reg128#10,#19
+# asm 2: vsri.i32 <b2=q10,<a2=q9,#19
+vsri.i32 q10,q9,#19
+
+# qhasm: diag1 ^= b2
+# asm 1: veor >diag1=reg128#5,<diag1=reg128#5,<b2=reg128#11
+# asm 2: veor >diag1=q4,<diag1=q4,<b2=q10
+veor q4,q4,q10
+
+# qhasm: 4x a3 = diag2 + diag1
+# asm 1: vadd.i32 >a3=reg128#10,<diag2=reg128#8,<diag1=reg128#5
+# asm 2: vadd.i32 >a3=q9,<diag2=q7,<diag1=q4
+vadd.i32 q9,q7,q4
+
+# qhasm: diag2 = diag2[2,3] diag2[0,1]
+# asm 1: vswp <diag2=reg128#8%bot,<diag2=reg128#8%top
+# asm 2: vswp <diag2=d14,<diag2=d15
+vswp d14,d15
+
+# qhasm: 4x b3 = a3 << 18
+# asm 1: vshl.i32 >b3=reg128#11,<a3=reg128#10,#18
+# asm 2: vshl.i32 >b3=q10,<a3=q9,#18
+vshl.i32 q10,q9,#18
+
+# qhasm: 4x b3 insert= a3 >> 14
+# asm 1: vsri.i32 <b3=reg128#11,<a3=reg128#10,#14
+# asm 2: vsri.i32 <b3=q10,<a3=q9,#14
+vsri.i32 q10,q9,#14
+
+# qhasm: diag1 = diag1[1,2,3] diag1[0]
+# asm 1: vext.32 >diag1=reg128#5,<diag1=reg128#5,<diag1=reg128#5,#1
+# asm 2: vext.32 >diag1=q4,<diag1=q4,<diag1=q4,#1
+vext.32 q4,q4,q4,#1
+
+# qhasm: diag0 ^= b3
+# asm 1: veor >diag0=reg128#2,<diag0=reg128#2,<b3=reg128#11
+# asm 2: veor >diag0=q1,<diag0=q1,<b3=q10
+veor q1,q1,q10
+
+# qhasm: 4x a0 = diag3 + diag0
+# asm 1: vadd.i32 >a0=reg128#10,<diag3=reg128#9,<diag0=reg128#2
+# asm 2: vadd.i32 >a0=q9,<diag3=q8,<diag0=q1
+vadd.i32 q9,q8,q1
+
+# qhasm: 4x b0 = a0 << 7
+# asm 1: vshl.i32 >b0=reg128#11,<a0=reg128#10,#7
+# asm 2: vshl.i32 >b0=q10,<a0=q9,#7
+vshl.i32 q10,q9,#7
+
+# qhasm: 4x b0 insert= a0 >> 25
+# asm 1: vsri.i32 <b0=reg128#11,<a0=reg128#10,#25
+# asm 2: vsri.i32 <b0=q10,<a0=q9,#25
+vsri.i32 q10,q9,#25
+
+# qhasm: diag1 ^= b0
+# asm 1: veor >diag1=reg128#5,<diag1=reg128#5,<b0=reg128#11
+# asm 2: veor >diag1=q4,<diag1=q4,<b0=q10
+veor q4,q4,q10
+
+# qhasm: 4x a1 = diag0 + diag1
+# asm 1: vadd.i32 >a1=reg128#10,<diag0=reg128#2,<diag1=reg128#5
+# asm 2: vadd.i32 >a1=q9,<diag0=q1,<diag1=q4
+vadd.i32 q9,q1,q4
+
+# qhasm: 4x b1 = a1 << 9
+# asm 1: vshl.i32 >b1=reg128#11,<a1=reg128#10,#9
+# asm 2: vshl.i32 >b1=q10,<a1=q9,#9
+vshl.i32 q10,q9,#9
+
+# qhasm: 4x b1 insert= a1 >> 23
+# asm 1: vsri.i32 <b1=reg128#11,<a1=reg128#10,#23
+# asm 2: vsri.i32 <b1=q10,<a1=q9,#23
+vsri.i32 q10,q9,#23
+
+# qhasm: unsigned>? i -= 2
+# asm 1: subs <i=int32#4,<i=int32#4,#2
+# asm 2: subs <i=r3,<i=r3,#2
+subs r3,r3,#2
+
+# qhasm: diag2 ^= b1
+# asm 1: veor >diag2=reg128#8,<diag2=reg128#8,<b1=reg128#11
+# asm 2: veor >diag2=q7,<diag2=q7,<b1=q10
+veor q7,q7,q10
+
+# qhasm: 4x a2 = diag1 + diag2
+# asm 1: vadd.i32 >a2=reg128#10,<diag1=reg128#5,<diag2=reg128#8
+# asm 2: vadd.i32 >a2=q9,<diag1=q4,<diag2=q7
+vadd.i32 q9,q4,q7
+
+# qhasm: diag1 = diag1[3] diag1[0,1,2]
+# asm 1: vext.32 >diag1=reg128#5,<diag1=reg128#5,<diag1=reg128#5,#3
+# asm 2: vext.32 >diag1=q4,<diag1=q4,<diag1=q4,#3
+vext.32 q4,q4,q4,#3
+
+# qhasm: 4x b2 = a2 << 13
+# asm 1: vshl.i32 >b2=reg128#11,<a2=reg128#10,#13
+# asm 2: vshl.i32 >b2=q10,<a2=q9,#13
+vshl.i32 q10,q9,#13
+
+# qhasm: 4x b2 insert= a2 >> 19
+# asm 1: vsri.i32 <b2=reg128#11,<a2=reg128#10,#19
+# asm 2: vsri.i32 <b2=q10,<a2=q9,#19
+vsri.i32 q10,q9,#19
+
+# qhasm: diag3 ^= b2
+# asm 1: veor >diag3=reg128#9,<diag3=reg128#9,<b2=reg128#11
+# asm 2: veor >diag3=q8,<diag3=q8,<b2=q10
+veor q8,q8,q10
+
+# qhasm: 4x a3 = diag2 + diag3
+# asm 1: vadd.i32 >a3=reg128#10,<diag2=reg128#8,<diag3=reg128#9
+# asm 2: vadd.i32 >a3=q9,<diag2=q7,<diag3=q8
+vadd.i32 q9,q7,q8
+
+# qhasm: diag2 = diag2[2,3] diag2[0,1]
+# asm 1: vswp <diag2=reg128#8%bot,<diag2=reg128#8%top
+# asm 2: vswp <diag2=d14,<diag2=d15
+vswp d14,d15
+
+# qhasm: 4x b3 = a3 << 18
+# asm 1: vshl.i32 >b3=reg128#11,<a3=reg128#10,#18
+# asm 2: vshl.i32 >b3=q10,<a3=q9,#18
+vshl.i32 q10,q9,#18
+
+# qhasm: 4x b3 insert= a3 >> 14
+# asm 1: vsri.i32 <b3=reg128#11,<a3=reg128#10,#14
+# asm 2: vsri.i32 <b3=q10,<a3=q9,#14
+vsri.i32 q10,q9,#14
+
+# qhasm: diag3 = diag3[1,2,3] diag3[0]
+# asm 1: vext.32 >diag3=reg128#9,<diag3=reg128#9,<diag3=reg128#9,#1
+# asm 2: vext.32 >diag3=q8,<diag3=q8,<diag3=q8,#1
+vext.32 q8,q8,q8,#1
+
+# qhasm: diag0 ^= b3
+# asm 1: veor >diag0=reg128#2,<diag0=reg128#2,<b3=reg128#11
+# asm 2: veor >diag0=q1,<diag0=q1,<b3=q10
+veor q1,q1,q10
+
+# qhasm: goto mainloop1 if unsigned>
+bhi ._mainloop1
+
+# qhasm: 2x abab = 0xffffffff
+# asm 1: vmov.i64 >abab=reg128#10,#0xffffffff
+# asm 2: vmov.i64 >abab=q9,#0xffffffff
+vmov.i64 q9,#0xffffffff
+
+# qhasm: 4x x0x5x10x15 = diag0 + start0
+# asm 1: vadd.i32 >x0x5x10x15=reg128#2,<diag0=reg128#2,<start0=reg128#3
+# asm 2: vadd.i32 >x0x5x10x15=q1,<diag0=q1,<start0=q2
+vadd.i32 q1,q1,q2
+
+# qhasm: 4x x12x1x6x11 = diag1 + start1
+# asm 1: vadd.i32 >x12x1x6x11=reg128#5,<diag1=reg128#5,<start1=reg128#4
+# asm 2: vadd.i32 >x12x1x6x11=q4,<diag1=q4,<start1=q3
+vadd.i32 q4,q4,q3
+
+# qhasm: 4x x8x13x2x7 = diag2 + start2
+# asm 1: vadd.i32 >x8x13x2x7=reg128#6,<diag2=reg128#8,<start2=reg128#6
+# asm 2: vadd.i32 >x8x13x2x7=q5,<diag2=q7,<start2=q5
+vadd.i32 q5,q7,q5
+
+# qhasm: 4x x4x9x14x3 = diag3 + start3
+# asm 1: vadd.i32 >x4x9x14x3=reg128#7,<diag3=reg128#9,<start3=reg128#7
+# asm 2: vadd.i32 >x4x9x14x3=q6,<diag3=q8,<start3=q6
+vadd.i32 q6,q8,q6
+
+# qhasm: x0x1x10x11 = x0x5x10x15
+# asm 1: vmov >x0x1x10x11=reg128#8,<x0x5x10x15=reg128#2
+# asm 2: vmov >x0x1x10x11=q7,<x0x5x10x15=q1
+vmov q7,q1
+
+# qhasm: x12x13x6x7 = x12x1x6x11
+# asm 1: vmov >x12x13x6x7=reg128#9,<x12x1x6x11=reg128#5
+# asm 2: vmov >x12x13x6x7=q8,<x12x1x6x11=q4
+vmov q8,q4
+
+# qhasm: x8x9x2x3 = x8x13x2x7
+# asm 1: vmov >x8x9x2x3=reg128#11,<x8x13x2x7=reg128#6
+# asm 2: vmov >x8x9x2x3=q10,<x8x13x2x7=q5
+vmov q10,q5
+
+# qhasm: x4x5x14x15 = x4x9x14x3
+# asm 1: vmov >x4x5x14x15=reg128#12,<x4x9x14x3=reg128#7
+# asm 2: vmov >x4x5x14x15=q11,<x4x9x14x3=q6
+vmov q11,q6
+
+# qhasm: x0x1x10x11 = (abab & x0x1x10x11) | (~abab & x12x1x6x11)
+# asm 1: vbif <x0x1x10x11=reg128#8,<x12x1x6x11=reg128#5,<abab=reg128#10
+# asm 2: vbif <x0x1x10x11=q7,<x12x1x6x11=q4,<abab=q9
+vbif q7,q4,q9
+
+# qhasm: x12x13x6x7 = (abab & x12x13x6x7) | (~abab & x8x13x2x7)
+# asm 1: vbif <x12x13x6x7=reg128#9,<x8x13x2x7=reg128#6,<abab=reg128#10
+# asm 2: vbif <x12x13x6x7=q8,<x8x13x2x7=q5,<abab=q9
+vbif q8,q5,q9
+
+# qhasm: x8x9x2x3 = (abab & x8x9x2x3) | (~abab & x4x9x14x3)
+# asm 1: vbif <x8x9x2x3=reg128#11,<x4x9x14x3=reg128#7,<abab=reg128#10
+# asm 2: vbif <x8x9x2x3=q10,<x4x9x14x3=q6,<abab=q9
+vbif q10,q6,q9
+
+# qhasm: x4x5x14x15 = (abab & x4x5x14x15) | (~abab & x0x5x10x15)
+# asm 1: vbif <x4x5x14x15=reg128#12,<x0x5x10x15=reg128#2,<abab=reg128#10
+# asm 2: vbif <x4x5x14x15=q11,<x0x5x10x15=q1,<abab=q9
+vbif q11,q1,q9
+
+# qhasm: x0x1x2x3 = x0x1x10x11
+# asm 1: vmov >x0x1x2x3=reg128#2,<x0x1x10x11=reg128#8
+# asm 2: vmov >x0x1x2x3=q1,<x0x1x10x11=q7
+vmov q1,q7
+
+# qhasm: x4x5x6x7 = x4x5x14x15
+# asm 1: vmov >x4x5x6x7=reg128#5,<x4x5x14x15=reg128#12
+# asm 2: vmov >x4x5x6x7=q4,<x4x5x14x15=q11
+vmov q4,q11
+
+# qhasm: x8x9x10x11 = x8x9x2x3
+# asm 1: vmov >x8x9x10x11=reg128#6,<x8x9x2x3=reg128#11
+# asm 2: vmov >x8x9x10x11=q5,<x8x9x2x3=q10
+vmov q5,q10
+
+# qhasm: x12x13x14x15 = x12x13x6x7
+# asm 1: vmov >x12x13x14x15=reg128#7,<x12x13x6x7=reg128#9
+# asm 2: vmov >x12x13x14x15=q6,<x12x13x6x7=q8
+vmov q6,q8
+
+# qhasm: x0x1x2x3 = x0x1x2x3[0,1] x8x9x2x3[2,3]
+# asm 1: vmov <x0x1x2x3=reg128#2%top,<x8x9x2x3=reg128#11%top
+# asm 2: vmov <x0x1x2x3=d3,<x8x9x2x3=d21
+vmov d3,d21
+
+# qhasm: x4x5x6x7 = x4x5x6x7[0,1] x12x13x6x7[2,3]
+# asm 1: vmov <x4x5x6x7=reg128#5%top,<x12x13x6x7=reg128#9%top
+# asm 2: vmov <x4x5x6x7=d9,<x12x13x6x7=d17
+vmov d9,d17
+
+# qhasm: x8x9x10x11 = x8x9x10x11[0,1] x0x1x10x11[2,3]
+# asm 1: vmov <x8x9x10x11=reg128#6%top,<x0x1x10x11=reg128#8%top
+# asm 2: vmov <x8x9x10x11=d11,<x0x1x10x11=d15
+vmov d11,d15
+
+# qhasm: x12x13x14x15 = x12x13x14x15[0,1] x4x5x14x15[2,3]
+# asm 1: vmov <x12x13x14x15=reg128#7%top,<x4x5x14x15=reg128#12%top
+# asm 2: vmov <x12x13x14x15=d13,<x4x5x14x15=d23
+vmov d13,d23
+
+# qhasm: =? m - 0
+# asm 1: cmp <m=int32#2,#0
+# asm 2: cmp <m=r1,#0
+cmp r1,#0
+
+# qhasm: goto nomessage1 if =
+beq ._nomessage1
+
+# qhasm: m0m1m2m3 = mem128[m]
+# asm 1: vld1.8 {>m0m1m2m3=reg128#8%bot->m0m1m2m3=reg128#8%top},[<m=int32#2]
+# asm 2: vld1.8 {>m0m1m2m3=d14->m0m1m2m3=d15},[<m=r1]
+vld1.8 {d14-d15},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m4m5m6m7 = mem128[m]
+# asm 1: vld1.8 {>m4m5m6m7=reg128#9%bot->m4m5m6m7=reg128#9%top},[<m=int32#2]
+# asm 2: vld1.8 {>m4m5m6m7=d16->m4m5m6m7=d17},[<m=r1]
+vld1.8 {d16-d17},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m8m9m10m11 = mem128[m]
+# asm 1: vld1.8 {>m8m9m10m11=reg128#10%bot->m8m9m10m11=reg128#10%top},[<m=int32#2]
+# asm 2: vld1.8 {>m8m9m10m11=d18->m8m9m10m11=d19},[<m=r1]
+vld1.8 {d18-d19},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: m12m13m14m15 = mem128[m]
+# asm 1: vld1.8 {>m12m13m14m15=reg128#11%bot->m12m13m14m15=reg128#11%top},[<m=int32#2]
+# asm 2: vld1.8 {>m12m13m14m15=d20->m12m13m14m15=d21},[<m=r1]
+vld1.8 {d20-d21},[r1]
+
+# qhasm: m += 16
+# asm 1: add <m=int32#2,<m=int32#2,#16
+# asm 2: add <m=r1,<m=r1,#16
+add r1,r1,#16
+
+# qhasm: x0x1x2x3 ^= m0m1m2m3
+# asm 1: veor >x0x1x2x3=reg128#2,<x0x1x2x3=reg128#2,<m0m1m2m3=reg128#8
+# asm 2: veor >x0x1x2x3=q1,<x0x1x2x3=q1,<m0m1m2m3=q7
+veor q1,q1,q7
+
+# qhasm: x4x5x6x7 ^= m4m5m6m7
+# asm 1: veor >x4x5x6x7=reg128#5,<x4x5x6x7=reg128#5,<m4m5m6m7=reg128#9
+# asm 2: veor >x4x5x6x7=q4,<x4x5x6x7=q4,<m4m5m6m7=q8
+veor q4,q4,q8
+
+# qhasm: x8x9x10x11 ^= m8m9m10m11
+# asm 1: veor >x8x9x10x11=reg128#6,<x8x9x10x11=reg128#6,<m8m9m10m11=reg128#10
+# asm 2: veor >x8x9x10x11=q5,<x8x9x10x11=q5,<m8m9m10m11=q9
+veor q5,q5,q9
+
+# qhasm: x12x13x14x15 ^= m12m13m14m15
+# asm 1: veor >x12x13x14x15=reg128#7,<x12x13x14x15=reg128#7,<m12m13m14m15=reg128#11
+# asm 2: veor >x12x13x14x15=q6,<x12x13x14x15=q6,<m12m13m14m15=q10
+veor q6,q6,q10
+
+# qhasm: nomessage1:
+._nomessage1:
+
+# qhasm: mem128[c] = x0x1x2x3
+# asm 1: vst1.8 {<x0x1x2x3=reg128#2%bot-<x0x1x2x3=reg128#2%top},[<c=int32#1]
+# asm 2: vst1.8 {<x0x1x2x3=d2-<x0x1x2x3=d3},[<c=r0]
+vst1.8 {d2-d3},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x4x5x6x7
+# asm 1: vst1.8 {<x4x5x6x7=reg128#5%bot-<x4x5x6x7=reg128#5%top},[<c=int32#1]
+# asm 2: vst1.8 {<x4x5x6x7=d8-<x4x5x6x7=d9},[<c=r0]
+vst1.8 {d8-d9},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x8x9x10x11
+# asm 1: vst1.8 {<x8x9x10x11=reg128#6%bot-<x8x9x10x11=reg128#6%top},[<c=int32#1]
+# asm 2: vst1.8 {<x8x9x10x11=d10-<x8x9x10x11=d11},[<c=r0]
+vst1.8 {d10-d11},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: mem128[c] = x12x13x14x15
+# asm 1: vst1.8 {<x12x13x14x15=reg128#7%bot-<x12x13x14x15=reg128#7%top},[<c=int32#1]
+# asm 2: vst1.8 {<x12x13x14x15=d12-<x12x13x14x15=d13},[<c=r0]
+vst1.8 {d12-d13},[r0]
+
+# qhasm: c += 16
+# asm 1: add <c=int32#1,<c=int32#1,#16
+# asm 2: add <c=r0,<c=r0,#16
+add r0,r0,#16
+
+# qhasm: unsigned<? mlenlow - 64
+# asm 1: cmp <mlenlow=int32#3,#64
+# asm 2: cmp <mlenlow=r2,#64
+cmp r2,#64
+
+# qhasm: goto xmlenatleast64 if !unsigned<
+bhs ._xmlenatleast64
+
+# qhasm: i = 0
+# asm 1: ldr >i=int32#4,=0
+# asm 2: ldr >i=r3,=0
+ldr r3,=0
+
+# qhasm: m = c - 64
+# asm 1: sub >m=int32#2,<c=int32#1,#64
+# asm 2: sub >m=r1,<c=r0,#64
+sub r1,r0,#64
+
+# qhasm: c = savec
+# asm 1: ldr >c=int32#1,<savec=stack32#1
+# asm 2: ldr >c=r0,<savec=[sp,#64]
+ldr r0,[sp,#64]
+
+# qhasm: ccopy:
+._ccopy:
+
+# qhasm: ci = mem8[m + 0]
+# asm 1: ldrb >ci=int32#5,[<m=int32#2,#0]
+# asm 2: ldrb >ci=r4,[<m=r1,#0]
+ldrb r4,[r1,#0]
+
+# qhasm: mem8[c + 0] = ci
+# asm 1: strb <ci=int32#5,[<c=int32#1,#0]
+# asm 2: strb <ci=r4,[<c=r0,#0]
+strb r4,[r0,#0]
+
+# qhasm: m += 1
+# asm 1: add <m=int32#2,<m=int32#2,#1
+# asm 2: add <m=r1,<m=r1,#1
+add r1,r1,#1
+
+# qhasm: c += 1
+# asm 1: add <c=int32#1,<c=int32#1,#1
+# asm 2: add <c=r0,<c=r0,#1
+add r0,r0,#1
+
+# qhasm: i += 1
+# asm 1: add <i=int32#4,<i=int32#4,#1
+# asm 2: add <i=r3,<i=r3,#1
+add r3,r3,#1
+
+# qhasm: unsigned<? i - mlenlow
+# asm 1: cmp <i=int32#4,<mlenlow=int32#3
+# asm 2: cmp <i=r3,<mlenlow=r2
+cmp r3,r2
+
+# qhasm: goto ccopy if unsigned<
+blo ._ccopy
+
+# qhasm: xmlenatleast64:
+._xmlenatleast64:
+
+# qhasm: unsigned>? mlenlow -= 64
+# asm 1: subs <mlenlow=int32#3,<mlenlow=int32#3,#64
+# asm 2: subs <mlenlow=r2,<mlenlow=r2,#64
+subs r2,r2,#64
+
+# qhasm: goto mlenatleast1 if unsigned>
+bhi ._mlenatleast1
+
+# qhasm: done:
+._done:
+
+# qhasm: new caller_r4
+
+# qhasm: caller_r4 = stack_r4
+# asm 1: ldr >caller_r4=int32#5,<stack_r4=stack32#2
+# asm 2: ldr >caller_r4=r4,<stack_r4=[sp,#68]
+ldr r4,[sp,#68]
+
+# qhasm: new caller_q4
+
+# qhasm: new caller_q5
+
+# qhasm: new caller_q6
+
+# qhasm: new caller_q7
+
+# qhasm: caller_q4 bot = stack_q4 bot
+# asm 1: vldr <caller_q4=reg128#5%bot,<stack_q4=stack128#1
+# asm 2: vldr <caller_q4=d8,<stack_q4=[sp,#96]
+vldr d8,[sp,#96]
+
+# qhasm: caller_q4 top = stack_q4 top
+# asm 1: vldr <caller_q4=reg128#5%top,<stack_q4=stack128#1
+# asm 2: vldr <caller_q4=d9,<stack_q4=[sp,#104]
+vldr d9,[sp,#104]
+
+# qhasm: caller_q5 bot = stack_q5 bot
+# asm 1: vldr <caller_q5=reg128#6%bot,<stack_q5=stack128#2
+# asm 2: vldr <caller_q5=d10,<stack_q5=[sp,#112]
+vldr d10,[sp,#112]
+
+# qhasm: caller_q5 top = stack_q5 top
+# asm 1: vldr <caller_q5=reg128#6%top,<stack_q5=stack128#2
+# asm 2: vldr <caller_q5=d11,<stack_q5=[sp,#120]
+vldr d11,[sp,#120]
+
+# qhasm: caller_q6 bot = stack_q6 bot
+# asm 1: vldr <caller_q6=reg128#7%bot,<stack_q6=stack128#3
+# asm 2: vldr <caller_q6=d12,<stack_q6=[sp,#128]
+vldr d12,[sp,#128]
+
+# qhasm: caller_q6 top = stack_q6 top
+# asm 1: vldr <caller_q6=reg128#7%top,<stack_q6=stack128#3
+# asm 2: vldr <caller_q6=d13,<stack_q6=[sp,#136]
+vldr d13,[sp,#136]
+
+# qhasm: caller_q7 bot = stack_q7 bot
+# asm 1: vldr <caller_q7=reg128#8%bot,<stack_q7=stack128#4
+# asm 2: vldr <caller_q7=d14,<stack_q7=[sp,#144]
+vldr d14,[sp,#144]
+
+# qhasm: caller_q7 top = stack_q7 top
+# asm 1: vldr <caller_q7=reg128#8%top,<stack_q7=stack128#4
+# asm 2: vldr <caller_q7=d15,<stack_q7=[sp,#152]
+vldr d15,[sp,#152]
+
+# qhasm: int32 result
+
+# qhasm: result = 0
+# asm 1: ldr >result=int32#1,=0
+# asm 2: ldr >result=r0,=0
+ldr r0,=0
+
+# qhasm: return result
+add sp,sp,#256
+bx lr
diff --git a/ext/bin/tap-windows-ndis5/x64/WdfCoinstaller01011.dll b/ext/bin/tap-windows-ndis5/x64/WdfCoinstaller01011.dll
deleted file mode 100644
index d49d2913..00000000
--- a/ext/bin/tap-windows-ndis5/x64/WdfCoinstaller01011.dll
+++ /dev/null
Binary files differ
diff --git a/ext/bin/tap-windows-ndis5/x64/zttap200.cat b/ext/bin/tap-windows-ndis5/x64/zttap200.cat
deleted file mode 100644
index a3769e40..00000000
--- a/ext/bin/tap-windows-ndis5/x64/zttap200.cat
+++ /dev/null
Binary files differ
diff --git a/ext/bin/tap-windows-ndis5/x64/zttap200.inf b/ext/bin/tap-windows-ndis5/x64/zttap200.inf
deleted file mode 100644
index dc1a7422..00000000
--- a/ext/bin/tap-windows-ndis5/x64/zttap200.inf
+++ /dev/null
@@ -1,79 +0,0 @@
-[Version]
-Signature="$WINDOWS NT$"
-Class=Net
-ClassGuid={4d36e972-e325-11ce-bfc1-08002be10318}
-Provider=%Provider%
-CatalogFile=zttap200.cat
-DriverVer=01/23/2014,15.19.17.816
-
-[Strings]
-DeviceDescription = "ZeroTier One Virtual Network Port"
-Provider = "ZeroTier Networks LLC"
-
-; To build for x86, take NTamd64 off this and off the named section manually, build, then put it back!
-[Manufacturer]
-%Provider%=zttap200,NTamd64
-
-[zttap200]
-%DeviceDescription%=zttap200.ndi,zttap200
-
-[ztTap200.NTamd64]
-%DeviceDescription%=zttap200.ndi,zttap200
-
-[zttap200.ndi]
-CopyFiles = zttap200.driver,zttap200.files
-AddReg = zttap200.reg
-AddReg = zttap200.params.reg
-Characteristics = 0x81
-
-[zttap200.ndi.Services]
-AddService = zttap200, 2, zttap200.service
-
-[zttap200.reg]
-HKR, Ndi, Service, 0, "zttap200"
-HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
-HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
-HKR, , Manufacturer, 0, "%Provider%"
-HKR, , ProductName, 0, "%DeviceDescription%"
-
-[zttap200.params.reg]
-HKR, Ndi\params\MTU, ParamDesc, 0, "MTU"
-HKR, Ndi\params\MTU, Type, 0, "int"
-HKR, Ndi\params\MTU, Default, 0, "2800"
-HKR, Ndi\params\MTU, Optional, 0, "0"
-HKR, Ndi\params\MTU, Min, 0, "100"
-HKR, Ndi\params\MTU, Max, 0, "2800"
-HKR, Ndi\params\MTU, Step, 0, "1"
-HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status"
-HKR, Ndi\params\MediaStatus, Type, 0, "enum"
-HKR, Ndi\params\MediaStatus, Default, 0, "0"
-HKR, Ndi\params\MediaStatus, Optional, 0, "0"
-HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled"
-HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected"
-HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address"
-HKR, Ndi\params\MAC, Type, 0, "edit"
-HKR, Ndi\params\MAC, Optional, 0, "1"
-
-[zttap200.service]
-DisplayName = %DeviceDescription%
-ServiceType = 1
-StartType = 3
-ErrorControl = 1
-LoadOrderGroup = NDIS
-ServiceBinary = %12%\zttap200.sys
-
-[SourceDisksNames]
-1 = %DeviceDescription%, zttap200.sys
-
-[SourceDisksFiles]
-zttap200.sys = 1
-
-[DestinationDirs]
-zttap200.files = 11
-zttap200.driver = 12
-
-[zttap200.files]
-;
-
-[zttap200.driver]
-zttap200.sys,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
diff --git a/ext/bin/tap-windows-ndis5/x64/zttap200.sys b/ext/bin/tap-windows-ndis5/x64/zttap200.sys
deleted file mode 100644
index 339351fb..00000000
--- a/ext/bin/tap-windows-ndis5/x64/zttap200.sys
+++ /dev/null
Binary files differ
diff --git a/ext/bin/tap-windows-ndis5/x86/WdfCoinstaller01011.dll b/ext/bin/tap-windows-ndis5/x86/WdfCoinstaller01011.dll
deleted file mode 100644
index e943ea45..00000000
--- a/ext/bin/tap-windows-ndis5/x86/WdfCoinstaller01011.dll
+++ /dev/null
Binary files differ
diff --git a/ext/bin/tap-windows-ndis5/x86/zttap200.cat b/ext/bin/tap-windows-ndis5/x86/zttap200.cat
deleted file mode 100644
index d90ecbbe..00000000
--- a/ext/bin/tap-windows-ndis5/x86/zttap200.cat
+++ /dev/null
Binary files differ
diff --git a/ext/bin/tap-windows-ndis5/x86/zttap200.inf b/ext/bin/tap-windows-ndis5/x86/zttap200.inf
deleted file mode 100644
index 99aac9f2..00000000
--- a/ext/bin/tap-windows-ndis5/x86/zttap200.inf
+++ /dev/null
@@ -1,76 +0,0 @@
-[Version]
-Signature="$WINDOWS NT$"
-Class=Net
-ClassGuid={4d36e972-e325-11ce-bfc1-08002be10318}
-Provider=%Provider%
-CatalogFile=zttap200.cat
-DriverVer=01/24/2014,17.25.51.226
-
-[Strings]
-DeviceDescription = "ZeroTier One Virtual Network Port"
-Provider = "ZeroTier Networks LLC"
-
-; To build for x86, take NTamd64 off this and off the named section manually, build, then put it back!
-[Manufacturer]
-%Provider%=zttap200
-
-[zttap200]
-%DeviceDescription%=zttap200.ndi,zttap200
-
-[zttap200.ndi]
-CopyFiles = zttap200.driver,zttap200.files
-AddReg = zttap200.reg
-AddReg = zttap200.params.reg
-Characteristics = 0x81
-
-[zttap200.ndi.Services]
-AddService = zttap200, 2, zttap200.service
-
-[zttap200.reg]
-HKR, Ndi, Service, 0, "zttap200"
-HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
-HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
-HKR, , Manufacturer, 0, "%Provider%"
-HKR, , ProductName, 0, "%DeviceDescription%"
-
-[zttap200.params.reg]
-HKR, Ndi\params\MTU, ParamDesc, 0, "MTU"
-HKR, Ndi\params\MTU, Type, 0, "int"
-HKR, Ndi\params\MTU, Default, 0, "2800"
-HKR, Ndi\params\MTU, Optional, 0, "0"
-HKR, Ndi\params\MTU, Min, 0, "100"
-HKR, Ndi\params\MTU, Max, 0, "2800"
-HKR, Ndi\params\MTU, Step, 0, "1"
-HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status"
-HKR, Ndi\params\MediaStatus, Type, 0, "enum"
-HKR, Ndi\params\MediaStatus, Default, 0, "0"
-HKR, Ndi\params\MediaStatus, Optional, 0, "0"
-HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled"
-HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected"
-HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address"
-HKR, Ndi\params\MAC, Type, 0, "edit"
-HKR, Ndi\params\MAC, Optional, 0, "1"
-
-[zttap200.service]
-DisplayName = %DeviceDescription%
-ServiceType = 1
-StartType = 3
-ErrorControl = 1
-LoadOrderGroup = NDIS
-ServiceBinary = %12%\zttap200.sys
-
-[SourceDisksNames]
-1 = %DeviceDescription%, zttap200.sys
-
-[SourceDisksFiles]
-zttap200.sys = 1
-
-[DestinationDirs]
-zttap200.files = 11
-zttap200.driver = 12
-
-[zttap200.files]
-;
-
-[zttap200.driver]
-zttap200.sys,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
diff --git a/ext/bin/tap-windows-ndis5/x86/zttap200.sys b/ext/bin/tap-windows-ndis5/x86/zttap200.sys
deleted file mode 100644
index b7b11fbe..00000000
--- a/ext/bin/tap-windows-ndis5/x86/zttap200.sys
+++ /dev/null
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/certutil.exe b/ext/bin/tap-windows-ndis6/certutil.exe
new file mode 100644
index 00000000..b9a0a09c
--- /dev/null
+++ b/ext/bin/tap-windows-ndis6/certutil.exe
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi b/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi
index 818796f4..17fe08c2 100644
--- a/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi
+++ b/ext/bin/tap-windows-ndis6/x64/ZeroTierOne_NDIS6_x64.msi
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi b/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi
index b9e2d7ea..415774c9 100644
--- a/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi
+++ b/ext/bin/tap-windows-ndis6/x86/ZeroTierOne_NDIS6_x86.msi
Binary files differ
diff --git a/ext/bin/tap-windows-ndis6/zttap300.cer b/ext/bin/tap-windows-ndis6/zttap300.cer
new file mode 100644
index 00000000..ef74e041
--- /dev/null
+++ b/ext/bin/tap-windows-ndis6/zttap300.cer
Binary files differ
diff --git a/ext/ed25519-amd64-asm/batch.c b/ext/ed25519-amd64-asm/batch.c
new file mode 100644
index 00000000..955392ea
--- /dev/null
+++ b/ext/ed25519-amd64-asm/batch.c
@@ -0,0 +1,94 @@
+#include "crypto_sign.h"
+
+#include "crypto_verify_32.h"
+#include "crypto_hash_sha512.h"
+#include "randombytes.h"
+
+#include "ge25519.h"
+#include "hram.h"
+
+#define MAXBATCH 64
+
+int crypto_sign_open_batch(
+ unsigned char* const m[],unsigned long long mlen[],
+ unsigned char* const sm[],const unsigned long long smlen[],
+ unsigned char* const pk[],
+ unsigned long long num
+ )
+{
+ int ret = 0;
+ unsigned long long i, j;
+ shortsc25519 r[MAXBATCH];
+ sc25519 scalars[2*MAXBATCH+1];
+ ge25519 points[2*MAXBATCH+1];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned long long batchsize;
+
+ for (i = 0;i < num;++i) mlen[i] = -1;
+
+ while (num >= 3) {
+ batchsize = num;
+ if (batchsize > MAXBATCH) batchsize = MAXBATCH;
+
+ for (i = 0;i < batchsize;++i)
+ if (smlen[i] < 64) goto fallback;
+
+ randombytes((unsigned char*)r,sizeof(shortsc25519) * batchsize);
+
+ /* Computing scalars[0] = ((r1s1 + r2s2 + ...)) */
+ for(i=0;i<batchsize;i++)
+ {
+ sc25519_from32bytes(&scalars[i], sm[i]+32);
+ sc25519_mul_shortsc(&scalars[i], &scalars[i], &r[i]);
+ }
+ for(i=1;i<batchsize;i++)
+ sc25519_add(&scalars[0], &scalars[0], &scalars[i]);
+
+ /* Computing scalars[1] ... scalars[batchsize] as r[i]*H(R[i],A[i],m[i]) */
+ for(i=0;i<batchsize;i++)
+ {
+ get_hram(hram, sm[i], pk[i], m[i], smlen[i]);
+ sc25519_from64bytes(&scalars[i+1],hram);
+ sc25519_mul_shortsc(&scalars[i+1],&scalars[i+1],&r[i]);
+ }
+ /* Setting scalars[batchsize+1] ... scalars[2*batchsize] to r[i] */
+ for(i=0;i<batchsize;i++)
+ sc25519_from_shortsc(&scalars[batchsize+i+1],&r[i]);
+
+ /* Computing points */
+ points[0] = ge25519_base;
+
+ for(i=0;i<batchsize;i++)
+ if (ge25519_unpackneg_vartime(&points[i+1], pk[i])) goto fallback;
+ for(i=0;i<batchsize;i++)
+ if (ge25519_unpackneg_vartime(&points[batchsize+i+1], sm[i])) goto fallback;
+
+ ge25519_multi_scalarmult_vartime(points, points, scalars, 2*batchsize+1);
+
+ if (ge25519_isneutral_vartime(points)) {
+ for(i=0;i<batchsize;i++)
+ {
+ for(j=0;j<smlen[i]-64;j++)
+ m[i][j] = sm[i][j + 64];
+ mlen[i] = smlen[i]-64;
+ }
+ } else {
+ fallback:
+
+ for (i = 0;i < batchsize;++i)
+ ret |= crypto_sign_open(m[i], &mlen[i], sm[i], smlen[i], pk[i]);
+ }
+
+ m += batchsize;
+ mlen += batchsize;
+ sm += batchsize;
+ smlen += batchsize;
+ pk += batchsize;
+ num -= batchsize;
+ }
+
+ for (i = 0;i < num;++i)
+ ret |= crypto_sign_open(m[i], &mlen[i], sm[i], smlen[i], pk[i]);
+
+ return ret;
+}
diff --git a/ext/ed25519-amd64-asm/choose_t.s b/ext/ed25519-amd64-asm/choose_t.s
new file mode 100644
index 00000000..f10d8b8b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/choose_t.s
@@ -0,0 +1,1565 @@
+
+# qhasm: int64 tp
+
+# qhasm: int64 pos
+
+# qhasm: int64 b
+
+# qhasm: int64 basep
+
+# qhasm: input tp
+
+# qhasm: input pos
+
+# qhasm: input b
+
+# qhasm: input basep
+
+# qhasm: int64 mask
+
+# qhasm: int64 u
+
+# qhasm: int64 tysubx0
+
+# qhasm: int64 tysubx1
+
+# qhasm: int64 tysubx2
+
+# qhasm: int64 tysubx3
+
+# qhasm: int64 txaddy0
+
+# qhasm: int64 txaddy1
+
+# qhasm: int64 txaddy2
+
+# qhasm: int64 txaddy3
+
+# qhasm: int64 tt2d0
+
+# qhasm: int64 tt2d1
+
+# qhasm: int64 tt2d2
+
+# qhasm: int64 tt2d3
+
+# qhasm: int64 tt0
+
+# qhasm: int64 tt1
+
+# qhasm: int64 tt2
+
+# qhasm: int64 tt3
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: int64 t
+
+# qhasm: stack64 tp_stack
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_choose_t
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_choose_t
+.globl crypto_sign_ed25519_amd64_64_choose_t
+_crypto_sign_ed25519_amd64_64_choose_t:
+crypto_sign_ed25519_amd64_64_choose_t:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: tp_stack = tp
+# asm 1: movq <tp=int64#1,>tp_stack=stack64#8
+# asm 2: movq <tp=%rdi,>tp_stack=56(%rsp)
+movq %rdi,56(%rsp)
+
+# qhasm: pos *= 768
+# asm 1: imulq $768,<pos=int64#2,>pos=int64#1
+# asm 2: imulq $768,<pos=%rsi,>pos=%rdi
+imulq $768,%rsi,%rdi
+
+# qhasm: mask = b
+# asm 1: mov <b=int64#3,>mask=int64#2
+# asm 2: mov <b=%rdx,>mask=%rsi
+mov %rdx,%rsi
+
+# qhasm: (int64) mask >>= 7
+# asm 1: sar $7,<mask=int64#2
+# asm 2: sar $7,<mask=%rsi
+sar $7,%rsi
+
+# qhasm: u = b
+# asm 1: mov <b=int64#3,>u=int64#5
+# asm 2: mov <b=%rdx,>u=%r8
+mov %rdx,%r8
+
+# qhasm: u += mask
+# asm 1: add <mask=int64#2,<u=int64#5
+# asm 2: add <mask=%rsi,<u=%r8
+add %rsi,%r8
+
+# qhasm: u ^= mask
+# asm 1: xor <mask=int64#2,<u=int64#5
+# asm 2: xor <mask=%rsi,<u=%r8
+xor %rsi,%r8
+
+# qhasm: tysubx0 = 1
+# asm 1: mov $1,>tysubx0=int64#2
+# asm 2: mov $1,>tysubx0=%rsi
+mov $1,%rsi
+
+# qhasm: tysubx1 = 0
+# asm 1: mov $0,>tysubx1=int64#6
+# asm 2: mov $0,>tysubx1=%r9
+mov $0,%r9
+
+# qhasm: tysubx2 = 0
+# asm 1: mov $0,>tysubx2=int64#7
+# asm 2: mov $0,>tysubx2=%rax
+mov $0,%rax
+
+# qhasm: tysubx3 = 0
+# asm 1: mov $0,>tysubx3=int64#8
+# asm 2: mov $0,>tysubx3=%r10
+mov $0,%r10
+
+# qhasm: txaddy0 = 1
+# asm 1: mov $1,>txaddy0=int64#9
+# asm 2: mov $1,>txaddy0=%r11
+mov $1,%r11
+
+# qhasm: txaddy1 = 0
+# asm 1: mov $0,>txaddy1=int64#10
+# asm 2: mov $0,>txaddy1=%r12
+mov $0,%r12
+
+# qhasm: txaddy2 = 0
+# asm 1: mov $0,>txaddy2=int64#11
+# asm 2: mov $0,>txaddy2=%r13
+mov $0,%r13
+
+# qhasm: txaddy3 = 0
+# asm 1: mov $0,>txaddy3=int64#12
+# asm 2: mov $0,>txaddy3=%r14
+mov $0,%r14
+
+# qhasm: =? u - 1
+# asm 1: cmp $1,<u=int64#5
+# asm 2: cmp $1,<u=%r8
+cmp $1,%r8
+
+# qhasm: t = *(uint64 *)(basep + 0 + pos)
+# asm 1: movq 0(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 0(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 0(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 8 + pos)
+# asm 1: movq 8(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 8(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 8(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 16 + pos)
+# asm 1: movq 16(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 16(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 16(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 24 + pos)
+# asm 1: movq 24(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 24(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 24(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 32 + pos)
+# asm 1: movq 32(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 32(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 32(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 40 + pos)
+# asm 1: movq 40(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 40(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 40(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 48 + pos)
+# asm 1: movq 48(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 48(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 48(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 56 + pos)
+# asm 1: movq 56(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 56(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 56(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 2
+# asm 1: cmp $2,<u=int64#5
+# asm 2: cmp $2,<u=%r8
+cmp $2,%r8
+
+# qhasm: t = *(uint64 *)(basep + 96 + pos)
+# asm 1: movq 96(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 96(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 96(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 104 + pos)
+# asm 1: movq 104(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 104(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 104(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 112 + pos)
+# asm 1: movq 112(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 112(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 112(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 120 + pos)
+# asm 1: movq 120(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 120(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 120(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 128 + pos)
+# asm 1: movq 128(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 128(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 128(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 136 + pos)
+# asm 1: movq 136(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 136(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 136(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 144 + pos)
+# asm 1: movq 144(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 144(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 144(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 152 + pos)
+# asm 1: movq 152(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 152(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 152(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 3
+# asm 1: cmp $3,<u=int64#5
+# asm 2: cmp $3,<u=%r8
+cmp $3,%r8
+
+# qhasm: t = *(uint64 *)(basep + 192 + pos)
+# asm 1: movq 192(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 192(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 192(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 200 + pos)
+# asm 1: movq 200(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 200(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 200(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 208 + pos)
+# asm 1: movq 208(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 208(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 208(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 216 + pos)
+# asm 1: movq 216(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 216(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 216(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 224 + pos)
+# asm 1: movq 224(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 224(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 224(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 232 + pos)
+# asm 1: movq 232(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 232(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 232(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 240 + pos)
+# asm 1: movq 240(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 240(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 240(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 248 + pos)
+# asm 1: movq 248(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 248(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 248(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 4
+# asm 1: cmp $4,<u=int64#5
+# asm 2: cmp $4,<u=%r8
+cmp $4,%r8
+
+# qhasm: t = *(uint64 *)(basep + 288 + pos)
+# asm 1: movq 288(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 288(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 288(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 296 + pos)
+# asm 1: movq 296(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 296(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 296(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 304 + pos)
+# asm 1: movq 304(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 304(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 304(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 312 + pos)
+# asm 1: movq 312(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 312(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 312(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 320 + pos)
+# asm 1: movq 320(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 320(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 320(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 328 + pos)
+# asm 1: movq 328(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 328(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 328(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 336 + pos)
+# asm 1: movq 336(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 336(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 336(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 344 + pos)
+# asm 1: movq 344(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 344(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 344(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 5
+# asm 1: cmp $5,<u=int64#5
+# asm 2: cmp $5,<u=%r8
+cmp $5,%r8
+
+# qhasm: t = *(uint64 *)(basep + 384 + pos)
+# asm 1: movq 384(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 384(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 384(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 392 + pos)
+# asm 1: movq 392(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 392(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 392(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 400 + pos)
+# asm 1: movq 400(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 400(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 400(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 408 + pos)
+# asm 1: movq 408(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 408(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 408(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 416 + pos)
+# asm 1: movq 416(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 416(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 416(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 424 + pos)
+# asm 1: movq 424(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 424(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 424(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 432 + pos)
+# asm 1: movq 432(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 432(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 432(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 440 + pos)
+# asm 1: movq 440(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 440(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 440(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 6
+# asm 1: cmp $6,<u=int64#5
+# asm 2: cmp $6,<u=%r8
+cmp $6,%r8
+
+# qhasm: t = *(uint64 *)(basep + 480 + pos)
+# asm 1: movq 480(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 480(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 480(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 488 + pos)
+# asm 1: movq 488(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 488(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 488(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 496 + pos)
+# asm 1: movq 496(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 496(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 496(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 504 + pos)
+# asm 1: movq 504(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 504(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 504(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 512 + pos)
+# asm 1: movq 512(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 512(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 512(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 520 + pos)
+# asm 1: movq 520(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 520(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 520(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 528 + pos)
+# asm 1: movq 528(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 528(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 528(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 536 + pos)
+# asm 1: movq 536(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 536(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 536(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 7
+# asm 1: cmp $7,<u=int64#5
+# asm 2: cmp $7,<u=%r8
+cmp $7,%r8
+
+# qhasm: t = *(uint64 *)(basep + 576 + pos)
+# asm 1: movq 576(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 576(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 576(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 584 + pos)
+# asm 1: movq 584(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 584(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 584(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 592 + pos)
+# asm 1: movq 592(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 592(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 592(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 600 + pos)
+# asm 1: movq 600(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 600(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 600(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 608 + pos)
+# asm 1: movq 608(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 608(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 608(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 616 + pos)
+# asm 1: movq 616(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 616(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 616(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 624 + pos)
+# asm 1: movq 624(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 624(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 624(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 632 + pos)
+# asm 1: movq 632(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 632(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 632(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: =? u - 8
+# asm 1: cmp $8,<u=int64#5
+# asm 2: cmp $8,<u=%r8
+cmp $8,%r8
+
+# qhasm: t = *(uint64 *)(basep + 672 + pos)
+# asm 1: movq 672(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 672(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 672(%rcx,%rdi),%r15
+
+# qhasm: tysubx0 = t if =
+# asm 1: cmove <t=int64#13,<tysubx0=int64#2
+# asm 2: cmove <t=%r15,<tysubx0=%rsi
+cmove %r15,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 680 + pos)
+# asm 1: movq 680(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 680(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 680(%rcx,%rdi),%r15
+
+# qhasm: tysubx1 = t if =
+# asm 1: cmove <t=int64#13,<tysubx1=int64#6
+# asm 2: cmove <t=%r15,<tysubx1=%r9
+cmove %r15,%r9
+
+# qhasm: t = *(uint64 *)(basep + 688 + pos)
+# asm 1: movq 688(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 688(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 688(%rcx,%rdi),%r15
+
+# qhasm: tysubx2 = t if =
+# asm 1: cmove <t=int64#13,<tysubx2=int64#7
+# asm 2: cmove <t=%r15,<tysubx2=%rax
+cmove %r15,%rax
+
+# qhasm: t = *(uint64 *)(basep + 696 + pos)
+# asm 1: movq 696(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 696(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 696(%rcx,%rdi),%r15
+
+# qhasm: tysubx3 = t if =
+# asm 1: cmove <t=int64#13,<tysubx3=int64#8
+# asm 2: cmove <t=%r15,<tysubx3=%r10
+cmove %r15,%r10
+
+# qhasm: t = *(uint64 *)(basep + 704 + pos)
+# asm 1: movq 704(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 704(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 704(%rcx,%rdi),%r15
+
+# qhasm: txaddy0 = t if =
+# asm 1: cmove <t=int64#13,<txaddy0=int64#9
+# asm 2: cmove <t=%r15,<txaddy0=%r11
+cmove %r15,%r11
+
+# qhasm: t = *(uint64 *)(basep + 712 + pos)
+# asm 1: movq 712(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 712(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 712(%rcx,%rdi),%r15
+
+# qhasm: txaddy1 = t if =
+# asm 1: cmove <t=int64#13,<txaddy1=int64#10
+# asm 2: cmove <t=%r15,<txaddy1=%r12
+cmove %r15,%r12
+
+# qhasm: t = *(uint64 *)(basep + 720 + pos)
+# asm 1: movq 720(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 720(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 720(%rcx,%rdi),%r15
+
+# qhasm: txaddy2 = t if =
+# asm 1: cmove <t=int64#13,<txaddy2=int64#11
+# asm 2: cmove <t=%r15,<txaddy2=%r13
+cmove %r15,%r13
+
+# qhasm: t = *(uint64 *)(basep + 728 + pos)
+# asm 1: movq 728(<basep=int64#4,<pos=int64#1),>t=int64#13
+# asm 2: movq 728(<basep=%rcx,<pos=%rdi),>t=%r15
+movq 728(%rcx,%rdi),%r15
+
+# qhasm: txaddy3 = t if =
+# asm 1: cmove <t=int64#13,<txaddy3=int64#12
+# asm 2: cmove <t=%r15,<txaddy3=%r14
+cmove %r15,%r14
+
+# qhasm: signed<? b - 0
+# asm 1: cmp $0,<b=int64#3
+# asm 2: cmp $0,<b=%rdx
+cmp $0,%rdx
+
+# qhasm: t = tysubx0
+# asm 1: mov <tysubx0=int64#2,>t=int64#13
+# asm 2: mov <tysubx0=%rsi,>t=%r15
+mov %rsi,%r15
+
+# qhasm: tysubx0 = txaddy0 if signed<
+# asm 1: cmovl <txaddy0=int64#9,<tysubx0=int64#2
+# asm 2: cmovl <txaddy0=%r11,<tysubx0=%rsi
+cmovl %r11,%rsi
+
+# qhasm: txaddy0 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy0=int64#9
+# asm 2: cmovl <t=%r15,<txaddy0=%r11
+cmovl %r15,%r11
+
+# qhasm: t = tysubx1
+# asm 1: mov <tysubx1=int64#6,>t=int64#13
+# asm 2: mov <tysubx1=%r9,>t=%r15
+mov %r9,%r15
+
+# qhasm: tysubx1 = txaddy1 if signed<
+# asm 1: cmovl <txaddy1=int64#10,<tysubx1=int64#6
+# asm 2: cmovl <txaddy1=%r12,<tysubx1=%r9
+cmovl %r12,%r9
+
+# qhasm: txaddy1 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy1=int64#10
+# asm 2: cmovl <t=%r15,<txaddy1=%r12
+cmovl %r15,%r12
+
+# qhasm: t = tysubx2
+# asm 1: mov <tysubx2=int64#7,>t=int64#13
+# asm 2: mov <tysubx2=%rax,>t=%r15
+mov %rax,%r15
+
+# qhasm: tysubx2 = txaddy2 if signed<
+# asm 1: cmovl <txaddy2=int64#11,<tysubx2=int64#7
+# asm 2: cmovl <txaddy2=%r13,<tysubx2=%rax
+cmovl %r13,%rax
+
+# qhasm: txaddy2 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy2=int64#11
+# asm 2: cmovl <t=%r15,<txaddy2=%r13
+cmovl %r15,%r13
+
+# qhasm: t = tysubx3
+# asm 1: mov <tysubx3=int64#8,>t=int64#13
+# asm 2: mov <tysubx3=%r10,>t=%r15
+mov %r10,%r15
+
+# qhasm: tysubx3 = txaddy3 if signed<
+# asm 1: cmovl <txaddy3=int64#12,<tysubx3=int64#8
+# asm 2: cmovl <txaddy3=%r14,<tysubx3=%r10
+cmovl %r14,%r10
+
+# qhasm: txaddy3 = t if signed<
+# asm 1: cmovl <t=int64#13,<txaddy3=int64#12
+# asm 2: cmovl <t=%r15,<txaddy3=%r14
+cmovl %r15,%r14
+
+# qhasm: tp = tp_stack
+# asm 1: movq <tp_stack=stack64#8,>tp=int64#13
+# asm 2: movq <tp_stack=56(%rsp),>tp=%r15
+movq 56(%rsp),%r15
+
+# qhasm: *(uint64 *)(tp + 0) = tysubx0
+# asm 1: movq <tysubx0=int64#2,0(<tp=int64#13)
+# asm 2: movq <tysubx0=%rsi,0(<tp=%r15)
+movq %rsi,0(%r15)
+
+# qhasm: *(uint64 *)(tp + 8) = tysubx1
+# asm 1: movq <tysubx1=int64#6,8(<tp=int64#13)
+# asm 2: movq <tysubx1=%r9,8(<tp=%r15)
+movq %r9,8(%r15)
+
+# qhasm: *(uint64 *)(tp + 16) = tysubx2
+# asm 1: movq <tysubx2=int64#7,16(<tp=int64#13)
+# asm 2: movq <tysubx2=%rax,16(<tp=%r15)
+movq %rax,16(%r15)
+
+# qhasm: *(uint64 *)(tp + 24) = tysubx3
+# asm 1: movq <tysubx3=int64#8,24(<tp=int64#13)
+# asm 2: movq <tysubx3=%r10,24(<tp=%r15)
+movq %r10,24(%r15)
+
+# qhasm: *(uint64 *)(tp + 32) = txaddy0
+# asm 1: movq <txaddy0=int64#9,32(<tp=int64#13)
+# asm 2: movq <txaddy0=%r11,32(<tp=%r15)
+movq %r11,32(%r15)
+
+# qhasm: *(uint64 *)(tp + 40) = txaddy1
+# asm 1: movq <txaddy1=int64#10,40(<tp=int64#13)
+# asm 2: movq <txaddy1=%r12,40(<tp=%r15)
+movq %r12,40(%r15)
+
+# qhasm: *(uint64 *)(tp + 48) = txaddy2
+# asm 1: movq <txaddy2=int64#11,48(<tp=int64#13)
+# asm 2: movq <txaddy2=%r13,48(<tp=%r15)
+movq %r13,48(%r15)
+
+# qhasm: *(uint64 *)(tp + 56) = txaddy3
+# asm 1: movq <txaddy3=int64#12,56(<tp=int64#13)
+# asm 2: movq <txaddy3=%r14,56(<tp=%r15)
+movq %r14,56(%r15)
+
+# qhasm: tt2d0 = 0
+# asm 1: mov $0,>tt2d0=int64#2
+# asm 2: mov $0,>tt2d0=%rsi
+mov $0,%rsi
+
+# qhasm: tt2d1 = 0
+# asm 1: mov $0,>tt2d1=int64#6
+# asm 2: mov $0,>tt2d1=%r9
+mov $0,%r9
+
+# qhasm: tt2d2 = 0
+# asm 1: mov $0,>tt2d2=int64#7
+# asm 2: mov $0,>tt2d2=%rax
+mov $0,%rax
+
+# qhasm: tt2d3 = 0
+# asm 1: mov $0,>tt2d3=int64#8
+# asm 2: mov $0,>tt2d3=%r10
+mov $0,%r10
+
+# qhasm: =? u - 1
+# asm 1: cmp $1,<u=int64#5
+# asm 2: cmp $1,<u=%r8
+cmp $1,%r8
+
+# qhasm: t = *(uint64 *)(basep + 64 + pos)
+# asm 1: movq 64(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 64(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 64(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 72 + pos)
+# asm 1: movq 72(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 72(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 72(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 80 + pos)
+# asm 1: movq 80(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 80(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 80(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 88 + pos)
+# asm 1: movq 88(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 88(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 88(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 2
+# asm 1: cmp $2,<u=int64#5
+# asm 2: cmp $2,<u=%r8
+cmp $2,%r8
+
+# qhasm: t = *(uint64 *)(basep + 160 + pos)
+# asm 1: movq 160(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 160(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 160(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 168 + pos)
+# asm 1: movq 168(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 168(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 168(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 176 + pos)
+# asm 1: movq 176(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 176(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 176(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 184 + pos)
+# asm 1: movq 184(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 184(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 184(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 3
+# asm 1: cmp $3,<u=int64#5
+# asm 2: cmp $3,<u=%r8
+cmp $3,%r8
+
+# qhasm: t = *(uint64 *)(basep + 256 + pos)
+# asm 1: movq 256(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 256(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 256(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 264 + pos)
+# asm 1: movq 264(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 264(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 264(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 272 + pos)
+# asm 1: movq 272(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 272(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 272(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 280 + pos)
+# asm 1: movq 280(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 280(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 280(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 4
+# asm 1: cmp $4,<u=int64#5
+# asm 2: cmp $4,<u=%r8
+cmp $4,%r8
+
+# qhasm: t = *(uint64 *)(basep + 352 + pos)
+# asm 1: movq 352(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 352(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 352(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 360 + pos)
+# asm 1: movq 360(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 360(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 360(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 368 + pos)
+# asm 1: movq 368(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 368(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 368(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 376 + pos)
+# asm 1: movq 376(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 376(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 376(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 5
+# asm 1: cmp $5,<u=int64#5
+# asm 2: cmp $5,<u=%r8
+cmp $5,%r8
+
+# qhasm: t = *(uint64 *)(basep + 448 + pos)
+# asm 1: movq 448(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 448(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 448(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 456 + pos)
+# asm 1: movq 456(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 456(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 456(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 464 + pos)
+# asm 1: movq 464(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 464(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 464(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 472 + pos)
+# asm 1: movq 472(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 472(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 472(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 6
+# asm 1: cmp $6,<u=int64#5
+# asm 2: cmp $6,<u=%r8
+cmp $6,%r8
+
+# qhasm: t = *(uint64 *)(basep + 544 + pos)
+# asm 1: movq 544(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 544(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 544(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 552 + pos)
+# asm 1: movq 552(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 552(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 552(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 560 + pos)
+# asm 1: movq 560(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 560(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 560(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 568 + pos)
+# asm 1: movq 568(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 568(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 568(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 7
+# asm 1: cmp $7,<u=int64#5
+# asm 2: cmp $7,<u=%r8
+cmp $7,%r8
+
+# qhasm: t = *(uint64 *)(basep + 640 + pos)
+# asm 1: movq 640(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 640(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 640(%rcx,%rdi),%r11
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#9,<tt2d0=int64#2
+# asm 2: cmove <t=%r11,<tt2d0=%rsi
+cmove %r11,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 648 + pos)
+# asm 1: movq 648(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 648(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 648(%rcx,%rdi),%r11
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#9,<tt2d1=int64#6
+# asm 2: cmove <t=%r11,<tt2d1=%r9
+cmove %r11,%r9
+
+# qhasm: t = *(uint64 *)(basep + 656 + pos)
+# asm 1: movq 656(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 656(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 656(%rcx,%rdi),%r11
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#9,<tt2d2=int64#7
+# asm 2: cmove <t=%r11,<tt2d2=%rax
+cmove %r11,%rax
+
+# qhasm: t = *(uint64 *)(basep + 664 + pos)
+# asm 1: movq 664(<basep=int64#4,<pos=int64#1),>t=int64#9
+# asm 2: movq 664(<basep=%rcx,<pos=%rdi),>t=%r11
+movq 664(%rcx,%rdi),%r11
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#9,<tt2d3=int64#8
+# asm 2: cmove <t=%r11,<tt2d3=%r10
+cmove %r11,%r10
+
+# qhasm: =? u - 8
+# asm 1: cmp $8,<u=int64#5
+# asm 2: cmp $8,<u=%r8
+cmp $8,%r8
+
+# qhasm: t = *(uint64 *)(basep + 736 + pos)
+# asm 1: movq 736(<basep=int64#4,<pos=int64#1),>t=int64#5
+# asm 2: movq 736(<basep=%rcx,<pos=%rdi),>t=%r8
+movq 736(%rcx,%rdi),%r8
+
+# qhasm: tt2d0 = t if =
+# asm 1: cmove <t=int64#5,<tt2d0=int64#2
+# asm 2: cmove <t=%r8,<tt2d0=%rsi
+cmove %r8,%rsi
+
+# qhasm: t = *(uint64 *)(basep + 744 + pos)
+# asm 1: movq 744(<basep=int64#4,<pos=int64#1),>t=int64#5
+# asm 2: movq 744(<basep=%rcx,<pos=%rdi),>t=%r8
+movq 744(%rcx,%rdi),%r8
+
+# qhasm: tt2d1 = t if =
+# asm 1: cmove <t=int64#5,<tt2d1=int64#6
+# asm 2: cmove <t=%r8,<tt2d1=%r9
+cmove %r8,%r9
+
+# qhasm: t = *(uint64 *)(basep + 752 + pos)
+# asm 1: movq 752(<basep=int64#4,<pos=int64#1),>t=int64#5
+# asm 2: movq 752(<basep=%rcx,<pos=%rdi),>t=%r8
+movq 752(%rcx,%rdi),%r8
+
+# qhasm: tt2d2 = t if =
+# asm 1: cmove <t=int64#5,<tt2d2=int64#7
+# asm 2: cmove <t=%r8,<tt2d2=%rax
+cmove %r8,%rax
+
+# qhasm: t = *(uint64 *)(basep + 760 + pos)
+# asm 1: movq 760(<basep=int64#4,<pos=int64#1),>t=int64#1
+# asm 2: movq 760(<basep=%rcx,<pos=%rdi),>t=%rdi
+movq 760(%rcx,%rdi),%rdi
+
+# qhasm: tt2d3 = t if =
+# asm 1: cmove <t=int64#1,<tt2d3=int64#8
+# asm 2: cmove <t=%rdi,<tt2d3=%r10
+cmove %rdi,%r10
+
+# qhasm: tt0 = 0
+# asm 1: mov $0,>tt0=int64#1
+# asm 2: mov $0,>tt0=%rdi
+mov $0,%rdi
+
+# qhasm: tt1 = 0
+# asm 1: mov $0,>tt1=int64#4
+# asm 2: mov $0,>tt1=%rcx
+mov $0,%rcx
+
+# qhasm: tt2 = 0
+# asm 1: mov $0,>tt2=int64#5
+# asm 2: mov $0,>tt2=%r8
+mov $0,%r8
+
+# qhasm: tt3 = 0
+# asm 1: mov $0,>tt3=int64#9
+# asm 2: mov $0,>tt3=%r11
+mov $0,%r11
+
+# qhasm: carry? tt0 -= tt2d0
+# asm 1: sub <tt2d0=int64#2,<tt0=int64#1
+# asm 2: sub <tt2d0=%rsi,<tt0=%rdi
+sub %rsi,%rdi
+
+# qhasm: carry? tt1 -= tt2d1 - carry
+# asm 1: sbb <tt2d1=int64#6,<tt1=int64#4
+# asm 2: sbb <tt2d1=%r9,<tt1=%rcx
+sbb %r9,%rcx
+
+# qhasm: carry? tt2 -= tt2d2 - carry
+# asm 1: sbb <tt2d2=int64#7,<tt2=int64#5
+# asm 2: sbb <tt2d2=%rax,<tt2=%r8
+sbb %rax,%r8
+
+# qhasm: carry? tt3 -= tt2d3 - carry
+# asm 1: sbb <tt2d3=int64#8,<tt3=int64#9
+# asm 2: sbb <tt2d3=%r10,<tt3=%r11
+sbb %r10,%r11
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#10
+# asm 2: mov $0,>subt0=%r12
+mov $0,%r12
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#11
+# asm 2: mov $38,>subt1=%r13
+mov $38,%r13
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#10,<subt1=int64#11
+# asm 2: cmovae <subt0=%r12,<subt1=%r13
+cmovae %r12,%r13
+
+# qhasm: carry? tt0 -= subt1
+# asm 1: sub <subt1=int64#11,<tt0=int64#1
+# asm 2: sub <subt1=%r13,<tt0=%rdi
+sub %r13,%rdi
+
+# qhasm: carry? tt1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<tt1=int64#4
+# asm 2: sbb <subt0=%r12,<tt1=%rcx
+sbb %r12,%rcx
+
+# qhasm: carry? tt2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<tt2=int64#5
+# asm 2: sbb <subt0=%r12,<tt2=%r8
+sbb %r12,%r8
+
+# qhasm: carry? tt3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<tt3=int64#9
+# asm 2: sbb <subt0=%r12,<tt3=%r11
+sbb %r12,%r11
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#11,<subt0=int64#10
+# asm 2: cmovc <subt1=%r13,<subt0=%r12
+cmovc %r13,%r12
+
+# qhasm: tt0 -= subt0
+# asm 1: sub <subt0=int64#10,<tt0=int64#1
+# asm 2: sub <subt0=%r12,<tt0=%rdi
+sub %r12,%rdi
+
+# qhasm: signed<? b - 0
+# asm 1: cmp $0,<b=int64#3
+# asm 2: cmp $0,<b=%rdx
+cmp $0,%rdx
+
+# qhasm: tt2d0 = tt0 if signed<
+# asm 1: cmovl <tt0=int64#1,<tt2d0=int64#2
+# asm 2: cmovl <tt0=%rdi,<tt2d0=%rsi
+cmovl %rdi,%rsi
+
+# qhasm: tt2d1 = tt1 if signed<
+# asm 1: cmovl <tt1=int64#4,<tt2d1=int64#6
+# asm 2: cmovl <tt1=%rcx,<tt2d1=%r9
+cmovl %rcx,%r9
+
+# qhasm: tt2d2 = tt2 if signed<
+# asm 1: cmovl <tt2=int64#5,<tt2d2=int64#7
+# asm 2: cmovl <tt2=%r8,<tt2d2=%rax
+cmovl %r8,%rax
+
+# qhasm: tt2d3 = tt3 if signed<
+# asm 1: cmovl <tt3=int64#9,<tt2d3=int64#8
+# asm 2: cmovl <tt3=%r11,<tt2d3=%r10
+cmovl %r11,%r10
+
+# qhasm: *(uint64 *)(tp + 64) = tt2d0
+# asm 1: movq <tt2d0=int64#2,64(<tp=int64#13)
+# asm 2: movq <tt2d0=%rsi,64(<tp=%r15)
+movq %rsi,64(%r15)
+
+# qhasm: *(uint64 *)(tp + 72) = tt2d1
+# asm 1: movq <tt2d1=int64#6,72(<tp=int64#13)
+# asm 2: movq <tt2d1=%r9,72(<tp=%r15)
+movq %r9,72(%r15)
+
+# qhasm: *(uint64 *)(tp + 80) = tt2d2
+# asm 1: movq <tt2d2=int64#7,80(<tp=int64#13)
+# asm 2: movq <tt2d2=%rax,80(<tp=%r15)
+movq %rax,80(%r15)
+
+# qhasm: *(uint64 *)(tp + 88) = tt2d3
+# asm 1: movq <tt2d3=int64#8,88(<tp=int64#13)
+# asm 2: movq <tt2d3=%r10,88(<tp=%r15)
+movq %r10,88(%r15)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/consts.s b/ext/ed25519-amd64-asm/consts.s
new file mode 100644
index 00000000..c272383f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/consts.s
@@ -0,0 +1,39 @@
+.data
+
+.globl crypto_sign_ed25519_amd64_64_121666
+.globl crypto_sign_ed25519_amd64_64_MU0
+.globl crypto_sign_ed25519_amd64_64_MU1
+.globl crypto_sign_ed25519_amd64_64_MU2
+.globl crypto_sign_ed25519_amd64_64_MU3
+.globl crypto_sign_ed25519_amd64_64_MU4
+.globl crypto_sign_ed25519_amd64_64_ORDER0
+.globl crypto_sign_ed25519_amd64_64_ORDER1
+.globl crypto_sign_ed25519_amd64_64_ORDER2
+.globl crypto_sign_ed25519_amd64_64_ORDER3
+.globl crypto_sign_ed25519_amd64_64_EC2D0
+.globl crypto_sign_ed25519_amd64_64_EC2D1
+.globl crypto_sign_ed25519_amd64_64_EC2D2
+.globl crypto_sign_ed25519_amd64_64_EC2D3
+.globl crypto_sign_ed25519_amd64_64_38
+
+.p2align 4
+
+crypto_sign_ed25519_amd64_64_121666: .quad 121666
+
+crypto_sign_ed25519_amd64_64_MU0: .quad 0xED9CE5A30A2C131B
+crypto_sign_ed25519_amd64_64_MU1: .quad 0x2106215D086329A7
+crypto_sign_ed25519_amd64_64_MU2: .quad 0xFFFFFFFFFFFFFFEB
+crypto_sign_ed25519_amd64_64_MU3: .quad 0xFFFFFFFFFFFFFFFF
+crypto_sign_ed25519_amd64_64_MU4: .quad 0x000000000000000F
+
+crypto_sign_ed25519_amd64_64_ORDER0: .quad 0x5812631A5CF5D3ED
+crypto_sign_ed25519_amd64_64_ORDER1: .quad 0x14DEF9DEA2F79CD6
+crypto_sign_ed25519_amd64_64_ORDER2: .quad 0x0000000000000000
+crypto_sign_ed25519_amd64_64_ORDER3: .quad 0x1000000000000000
+
+crypto_sign_ed25519_amd64_64_EC2D0: .quad 0xEBD69B9426B2F146
+crypto_sign_ed25519_amd64_64_EC2D1: .quad 0x00E0149A8283B156
+crypto_sign_ed25519_amd64_64_EC2D2: .quad 0x198E80F2EEF3D130
+crypto_sign_ed25519_amd64_64_EC2D3: .quad 0xA406D9DC56DFFCE7
+
+crypto_sign_ed25519_amd64_64_38: .quad 38
diff --git a/ext/ed25519-amd64-asm/fe25519.h b/ext/ed25519-amd64-asm/fe25519.h
new file mode 100644
index 00000000..33ffabbe
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519.h
@@ -0,0 +1,64 @@
+#ifndef FE25519_H
+#define FE25519_H
+
+#define fe25519 crypto_sign_ed25519_amd64_64_fe25519
+#define fe25519_freeze crypto_sign_ed25519_amd64_64_fe25519_freeze
+#define fe25519_unpack crypto_sign_ed25519_amd64_64_fe25519_unpack
+#define fe25519_pack crypto_sign_ed25519_amd64_64_fe25519_pack
+#define fe25519_iszero_vartime crypto_sign_ed25519_amd64_64_fe25519_iszero_vartime
+#define fe25519_iseq_vartime crypto_sign_ed25519_amd64_64_fe25519_iseq_vartime
+#define fe25519_cmov crypto_sign_ed25519_amd64_64_fe25519_cmov
+#define fe25519_setint crypto_sign_ed25519_amd64_64_fe25519_setint
+#define fe25519_neg crypto_sign_ed25519_amd64_64_fe25519_neg
+#define fe25519_getparity crypto_sign_ed25519_amd64_64_fe25519_getparity
+#define fe25519_add crypto_sign_ed25519_amd64_64_fe25519_add
+#define fe25519_sub crypto_sign_ed25519_amd64_64_fe25519_sub
+#define fe25519_mul crypto_sign_ed25519_amd64_64_fe25519_mul
+#define fe25519_mul121666 crypto_sign_ed25519_amd64_64_fe25519_mul121666
+#define fe25519_square crypto_sign_ed25519_amd64_64_fe25519_square
+#define fe25519_invert crypto_sign_ed25519_amd64_64_fe25519_invert
+#define fe25519_pow2523 crypto_sign_ed25519_amd64_64_fe25519_pow2523
+
+typedef struct
+{
+ unsigned long long v[4];
+}
+fe25519;
+
+void fe25519_freeze(fe25519 *r);
+
+void fe25519_unpack(fe25519 *r, const unsigned char x[32]);
+
+void fe25519_pack(unsigned char r[32], const fe25519 *x);
+
+void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b);
+
+void fe25519_cswap(fe25519 *r, fe25519 *x, unsigned char b);
+
+void fe25519_setint(fe25519 *r, unsigned int v);
+
+void fe25519_neg(fe25519 *r, const fe25519 *x);
+
+unsigned char fe25519_getparity(const fe25519 *x);
+
+int fe25519_iszero_vartime(const fe25519 *x);
+
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y);
+
+void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y);
+
+void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y);
+
+void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y);
+
+void fe25519_mul121666(fe25519 *r, const fe25519 *x);
+
+void fe25519_square(fe25519 *r, const fe25519 *x);
+
+void fe25519_pow(fe25519 *r, const fe25519 *x, const unsigned char *e);
+
+void fe25519_invert(fe25519 *r, const fe25519 *x);
+
+void fe25519_pow2523(fe25519 *r, const fe25519 *x);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/fe25519_add.s b/ext/ed25519-amd64-asm/fe25519_add.s
new file mode 100644
index 00000000..b2e56252
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_add.s
@@ -0,0 +1,189 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_add
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_add
+.globl crypto_sign_ed25519_amd64_64_fe25519_add
+_crypto_sign_ed25519_amd64_64_fe25519_add:
+crypto_sign_ed25519_amd64_64_fe25519_add:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 += *(uint64 *)(yp + 0)
+# asm 1: addq 0(<yp=int64#3),<r0=int64#4
+# asm 2: addq 0(<yp=%rdx),<r0=%rcx
+addq 0(%rdx),%rcx
+
+# qhasm: carry? r1 += *(uint64 *)(yp + 8) + carry
+# asm 1: adcq 8(<yp=int64#3),<r1=int64#5
+# asm 2: adcq 8(<yp=%rdx),<r1=%r8
+adcq 8(%rdx),%r8
+
+# qhasm: carry? r2 += *(uint64 *)(yp + 16) + carry
+# asm 1: adcq 16(<yp=int64#3),<r2=int64#6
+# asm 2: adcq 16(<yp=%rdx),<r2=%r9
+adcq 16(%rdx),%r9
+
+# qhasm: carry? r3 += *(uint64 *)(yp + 24) + carry
+# asm 1: adcq 24(<yp=int64#3),<r3=int64#2
+# asm 2: adcq 24(<yp=%rdx),<r3=%rsi
+adcq 24(%rdx),%rsi
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#3
+# asm 2: mov $0,>addt0=%rdx
+mov $0,%rdx
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#3,<addt1=int64#7
+# asm 2: cmovae <addt0=%rdx,<addt1=%rax
+cmovae %rdx,%rax
+
+# qhasm: carry? r0 += addt1
+# asm 1: add <addt1=int64#7,<r0=int64#4
+# asm 2: add <addt1=%rax,<r0=%rcx
+add %rax,%rcx
+
+# qhasm: carry? r1 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<r1=int64#5
+# asm 2: adc <addt0=%rdx,<r1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? r2 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<r2=int64#6
+# asm 2: adc <addt0=%rdx,<r2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? r3 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<r3=int64#2
+# asm 2: adc <addt0=%rdx,<r3=%rsi
+adc %rdx,%rsi
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#3
+# asm 2: cmovc <addt1=%rax,<addt0=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r0 += addt0
+# asm 1: add <addt0=int64#3,<r0=int64#4
+# asm 2: add <addt0=%rdx,<r0=%rcx
+add %rdx,%rcx
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_freeze.s b/ext/ed25519-amd64-asm/fe25519_freeze.s
new file mode 100644
index 00000000..dea29021
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_freeze.s
@@ -0,0 +1,322 @@
+
+# qhasm: int64 rp
+
+# qhasm: input rp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 two63
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_freeze
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_freeze
+.globl crypto_sign_ed25519_amd64_64_fe25519_freeze
+_crypto_sign_ed25519_amd64_64_fe25519_freeze:
+crypto_sign_ed25519_amd64_64_fe25519_freeze:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: r0 = *(uint64 *) (rp + 0)
+# asm 1: movq 0(<rp=int64#1),>r0=int64#2
+# asm 2: movq 0(<rp=%rdi),>r0=%rsi
+movq 0(%rdi),%rsi
+
+# qhasm: r1 = *(uint64 *) (rp + 8)
+# asm 1: movq 8(<rp=int64#1),>r1=int64#3
+# asm 2: movq 8(<rp=%rdi),>r1=%rdx
+movq 8(%rdi),%rdx
+
+# qhasm: r2 = *(uint64 *) (rp + 16)
+# asm 1: movq 16(<rp=int64#1),>r2=int64#4
+# asm 2: movq 16(<rp=%rdi),>r2=%rcx
+movq 16(%rdi),%rcx
+
+# qhasm: r3 = *(uint64 *) (rp + 24)
+# asm 1: movq 24(<rp=int64#1),>r3=int64#5
+# asm 2: movq 24(<rp=%rdi),>r3=%r8
+movq 24(%rdi),%r8
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#2,>t0=int64#6
+# asm 2: mov <r0=%rsi,>t0=%r9
+mov %rsi,%r9
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#3,>t1=int64#7
+# asm 2: mov <r1=%rdx,>t1=%rax
+mov %rdx,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#4,>t2=int64#8
+# asm 2: mov <r2=%rcx,>t2=%r10
+mov %rcx,%r10
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#5,>t3=int64#9
+# asm 2: mov <r3=%r8,>t3=%r11
+mov %r8,%r11
+
+# qhasm: two63 = 1
+# asm 1: mov $1,>two63=int64#10
+# asm 2: mov $1,>two63=%r12
+mov $1,%r12
+
+# qhasm: two63 <<= 63
+# asm 1: shl $63,<two63=int64#10
+# asm 2: shl $63,<two63=%r12
+shl $63,%r12
+
+# qhasm: carry? t0 += 19
+# asm 1: add $19,<t0=int64#6
+# asm 2: add $19,<t0=%r9
+add $19,%r9
+
+# qhasm: carry? t1 += 0 + carry
+# asm 1: adc $0,<t1=int64#7
+# asm 2: adc $0,<t1=%rax
+adc $0,%rax
+
+# qhasm: carry? t2 += 0 + carry
+# asm 1: adc $0,<t2=int64#8
+# asm 2: adc $0,<t2=%r10
+adc $0,%r10
+
+# qhasm: carry? t3 += two63 + carry
+# asm 1: adc <two63=int64#10,<t3=int64#9
+# asm 2: adc <two63=%r12,<t3=%r11
+adc %r12,%r11
+
+# qhasm: r0 = t0 if carry
+# asm 1: cmovc <t0=int64#6,<r0=int64#2
+# asm 2: cmovc <t0=%r9,<r0=%rsi
+cmovc %r9,%rsi
+
+# qhasm: r1 = t1 if carry
+# asm 1: cmovc <t1=int64#7,<r1=int64#3
+# asm 2: cmovc <t1=%rax,<r1=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r2 = t2 if carry
+# asm 1: cmovc <t2=int64#8,<r2=int64#4
+# asm 2: cmovc <t2=%r10,<r2=%rcx
+cmovc %r10,%rcx
+
+# qhasm: r3 = t3 if carry
+# asm 1: cmovc <t3=int64#9,<r3=int64#5
+# asm 2: cmovc <t3=%r11,<r3=%r8
+cmovc %r11,%r8
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#2,>t0=int64#6
+# asm 2: mov <r0=%rsi,>t0=%r9
+mov %rsi,%r9
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#3,>t1=int64#7
+# asm 2: mov <r1=%rdx,>t1=%rax
+mov %rdx,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#4,>t2=int64#8
+# asm 2: mov <r2=%rcx,>t2=%r10
+mov %rcx,%r10
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#5,>t3=int64#9
+# asm 2: mov <r3=%r8,>t3=%r11
+mov %r8,%r11
+
+# qhasm: carry? t0 += 19
+# asm 1: add $19,<t0=int64#6
+# asm 2: add $19,<t0=%r9
+add $19,%r9
+
+# qhasm: carry? t1 += 0 + carry
+# asm 1: adc $0,<t1=int64#7
+# asm 2: adc $0,<t1=%rax
+adc $0,%rax
+
+# qhasm: carry? t2 += 0 + carry
+# asm 1: adc $0,<t2=int64#8
+# asm 2: adc $0,<t2=%r10
+adc $0,%r10
+
+# qhasm: carry? t3 += two63 + carry
+# asm 1: adc <two63=int64#10,<t3=int64#9
+# asm 2: adc <two63=%r12,<t3=%r11
+adc %r12,%r11
+
+# qhasm: r0 = t0 if carry
+# asm 1: cmovc <t0=int64#6,<r0=int64#2
+# asm 2: cmovc <t0=%r9,<r0=%rsi
+cmovc %r9,%rsi
+
+# qhasm: r1 = t1 if carry
+# asm 1: cmovc <t1=int64#7,<r1=int64#3
+# asm 2: cmovc <t1=%rax,<r1=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r2 = t2 if carry
+# asm 1: cmovc <t2=int64#8,<r2=int64#4
+# asm 2: cmovc <t2=%r10,<r2=%rcx
+cmovc %r10,%rcx
+
+# qhasm: r3 = t3 if carry
+# asm 1: cmovc <t3=int64#9,<r3=int64#5
+# asm 2: cmovc <t3=%r11,<r3=%r8
+cmovc %r11,%r8
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#2,0(<rp=int64#1)
+# asm 2: movq <r0=%rsi,0(<rp=%rdi)
+movq %rsi,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#3,8(<rp=int64#1)
+# asm 2: movq <r1=%rdx,8(<rp=%rdi)
+movq %rdx,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#4,16(<rp=int64#1)
+# asm 2: movq <r2=%rcx,16(<rp=%rdi)
+movq %rcx,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#5,24(<rp=int64#1)
+# asm 2: movq <r3=%r8,24(<rp=%rdi)
+movq %r8,24(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_getparity.c b/ext/ed25519-amd64-asm/fe25519_getparity.c
new file mode 100644
index 00000000..a003ec8f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_getparity.c
@@ -0,0 +1,8 @@
+#include "fe25519.h"
+
+unsigned char fe25519_getparity(const fe25519 *x)
+{
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ return (unsigned char)t.v[0] & 1;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_invert.c b/ext/ed25519-amd64-asm/fe25519_invert.c
new file mode 100644
index 00000000..a46d141f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_invert.c
@@ -0,0 +1,60 @@
+#include "fe25519.h"
+
+void fe25519_invert(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^253 - 2^3 */ fe25519_square(&t,&t);
+
+ /* 2^254 - 2^4 */ fe25519_square(&t,&t);
+
+ /* 2^255 - 2^5 */ fe25519_square(&t,&t);
+ /* 2^255 - 21 */ fe25519_mul(r,&t,&z11);
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_iseq.c b/ext/ed25519-amd64-asm/fe25519_iseq.c
new file mode 100644
index 00000000..bf72f8c9
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_iseq.c
@@ -0,0 +1,14 @@
+#include "fe25519.h"
+
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
+{
+ fe25519 t1 = *x;
+ fe25519 t2 = *y;
+ fe25519_freeze(&t1);
+ fe25519_freeze(&t2);
+ if(t1.v[0] != t2.v[0]) return 0;
+ if(t1.v[1] != t2.v[1]) return 0;
+ if(t1.v[2] != t2.v[2]) return 0;
+ if(t1.v[3] != t2.v[3]) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_iszero.c b/ext/ed25519-amd64-asm/fe25519_iszero.c
new file mode 100644
index 00000000..99e4dafa
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_iszero.c
@@ -0,0 +1,12 @@
+#include "fe25519.h"
+
+int fe25519_iszero_vartime(const fe25519 *x)
+{
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ if (t.v[0]) return 0;
+ if (t.v[1]) return 0;
+ if (t.v[2]) return 0;
+ if (t.v[3]) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_mul.s b/ext/ed25519-amd64-asm/fe25519_mul.s
new file mode 100644
index 00000000..14784281
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_mul.s
@@ -0,0 +1,865 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_mul
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_mul
+.globl crypto_sign_ed25519_amd64_64_fe25519_mul
+_crypto_sign_ed25519_amd64_64_fe25519_mul:
+crypto_sign_ed25519_amd64_64_fe25519_mul:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: yp = yp
+# asm 1: mov <yp=int64#3,>yp=int64#4
+# asm 2: mov <yp=%rdx,>yp=%rcx
+mov %rdx,%rcx
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>mulx0=int64#10
+# asm 2: movq 0(<xp=%rsi),>mulx0=%r12
+movq 0(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: r0 = mulrax
+# asm 1: mov <mulrax=int64#7,>r0=int64#11
+# asm 2: mov <mulrax=%rax,>r0=%r13
+mov %rax,%r13
+
+# qhasm: r1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>r1=int64#12
+# asm 2: mov <mulrdx=%rdx,>r1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? r1 += mulrax
+# asm 1: add <mulrax=int64#7,<r1=int64#12
+# asm 2: add <mulrax=%rax,<r1=%r14
+add %rax,%r14
+
+# qhasm: r2 = 0
+# asm 1: mov $0,>r2=int64#13
+# asm 2: mov $0,>r2=%r15
+mov $0,%r15
+
+# qhasm: r2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<r2=int64#13
+# asm 2: adc <mulrdx=%rdx,<r2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? r2 += mulrax
+# asm 1: add <mulrax=int64#7,<r2=int64#13
+# asm 2: add <mulrax=%rax,<r2=%r15
+add %rax,%r15
+
+# qhasm: r3 = 0
+# asm 1: mov $0,>r3=int64#14
+# asm 2: mov $0,>r3=%rbx
+mov $0,%rbx
+
+# qhasm: r3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<r3=int64#14
+# asm 2: adc <mulrdx=%rdx,<r3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>mulx1=int64#10
+# asm 2: movq 8(<xp=%rsi),>mulx1=%r12
+movq 8(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? r1 += mulrax
+# asm 1: add <mulrax=int64#7,<r1=int64#12
+# asm 2: add <mulrax=%rax,<r1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? r2 += mulrax
+# asm 1: add <mulrax=int64#7,<r2=int64#13
+# asm 2: add <mulrax=%rax,<r2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? r2 += mulc
+# asm 1: add <mulc=int64#15,<r2=int64#13
+# asm 2: add <mulc=%rbp,<r2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? r3 += mulc
+# asm 1: add <mulc=int64#15,<r3=int64#14
+# asm 2: add <mulc=%rbp,<r3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>mulx2=int64#10
+# asm 2: movq 16(<xp=%rsi),>mulx2=%r12
+movq 16(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? r2 += mulrax
+# asm 1: add <mulrax=int64#7,<r2=int64#13
+# asm 2: add <mulrax=%rax,<r2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? r3 += mulc
+# asm 1: add <mulc=int64#15,<r3=int64#14
+# asm 2: add <mulc=%rbp,<r3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>mulx3=int64#2
+# asm 2: movq 24(<xp=%rsi),>mulx3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: mulrax = *(uint64 *)(yp + 0)
+# asm 1: movq 0(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<yp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? r3 += mulrax
+# asm 1: add <mulrax=int64#7,<r3=int64#14
+# asm 2: add <mulrax=%rax,<r3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 8)
+# asm 1: movq 8(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<yp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#10,<mulr4=int64#5
+# asm 2: add <mulc=%r12,<mulr4=%r8
+add %r12,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 16)
+# asm 1: movq 16(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<yp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#10,<mulr5=int64#6
+# asm 2: add <mulc=%r12,<mulr5=%r9
+add %r12,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(yp + 24)
+# asm 1: movq 24(<yp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<yp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#10,<mulr6=int64#8
+# asm 2: add <mulc=%r12,<mulr6=%r10
+add %r12,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? r0 += mulr4
+# asm 1: add <mulr4=int64#2,<r0=int64#11
+# asm 2: add <mulr4=%rsi,<r0=%r13
+add %rsi,%r13
+
+# qhasm: carry? r1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<r1=int64#12
+# asm 2: adc <mulr5=%rcx,<r1=%r14
+adc %rcx,%r14
+
+# qhasm: carry? r2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<r2=int64#13
+# asm 2: adc <mulr6=%r8,<r2=%r15
+adc %r8,%r15
+
+# qhasm: carry? r3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<r3=int64#14
+# asm 2: adc <mulr7=%r9,<r3=%rbx
+adc %r9,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? r0 += mulr8
+# asm 1: add <mulr8=int64#3,<r0=int64#11
+# asm 2: add <mulr8=%rdx,<r0=%r13
+add %rdx,%r13
+
+# qhasm: carry? r1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<r1=int64#12
+# asm 2: adc <mulzero=%rsi,<r1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? r2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<r2=int64#13
+# asm 2: adc <mulzero=%rsi,<r2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? r3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<r3=int64#14
+# asm 2: adc <mulzero=%rsi,<r3=%rbx
+adc %rsi,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: r0 += mulzero
+# asm 1: add <mulzero=int64#2,<r0=int64#11
+# asm 2: add <mulzero=%rsi,<r0=%r13
+add %rsi,%r13
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#12,8(<rp=int64#1)
+# asm 2: movq <r1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#13,16(<rp=int64#1)
+# asm 2: movq <r2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#14,24(<rp=int64#1)
+# asm 2: movq <r3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#11,0(<rp=int64#1)
+# asm 2: movq <r0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_neg.c b/ext/ed25519-amd64-asm/fe25519_neg.c
new file mode 100644
index 00000000..235b209d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_neg.c
@@ -0,0 +1,8 @@
+#include "fe25519.h"
+
+void fe25519_neg(fe25519 *r, const fe25519 *x)
+{
+ fe25519 t;
+ fe25519_setint(&t,0);
+ fe25519_sub(r,&t,x);
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_pack.c b/ext/ed25519-amd64-asm/fe25519_pack.c
new file mode 100644
index 00000000..caf51853
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_pack.c
@@ -0,0 +1,13 @@
+#include "fe25519.h"
+
+/* Assumes input x being reduced below 2^255 */
+void fe25519_pack(unsigned char r[32], const fe25519 *x)
+{
+ int i;
+ fe25519 t;
+ t = *x;
+ fe25519_freeze(&t);
+ /* assuming little-endian */
+ for(i=0;i<32;i++) r[i] = i[(unsigned char *)&t.v];
+}
+
diff --git a/ext/ed25519-amd64-asm/fe25519_pow2523.c b/ext/ed25519-amd64-asm/fe25519_pow2523.c
new file mode 100644
index 00000000..60042a0a
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_pow2523.c
@@ -0,0 +1,55 @@
+#include "fe25519.h"
+
+void fe25519_pow2523(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^252 - 3 */ fe25519_mul(r,&t,x);
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_setint.c b/ext/ed25519-amd64-asm/fe25519_setint.c
new file mode 100644
index 00000000..585c4bdd
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_setint.c
@@ -0,0 +1,9 @@
+#include "fe25519.h"
+
+void fe25519_setint(fe25519 *r, unsigned int v)
+{
+ r->v[0] = v;
+ r->v[1] = 0;
+ r->v[2] = 0;
+ r->v[3] = 0;
+}
diff --git a/ext/ed25519-amd64-asm/fe25519_square.s b/ext/ed25519-amd64-asm/fe25519_square.s
new file mode 100644
index 00000000..a74d9e88
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_square.s
@@ -0,0 +1,639 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 squarer4
+
+# qhasm: int64 squarer5
+
+# qhasm: int64 squarer6
+
+# qhasm: int64 squarer7
+
+# qhasm: int64 squarer8
+
+# qhasm: int64 squarerax
+
+# qhasm: int64 squarerdx
+
+# qhasm: int64 squaret1
+
+# qhasm: int64 squaret2
+
+# qhasm: int64 squaret3
+
+# qhasm: int64 squarec
+
+# qhasm: int64 squarezero
+
+# qhasm: int64 squarei38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_square
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_square
+.globl crypto_sign_ed25519_amd64_64_fe25519_square
+_crypto_sign_ed25519_amd64_64_fe25519_square:
+crypto_sign_ed25519_amd64_64_fe25519_square:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<xp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: r1 = squarerax
+# asm 1: mov <squarerax=int64#7,>r1=int64#5
+# asm 2: mov <squarerax=%rax,>r1=%r8
+mov %rax,%r8
+
+# qhasm: r2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>r2=int64#6
+# asm 2: mov <squarerdx=%rdx,>r2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<xp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 8)
+# asm 1: mulq 8(<xp=int64#2)
+# asm 2: mulq 8(<xp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: r3 = squarerax
+# asm 1: mov <squarerax=int64#7,>r3=int64#8
+# asm 2: mov <squarerax=%rax,>r3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 16)
+# asm 1: mulq 16(<xp=int64#2)
+# asm 2: mulq 16(<xp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<xp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? r2 += squarerax
+# asm 1: add <squarerax=int64#7,<r2=int64#6
+# asm 2: add <squarerax=%rax,<r2=%r9
+add %rax,%r9
+
+# qhasm: carry? r3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<r3=int64#8
+# asm 2: adc <squarerdx=%rdx,<r3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 8)
+# asm 1: mulq 8(<xp=int64#2)
+# asm 2: mulq 8(<xp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? r3 += squarerax
+# asm 1: add <squarerax=int64#7,<r3=int64#8
+# asm 2: add <squarerax=%rax,<r3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? r1 += r1
+# asm 1: add <r1=int64#5,<r1=int64#5
+# asm 2: add <r1=%r8,<r1=%r8
+add %r8,%r8
+
+# qhasm: carry? r2 += r2 + carry
+# asm 1: adc <r2=int64#6,<r2=int64#6
+# asm 2: adc <r2=%r9,<r2=%r9
+adc %r9,%r9
+
+# qhasm: carry? r3 += r3 + carry
+# asm 1: adc <r3=int64#8,<r3=int64#8
+# asm 2: adc <r3=%r10,<r3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 0(<xp=%rsi),>squarerax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 0)
+# asm 1: mulq 0(<xp=int64#2)
+# asm 2: mulq 0(<xp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: r0 = squarerax
+# asm 1: mov <squarerax=int64#7,>r0=int64#12
+# asm 2: mov <squarerax=%rax,>r0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<xp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 8)
+# asm 1: mulq 8(<xp=int64#2)
+# asm 2: mulq 8(<xp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<xp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 16)
+# asm 1: mulq 16(<xp=int64#2)
+# asm 2: mulq 16(<xp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: carry? r1 += squaret1
+# asm 1: add <squaret1=int64#13,<r1=int64#5
+# asm 2: add <squaret1=%r15,<r1=%r8
+add %r15,%r8
+
+# qhasm: carry? r2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<r2=int64#6
+# asm 2: adc <squaret2=%rbx,<r2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? r3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<r3=int64#8
+# asm 2: adc <squaret3=%rbp,<r3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<xp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(xp + 24)
+# asm 1: mulq 24(<xp=int64#2)
+# asm 2: mulq 24(<xp=%rsi)
+mulq 24(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#2
+# asm 2: mov <squarerax=%rax,>squarer4=%rsi
+mov %rax,%rsi
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r11
+mov %rdx,%r11
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#9
+# asm 2: add <squarerax=%rax,<squarer5=%r11
+add %rax,%r11
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#10
+# asm 2: mov $0,>squarer6=%r12
+mov $0,%r12
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r12
+adc %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#10
+# asm 2: add <squarerax=%rax,<squarer6=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? r0 += squarer4
+# asm 1: add <squarer4=int64#2,<r0=int64#12
+# asm 2: add <squarer4=%rsi,<r0=%r14
+add %rsi,%r14
+
+# qhasm: carry? r1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#9,<r1=int64#5
+# asm 2: adc <squarer5=%r11,<r1=%r8
+adc %r11,%r8
+
+# qhasm: carry? r2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#10,<r2=int64#6
+# asm 2: adc <squarer6=%r12,<r2=%r9
+adc %r12,%r9
+
+# qhasm: carry? r3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<r3=int64#8
+# asm 2: adc <squarer7=%rcx,<r3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#2
+# asm 2: mov $0,>squarezero=%rsi
+mov $0,%rsi
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarer8=int64#7
+# asm 2: adc <squarezero=%rsi,<squarer8=%rax
+adc %rsi,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#3
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? r0 += squarer8
+# asm 1: add <squarer8=int64#3,<r0=int64#12
+# asm 2: add <squarer8=%rdx,<r0=%r14
+add %rdx,%r14
+
+# qhasm: carry? r1 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<r1=int64#5
+# asm 2: adc <squarezero=%rsi,<r1=%r8
+adc %rsi,%r8
+
+# qhasm: carry? r2 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<r2=int64#6
+# asm 2: adc <squarezero=%rsi,<r2=%r9
+adc %rsi,%r9
+
+# qhasm: carry? r3 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<r3=int64#8
+# asm 2: adc <squarezero=%rsi,<r3=%r10
+adc %rsi,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarezero=int64#2
+# asm 2: adc <squarezero=%rsi,<squarezero=%rsi
+adc %rsi,%rsi
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#2,>squarezero=int64#2
+# asm 2: imulq $38,<squarezero=%rsi,>squarezero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: r0 += squarezero
+# asm 1: add <squarezero=int64#2,<r0=int64#12
+# asm 2: add <squarezero=%rsi,<r0=%r14
+add %rsi,%r14
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#8,24(<rp=int64#1)
+# asm 2: movq <r3=%r10,24(<rp=%rdi)
+movq %r10,24(%rdi)
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#12,0(<rp=int64#1)
+# asm 2: movq <r0=%r14,0(<rp=%rdi)
+movq %r14,0(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_sub.s b/ext/ed25519-amd64-asm/fe25519_sub.s
new file mode 100644
index 00000000..0b395bce
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_sub.s
@@ -0,0 +1,189 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_fe25519_sub
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_fe25519_sub
+.globl crypto_sign_ed25519_amd64_64_fe25519_sub
+_crypto_sign_ed25519_amd64_64_fe25519_sub:
+crypto_sign_ed25519_amd64_64_fe25519_sub:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 -= *(uint64 *)(yp + 0)
+# asm 1: subq 0(<yp=int64#3),<r0=int64#4
+# asm 2: subq 0(<yp=%rdx),<r0=%rcx
+subq 0(%rdx),%rcx
+
+# qhasm: carry? r1 -= *(uint64 *)(yp + 8) - carry
+# asm 1: sbbq 8(<yp=int64#3),<r1=int64#5
+# asm 2: sbbq 8(<yp=%rdx),<r1=%r8
+sbbq 8(%rdx),%r8
+
+# qhasm: carry? r2 -= *(uint64 *)(yp + 16) - carry
+# asm 1: sbbq 16(<yp=int64#3),<r2=int64#6
+# asm 2: sbbq 16(<yp=%rdx),<r2=%r9
+sbbq 16(%rdx),%r9
+
+# qhasm: carry? r3 -= *(uint64 *)(yp + 24) - carry
+# asm 1: sbbq 24(<yp=int64#3),<r3=int64#2
+# asm 2: sbbq 24(<yp=%rdx),<r3=%rsi
+sbbq 24(%rdx),%rsi
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#3
+# asm 2: mov $0,>subt0=%rdx
+mov $0,%rdx
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#7
+# asm 2: mov $38,>subt1=%rax
+mov $38,%rax
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#3,<subt1=int64#7
+# asm 2: cmovae <subt0=%rdx,<subt1=%rax
+cmovae %rdx,%rax
+
+# qhasm: carry? r0 -= subt1
+# asm 1: sub <subt1=int64#7,<r0=int64#4
+# asm 2: sub <subt1=%rax,<r0=%rcx
+sub %rax,%rcx
+
+# qhasm: carry? r1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<r1=int64#5
+# asm 2: sbb <subt0=%rdx,<r1=%r8
+sbb %rdx,%r8
+
+# qhasm: carry? r2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<r2=int64#6
+# asm 2: sbb <subt0=%rdx,<r2=%r9
+sbb %rdx,%r9
+
+# qhasm: carry? r3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<r3=int64#2
+# asm 2: sbb <subt0=%rdx,<r3=%rsi
+sbb %rdx,%rsi
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#7,<subt0=int64#3
+# asm 2: cmovc <subt1=%rax,<subt0=%rdx
+cmovc %rax,%rdx
+
+# qhasm: r0 -= subt0
+# asm 1: sub <subt0=int64#3,<r0=int64#4
+# asm 2: sub <subt0=%rdx,<r0=%rcx
+sub %rdx,%rcx
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/fe25519_unpack.c b/ext/ed25519-amd64-asm/fe25519_unpack.c
new file mode 100644
index 00000000..b3b0f4d5
--- /dev/null
+++ b/ext/ed25519-amd64-asm/fe25519_unpack.c
@@ -0,0 +1,11 @@
+#include "fe25519.h"
+
+void fe25519_unpack(fe25519 *r, const unsigned char x[32])
+{
+ /* assuming little-endian */
+ r->v[0] = *(unsigned long long *)x;
+ r->v[1] = *(((unsigned long long *)x)+1);
+ r->v[2] = *(((unsigned long long *)x)+2);
+ r->v[3] = *(((unsigned long long *)x)+3);
+ r->v[3] &= 0x7fffffffffffffffULL;
+}
diff --git a/ext/ed25519-amd64-asm/ge25519.h b/ext/ed25519-amd64-asm/ge25519.h
new file mode 100644
index 00000000..0b15136b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519.h
@@ -0,0 +1,95 @@
+#ifndef GE25519_H
+#define GE25519_H
+
+#include "fe25519.h"
+#include "sc25519.h"
+
+#define ge25519 crypto_sign_ed25519_amd64_64_ge25519
+#define ge25519_base crypto_sign_ed25519_amd64_64_ge25519_base
+#define ge25519_unpackneg_vartime crypto_sign_ed25519_amd64_64_unpackneg_vartime
+#define ge25519_pack crypto_sign_ed25519_amd64_64_pack
+#define ge25519_isneutral_vartime crypto_sign_ed25519_amd64_64_isneutral_vartime
+#define ge25519_add crypto_sign_ed25519_amd64_64_ge25519_add
+#define ge25519_double crypto_sign_ed25519_amd64_64_ge25519_double
+#define ge25519_double_scalarmult_vartime crypto_sign_ed25519_amd64_64_double_scalarmult_vartime
+#define ge25519_multi_scalarmult_vartime crypto_sign_ed25519_amd64_64_ge25519_multi_scalarmult_vartime
+#define ge25519_scalarmult_base crypto_sign_ed25519_amd64_64_scalarmult_base
+#define ge25519_p1p1_to_p2 crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+#define ge25519_p1p1_to_p3 crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+#define ge25519_add_p1p1 crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+#define ge25519_dbl_p1p1 crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+#define choose_t crypto_sign_ed25519_amd64_64_choose_t
+#define ge25519_nielsadd2 crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+#define ge25519_nielsadd_p1p1 crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+#define ge25519_pnielsadd_p1p1 crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+
+
+#define ge25519_p3 ge25519
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+ fe25519 t;
+} ge25519;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 z;
+ fe25519 y;
+ fe25519 t;
+} ge25519_p1p1;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+} ge25519_p2;
+
+typedef struct
+{
+ fe25519 ysubx;
+ fe25519 xaddy;
+ fe25519 t2d;
+} ge25519_niels;
+
+typedef struct
+{
+ fe25519 ysubx;
+ fe25519 xaddy;
+ fe25519 z;
+ fe25519 t2d;
+} ge25519_pniels;
+
+extern void ge25519_p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p);
+extern void ge25519_p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p);
+extern void ge25519_add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q);
+extern void ge25519_dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p);
+extern void choose_t(ge25519_niels *t, unsigned long long pos, signed long long b, const ge25519_niels *base_multiples);
+extern void ge25519_nielsadd2(ge25519_p3 *r, const ge25519_niels *q);
+extern void ge25519_nielsadd_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_niels *q);
+extern void ge25519_pnielsadd_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_pniels *q);
+
+extern const ge25519 ge25519_base;
+
+extern int ge25519_unpackneg_vartime(ge25519 *r, const unsigned char p[32]);
+
+extern void ge25519_pack(unsigned char r[32], const ge25519 *p);
+
+extern int ge25519_isneutral_vartime(const ge25519 *p);
+
+extern void ge25519_add(ge25519 *r, const ge25519 *p, const ge25519 *q);
+
+extern void ge25519_double(ge25519 *r, const ge25519 *p);
+
+/* computes [s1]p1 + [s2]ge25519_base */
+extern void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const sc25519 *s1, const sc25519 *s2);
+
+extern void ge25519_multi_scalarmult_vartime(ge25519 *r, ge25519 *p, sc25519 *s, const unsigned long long npoints);
+
+extern void ge25519_scalarmult_base(ge25519 *r, const sc25519 *s);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/ge25519_add.c b/ext/ed25519-amd64-asm/ge25519_add.c
new file mode 100644
index 00000000..c4d1c68a
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_add.c
@@ -0,0 +1,8 @@
+#include "ge25519.h"
+
+void ge25519_add(ge25519_p3 *r, const ge25519_p3 *p, const ge25519_p3 *q)
+{
+ ge25519_p1p1 grp1p1;
+ ge25519_add_p1p1(&grp1p1, p, q);
+ ge25519_p1p1_to_p3(r, &grp1p1);
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_add_p1p1.s b/ext/ed25519-amd64-asm/ge25519_add_p1p1.s
new file mode 100644
index 00000000..0cb13898
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_add_p1p1.s
@@ -0,0 +1,4554 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 x0
+
+# qhasm: int64 x1
+
+# qhasm: int64 x2
+
+# qhasm: int64 x3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_add_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_add_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_add_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $192,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: qp = qp
+# asm 1: mov <qp=int64#3,>qp=int64#4
+# asm 2: mov <qp=%rdx,>qp=%rcx
+mov %rdx,%rcx
+
+# qhasm: a0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>a0=int64#3
+# asm 2: movq 32(<pp=%rsi),>a0=%rdx
+movq 32(%rsi),%rdx
+
+# qhasm: a1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>a1=int64#5
+# asm 2: movq 40(<pp=%rsi),>a1=%r8
+movq 40(%rsi),%r8
+
+# qhasm: a2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>a2=int64#6
+# asm 2: movq 48(<pp=%rsi),>a2=%r9
+movq 48(%rsi),%r9
+
+# qhasm: a3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>a3=int64#7
+# asm 2: movq 56(<pp=%rsi),>a3=%rax
+movq 56(%rsi),%rax
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#8
+# asm 2: mov <a0=%rdx,>b0=%r10
+mov %rdx,%r10
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#5,>b1=int64#9
+# asm 2: mov <a1=%r8,>b1=%r11
+mov %r8,%r11
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#6,>b2=int64#10
+# asm 2: mov <a2=%r9,>b2=%r12
+mov %r9,%r12
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#7,>b3=int64#11
+# asm 2: mov <a3=%rax,>b3=%r13
+mov %rax,%r13
+
+# qhasm: carry? a0 -= *(uint64 *)(pp + 0)
+# asm 1: subq 0(<pp=int64#2),<a0=int64#3
+# asm 2: subq 0(<pp=%rsi),<a0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *)(pp + 8) - carry
+# asm 1: sbbq 8(<pp=int64#2),<a1=int64#5
+# asm 2: sbbq 8(<pp=%rsi),<a1=%r8
+sbbq 8(%rsi),%r8
+
+# qhasm: carry? a2 -= *(uint64 *)(pp + 16) - carry
+# asm 1: sbbq 16(<pp=int64#2),<a2=int64#6
+# asm 2: sbbq 16(<pp=%rsi),<a2=%r9
+sbbq 16(%rsi),%r9
+
+# qhasm: carry? a3 -= *(uint64 *)(pp + 24) - carry
+# asm 1: sbbq 24(<pp=int64#2),<a3=int64#7
+# asm 2: sbbq 24(<pp=%rsi),<a3=%rax
+sbbq 24(%rsi),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#13,<a0=int64#3
+# asm 2: sub <subt1=%r15,<a0=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a1=int64#5
+# asm 2: sbb <subt0=%r14,<a1=%r8
+sbb %r14,%r8
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a2=int64#6
+# asm 2: sbb <subt0=%r14,<a2=%r9
+sbb %r14,%r9
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a3=int64#7
+# asm 2: sbb <subt0=%r14,<a3=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#12,<a0=int64#3
+# asm 2: sub <subt0=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? b0 += *(uint64 *)(pp + 0)
+# asm 1: addq 0(<pp=int64#2),<b0=int64#8
+# asm 2: addq 0(<pp=%rsi),<b0=%r10
+addq 0(%rsi),%r10
+
+# qhasm: carry? b1 += *(uint64 *)(pp + 8) + carry
+# asm 1: adcq 8(<pp=int64#2),<b1=int64#9
+# asm 2: adcq 8(<pp=%rsi),<b1=%r11
+adcq 8(%rsi),%r11
+
+# qhasm: carry? b2 += *(uint64 *)(pp + 16) + carry
+# asm 1: adcq 16(<pp=int64#2),<b2=int64#10
+# asm 2: adcq 16(<pp=%rsi),<b2=%r12
+adcq 16(%rsi),%r12
+
+# qhasm: carry? b3 += *(uint64 *)(pp + 24) + carry
+# asm 1: adcq 24(<pp=int64#2),<b3=int64#11
+# asm 2: adcq 24(<pp=%rsi),<b3=%r13
+adcq 24(%rsi),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#13,<b0=int64#8
+# asm 2: add <addt1=%r15,<b0=%r10
+add %r15,%r10
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b1=int64#9
+# asm 2: adc <addt0=%r14,<b1=%r11
+adc %r14,%r11
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b2=int64#10
+# asm 2: adc <addt0=%r14,<b2=%r12
+adc %r14,%r12
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b3=int64#11
+# asm 2: adc <addt0=%r14,<b3=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#12,<b0=int64#8
+# asm 2: add <addt0=%r14,<b0=%r10
+add %r14,%r10
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#7,>a3_stack=stack64#11
+# asm 2: movq <a3=%rax,>a3_stack=80(%rsp)
+movq %rax,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#8,>b0_stack=stack64#12
+# asm 2: movq <b0=%r10,>b0_stack=88(%rsp)
+movq %r10,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#9,>b1_stack=stack64#13
+# asm 2: movq <b1=%r11,>b1_stack=96(%rsp)
+movq %r11,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#10,>b2_stack=stack64#14
+# asm 2: movq <b2=%r12,>b2_stack=104(%rsp)
+movq %r12,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#11,>b3_stack=stack64#15
+# asm 2: movq <b3=%r13,>b3_stack=112(%rsp)
+movq %r13,112(%rsp)
+
+# qhasm: t10 = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>t10=int64#3
+# asm 2: movq 32(<qp=%rcx),>t10=%rdx
+movq 32(%rcx),%rdx
+
+# qhasm: t11 = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>t11=int64#5
+# asm 2: movq 40(<qp=%rcx),>t11=%r8
+movq 40(%rcx),%r8
+
+# qhasm: t12 = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>t12=int64#6
+# asm 2: movq 48(<qp=%rcx),>t12=%r9
+movq 48(%rcx),%r9
+
+# qhasm: t13 = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>t13=int64#7
+# asm 2: movq 56(<qp=%rcx),>t13=%rax
+movq 56(%rcx),%rax
+
+# qhasm: t20 = t10
+# asm 1: mov <t10=int64#3,>t20=int64#8
+# asm 2: mov <t10=%rdx,>t20=%r10
+mov %rdx,%r10
+
+# qhasm: t21 = t11
+# asm 1: mov <t11=int64#5,>t21=int64#9
+# asm 2: mov <t11=%r8,>t21=%r11
+mov %r8,%r11
+
+# qhasm: t22 = t12
+# asm 1: mov <t12=int64#6,>t22=int64#10
+# asm 2: mov <t12=%r9,>t22=%r12
+mov %r9,%r12
+
+# qhasm: t23 = t13
+# asm 1: mov <t13=int64#7,>t23=int64#11
+# asm 2: mov <t13=%rax,>t23=%r13
+mov %rax,%r13
+
+# qhasm: carry? t10 -= *(uint64 *) (qp + 0)
+# asm 1: subq 0(<qp=int64#4),<t10=int64#3
+# asm 2: subq 0(<qp=%rcx),<t10=%rdx
+subq 0(%rcx),%rdx
+
+# qhasm: carry? t11 -= *(uint64 *) (qp + 8) - carry
+# asm 1: sbbq 8(<qp=int64#4),<t11=int64#5
+# asm 2: sbbq 8(<qp=%rcx),<t11=%r8
+sbbq 8(%rcx),%r8
+
+# qhasm: carry? t12 -= *(uint64 *) (qp + 16) - carry
+# asm 1: sbbq 16(<qp=int64#4),<t12=int64#6
+# asm 2: sbbq 16(<qp=%rcx),<t12=%r9
+sbbq 16(%rcx),%r9
+
+# qhasm: carry? t13 -= *(uint64 *) (qp + 24) - carry
+# asm 1: sbbq 24(<qp=int64#4),<t13=int64#7
+# asm 2: sbbq 24(<qp=%rcx),<t13=%rax
+sbbq 24(%rcx),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? t10 -= subt1
+# asm 1: sub <subt1=int64#13,<t10=int64#3
+# asm 2: sub <subt1=%r15,<t10=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? t11 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<t11=int64#5
+# asm 2: sbb <subt0=%r14,<t11=%r8
+sbb %r14,%r8
+
+# qhasm: carry? t12 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<t12=int64#6
+# asm 2: sbb <subt0=%r14,<t12=%r9
+sbb %r14,%r9
+
+# qhasm: carry? t13 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<t13=int64#7
+# asm 2: sbb <subt0=%r14,<t13=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: t10 -= subt0
+# asm 1: sub <subt0=int64#12,<t10=int64#3
+# asm 2: sub <subt0=%r14,<t10=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? t20 += *(uint64 *) (qp + 0)
+# asm 1: addq 0(<qp=int64#4),<t20=int64#8
+# asm 2: addq 0(<qp=%rcx),<t20=%r10
+addq 0(%rcx),%r10
+
+# qhasm: carry? t21 += *(uint64 *) (qp + 8) + carry
+# asm 1: adcq 8(<qp=int64#4),<t21=int64#9
+# asm 2: adcq 8(<qp=%rcx),<t21=%r11
+adcq 8(%rcx),%r11
+
+# qhasm: carry? t22 += *(uint64 *) (qp + 16) + carry
+# asm 1: adcq 16(<qp=int64#4),<t22=int64#10
+# asm 2: adcq 16(<qp=%rcx),<t22=%r12
+adcq 16(%rcx),%r12
+
+# qhasm: carry? t23 += *(uint64 *) (qp + 24) + carry
+# asm 1: adcq 24(<qp=int64#4),<t23=int64#11
+# asm 2: adcq 24(<qp=%rcx),<t23=%r13
+adcq 24(%rcx),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? t20 += addt1
+# asm 1: add <addt1=int64#13,<t20=int64#8
+# asm 2: add <addt1=%r15,<t20=%r10
+add %r15,%r10
+
+# qhasm: carry? t21 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<t21=int64#9
+# asm 2: adc <addt0=%r14,<t21=%r11
+adc %r14,%r11
+
+# qhasm: carry? t22 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<t22=int64#10
+# asm 2: adc <addt0=%r14,<t22=%r12
+adc %r14,%r12
+
+# qhasm: carry? t23 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<t23=int64#11
+# asm 2: adc <addt0=%r14,<t23=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: t20 += addt0
+# asm 1: add <addt0=int64#12,<t20=int64#8
+# asm 2: add <addt0=%r14,<t20=%r10
+add %r14,%r10
+
+# qhasm: t10_stack = t10
+# asm 1: movq <t10=int64#3,>t10_stack=stack64#16
+# asm 2: movq <t10=%rdx,>t10_stack=120(%rsp)
+movq %rdx,120(%rsp)
+
+# qhasm: t11_stack = t11
+# asm 1: movq <t11=int64#5,>t11_stack=stack64#17
+# asm 2: movq <t11=%r8,>t11_stack=128(%rsp)
+movq %r8,128(%rsp)
+
+# qhasm: t12_stack = t12
+# asm 1: movq <t12=int64#6,>t12_stack=stack64#18
+# asm 2: movq <t12=%r9,>t12_stack=136(%rsp)
+movq %r9,136(%rsp)
+
+# qhasm: t13_stack = t13
+# asm 1: movq <t13=int64#7,>t13_stack=stack64#19
+# asm 2: movq <t13=%rax,>t13_stack=144(%rsp)
+movq %rax,144(%rsp)
+
+# qhasm: t20_stack = t20
+# asm 1: movq <t20=int64#8,>t20_stack=stack64#20
+# asm 2: movq <t20=%r10,>t20_stack=152(%rsp)
+movq %r10,152(%rsp)
+
+# qhasm: t21_stack = t21
+# asm 1: movq <t21=int64#9,>t21_stack=stack64#21
+# asm 2: movq <t21=%r11,>t21_stack=160(%rsp)
+movq %r11,160(%rsp)
+
+# qhasm: t22_stack = t22
+# asm 1: movq <t22=int64#10,>t22_stack=stack64#22
+# asm 2: movq <t22=%r12,>t22_stack=168(%rsp)
+movq %r12,168(%rsp)
+
+# qhasm: t23_stack = t23
+# asm 1: movq <t23=int64#11,>t23_stack=stack64#23
+# asm 2: movq <t23=%r13,>t23_stack=176(%rsp)
+movq %r13,176(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#11
+# asm 2: mov <mulrax=%rax,>a0=%r13
+mov %rax,%r13
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#12
+# asm 2: mov <mulrdx=%rdx,>a1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#13
+# asm 2: mov $0,>a2=%r15
+mov $0,%r15
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#13
+# asm 2: adc <mulrdx=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#14
+# asm 2: mov $0,>a3=%rbx
+mov $0,%rbx
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#14
+# asm 2: adc <mulrdx=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#15,<a2=int64#13
+# asm 2: add <mulc=%rbp,<a2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = t10_stack
+# asm 1: movq <t10_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <t10_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t11_stack
+# asm 1: movq <t11_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <t11_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t12_stack
+# asm 1: movq <t12_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <t12_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t13_stack
+# asm 1: movq <t13_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <t13_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#5,<a0=int64#11
+# asm 2: add <mulr4=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<a1=int64#12
+# asm 2: adc <mulr5=%r9,<a1=%r14
+adc %r9,%r14
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<a2=int64#13
+# asm 2: adc <mulr6=%r10,<a2=%r15
+adc %r10,%r15
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<a3=int64#14
+# asm 2: adc <mulr7=%r11,<a3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#5,<a0=int64#11
+# asm 2: add <mulr8=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#12
+# asm 2: adc <mulzero=%rdx,<a1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#13
+# asm 2: adc <mulzero=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#14
+# asm 2: adc <mulzero=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#11
+# asm 2: add <mulzero=%rdx,<a0=%r13
+add %rdx,%r13
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#11,>a0_stack=stack64#8
+# asm 2: movq <a0=%r13,>a0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#12,>a1_stack=stack64#9
+# asm 2: movq <a1=%r14,>a1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#13,>a2_stack=stack64#10
+# asm 2: movq <a2=%r15,>a2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#14,>a3_stack=stack64#11
+# asm 2: movq <a3=%rbx,>a3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#10
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r12
+movq 88(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#11
+# asm 2: mov <mulrax=%rax,>rx0=%r13
+mov %rax,%r13
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rx1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#13
+# asm 2: mov $0,>rx2=%r15
+mov $0,%r15
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#14
+# asm 2: mov $0,>rx3=%rbx
+mov $0,%rbx
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#10
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r12
+movq 96(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#15,<rx2=int64#13
+# asm 2: add <mulc=%rbp,<rx2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#10
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r12
+movq 104(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#10
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r12
+movq 112(%rsp),%r12
+
+# qhasm: mulrax = t20_stack
+# asm 1: movq <t20_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <t20_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t21_stack
+# asm 1: movq <t21_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <t21_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t22_stack
+# asm 1: movq <t22_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <t22_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = t23_stack
+# asm 1: movq <t23_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <t23_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#5,<rx0=int64#11
+# asm 2: add <mulr4=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<rx1=int64#12
+# asm 2: adc <mulr5=%r9,<rx1=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<rx2=int64#13
+# asm 2: adc <mulr6=%r10,<rx2=%r15
+adc %r10,%r15
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<rx3=int64#14
+# asm 2: adc <mulr7=%r11,<rx3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#5,<rx0=int64#11
+# asm 2: add <mulr8=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#12
+# asm 2: adc <mulzero=%rdx,<rx1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#13
+# asm 2: adc <mulzero=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#14
+# asm 2: adc <mulzero=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#11
+# asm 2: add <mulzero=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: ry0 = rx0
+# asm 1: mov <rx0=int64#11,>ry0=int64#3
+# asm 2: mov <rx0=%r13,>ry0=%rdx
+mov %r13,%rdx
+
+# qhasm: ry1 = rx1
+# asm 1: mov <rx1=int64#12,>ry1=int64#5
+# asm 2: mov <rx1=%r14,>ry1=%r8
+mov %r14,%r8
+
+# qhasm: ry2 = rx2
+# asm 1: mov <rx2=int64#13,>ry2=int64#6
+# asm 2: mov <rx2=%r15,>ry2=%r9
+mov %r15,%r9
+
+# qhasm: ry3 = rx3
+# asm 1: mov <rx3=int64#14,>ry3=int64#7
+# asm 2: mov <rx3=%rbx,>ry3=%rax
+mov %rbx,%rax
+
+# qhasm: carry? ry0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<ry0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<ry0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? ry1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<ry1=int64#5
+# asm 2: adcq <a1_stack=64(%rsp),<ry1=%r8
+adcq 64(%rsp),%r8
+
+# qhasm: carry? ry2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<ry2=int64#6
+# asm 2: adcq <a2_stack=72(%rsp),<ry2=%r9
+adcq 72(%rsp),%r9
+
+# qhasm: carry? ry3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<ry3=int64#7
+# asm 2: adcq <a3_stack=80(%rsp),<ry3=%rax
+adcq 80(%rsp),%rax
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#8
+# asm 2: mov $0,>addt0=%r10
+mov $0,%r10
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#9
+# asm 2: mov $38,>addt1=%r11
+mov $38,%r11
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#8,<addt1=int64#9
+# asm 2: cmovae <addt0=%r10,<addt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? ry0 += addt1
+# asm 1: add <addt1=int64#9,<ry0=int64#3
+# asm 2: add <addt1=%r11,<ry0=%rdx
+add %r11,%rdx
+
+# qhasm: carry? ry1 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry1=int64#5
+# asm 2: adc <addt0=%r10,<ry1=%r8
+adc %r10,%r8
+
+# qhasm: carry? ry2 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry2=int64#6
+# asm 2: adc <addt0=%r10,<ry2=%r9
+adc %r10,%r9
+
+# qhasm: carry? ry3 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry3=int64#7
+# asm 2: adc <addt0=%r10,<ry3=%rax
+adc %r10,%rax
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#9,<addt0=int64#8
+# asm 2: cmovc <addt1=%r11,<addt0=%r10
+cmovc %r11,%r10
+
+# qhasm: ry0 += addt0
+# asm 1: add <addt0=int64#8,<ry0=int64#3
+# asm 2: add <addt0=%r10,<ry0=%rdx
+add %r10,%rdx
+
+# qhasm: carry? rx0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<rx0=int64#11
+# asm 2: subq <a0_stack=56(%rsp),<rx0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rx1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<rx1=int64#12
+# asm 2: sbbq <a1_stack=64(%rsp),<rx1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rx2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<rx2=int64#13
+# asm 2: sbbq <a2_stack=72(%rsp),<rx2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rx3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<rx3=int64#14
+# asm 2: sbbq <a3_stack=80(%rsp),<rx3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#8
+# asm 2: mov $0,>subt0=%r10
+mov $0,%r10
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#9
+# asm 2: mov $38,>subt1=%r11
+mov $38,%r11
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#8,<subt1=int64#9
+# asm 2: cmovae <subt0=%r10,<subt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? rx0 -= subt1
+# asm 1: sub <subt1=int64#9,<rx0=int64#11
+# asm 2: sub <subt1=%r11,<rx0=%r13
+sub %r11,%r13
+
+# qhasm: carry? rx1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx1=int64#12
+# asm 2: sbb <subt0=%r10,<rx1=%r14
+sbb %r10,%r14
+
+# qhasm: carry? rx2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx2=int64#13
+# asm 2: sbb <subt0=%r10,<rx2=%r15
+sbb %r10,%r15
+
+# qhasm: carry? rx3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx3=int64#14
+# asm 2: sbb <subt0=%r10,<rx3=%rbx
+sbb %r10,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#9,<subt0=int64#8
+# asm 2: cmovc <subt1=%r11,<subt0=%r10
+cmovc %r11,%r10
+
+# qhasm: rx0 -= subt0
+# asm 1: sub <subt0=int64#8,<rx0=int64#11
+# asm 2: sub <subt0=%r10,<rx0=%r13
+sub %r10,%r13
+
+# qhasm: *(uint64 *) (rp + 0) = rx0
+# asm 1: movq <rx0=int64#11,0(<rp=int64#1)
+# asm 2: movq <rx0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *) (rp + 8) = rx1
+# asm 1: movq <rx1=int64#12,8(<rp=int64#1)
+# asm 2: movq <rx1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *) (rp + 16) = rx2
+# asm 1: movq <rx2=int64#13,16(<rp=int64#1)
+# asm 2: movq <rx2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *) (rp + 24) = rx3
+# asm 1: movq <rx3=int64#14,24(<rp=int64#1)
+# asm 2: movq <rx3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: *(uint64 *) (rp + 64) = ry0
+# asm 1: movq <ry0=int64#3,64(<rp=int64#1)
+# asm 2: movq <ry0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *) (rp + 72) = ry1
+# asm 1: movq <ry1=int64#5,72(<rp=int64#1)
+# asm 2: movq <ry1=%r8,72(<rp=%rdi)
+movq %r8,72(%rdi)
+
+# qhasm: *(uint64 *) (rp + 80) = ry2
+# asm 1: movq <ry2=int64#6,80(<rp=int64#1)
+# asm 2: movq <ry2=%r9,80(<rp=%rdi)
+movq %r9,80(%rdi)
+
+# qhasm: *(uint64 *) (rp + 88) = ry3
+# asm 1: movq <ry3=int64#7,88(<rp=int64#1)
+# asm 2: movq <ry3=%rax,88(<rp=%rdi)
+movq %rax,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 96(<pp=%rsi),>mulx0=%r12
+movq 96(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 104(<pp=%rsi),>mulx1=%r12
+movq 104(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 112(<pp=%rsi),>mulx2=%r12
+movq 112(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulx3=int64#10
+# asm 2: movq 120(<pp=%rsi),>mulx3=%r12
+movq 120(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#5,<c0=int64#11
+# asm 2: add <mulr4=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<c1=int64#12
+# asm 2: adc <mulr5=%r9,<c1=%r14
+adc %r9,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<c2=int64#13
+# asm 2: adc <mulr6=%r10,<c2=%r15
+adc %r10,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<c3=int64#14
+# asm 2: adc <mulr7=%r11,<c3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#5,<c0=int64#11
+# asm 2: add <mulr8=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#11,>c0_stack=stack64#8
+# asm 2: movq <c0=%r13,>c0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#12,>c1_stack=stack64#9
+# asm 2: movq <c1=%r14,>c1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#13,>c2_stack=stack64#10
+# asm 2: movq <c2=%r15,>c2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#14,>c3_stack=stack64#11
+# asm 2: movq <c3=%rbx,>c3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = c0_stack
+# asm 1: movq <c0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <c0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = c1_stack
+# asm 1: movq <c1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <c1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = c2_stack
+# asm 1: movq <c2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <c2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = c3_stack
+# asm 1: movq <c3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <c3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D0
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D0,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D0(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D1
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D1,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D1(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D2
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D2,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D2(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)&crypto_sign_ed25519_amd64_64_EC2D3
+# asm 1: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=int64#7
+# asm 2: movq crypto_sign_ed25519_amd64_64_EC2D3,>mulrax=%rax
+movq crypto_sign_ed25519_amd64_64_EC2D3(%rip),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#5,<c0=int64#11
+# asm 2: add <mulr4=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<c1=int64#12
+# asm 2: adc <mulr5=%r9,<c1=%r14
+adc %r9,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<c2=int64#13
+# asm 2: adc <mulr6=%r10,<c2=%r15
+adc %r10,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<c3=int64#14
+# asm 2: adc <mulr7=%r11,<c3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#5,<c0=int64#11
+# asm 2: add <mulr8=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#11,>c0_stack=stack64#8
+# asm 2: movq <c0=%r13,>c0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#12,>c1_stack=stack64#9
+# asm 2: movq <c1=%r14,>c1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#13,>c2_stack=stack64#10
+# asm 2: movq <c2=%r15,>c2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#14,>c3_stack=stack64#11
+# asm 2: movq <c3=%rbx,>c3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r12
+movq 64(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#11
+# asm 2: mov <mulrax=%rax,>rt0=%r13
+mov %rax,%r13
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rt1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#13
+# asm 2: mov $0,>rt2=%r15
+mov $0,%r15
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rt2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#14
+# asm 2: mov $0,>rt3=%rbx
+mov $0,%rbx
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rt3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r12
+movq 72(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#15,<rt2=int64#13
+# asm 2: add <mulc=%rbp,<rt2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r12
+movq 80(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#2
+# asm 2: movq 88(<pp=%rsi),>mulx3=%rsi
+movq 88(%rsi),%rsi
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#10,<mulr4=int64#5
+# asm 2: add <mulc=%r12,<mulr4=%r8
+add %r12,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#10,<mulr5=int64#6
+# asm 2: add <mulc=%r12,<mulr5=%r9
+add %r12,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#10,<mulr6=int64#8
+# asm 2: add <mulc=%r12,<mulr6=%r10
+add %r12,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#11
+# asm 2: add <mulr4=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#12
+# asm 2: adc <mulr5=%rcx,<rt1=%r14
+adc %rcx,%r14
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#13
+# asm 2: adc <mulr6=%r8,<rt2=%r15
+adc %r8,%r15
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#14
+# asm 2: adc <mulr7=%r9,<rt3=%rbx
+adc %r9,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#11
+# asm 2: add <mulr8=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#12
+# asm 2: adc <mulzero=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#13
+# asm 2: adc <mulzero=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#14
+# asm 2: adc <mulzero=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#11
+# asm 2: add <mulzero=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt0 += rt0
+# asm 1: add <rt0=int64#11,<rt0=int64#11
+# asm 2: add <rt0=%r13,<rt0=%r13
+add %r13,%r13
+
+# qhasm: carry? rt1 += rt1 + carry
+# asm 1: adc <rt1=int64#12,<rt1=int64#12
+# asm 2: adc <rt1=%r14,<rt1=%r14
+adc %r14,%r14
+
+# qhasm: carry? rt2 += rt2 + carry
+# asm 1: adc <rt2=int64#13,<rt2=int64#13
+# asm 2: adc <rt2=%r15,<rt2=%r15
+adc %r15,%r15
+
+# qhasm: carry? rt3 += rt3 + carry
+# asm 1: adc <rt3=int64#14,<rt3=int64#14
+# asm 2: adc <rt3=%rbx,<rt3=%rbx
+adc %rbx,%rbx
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rt0 += addt1
+# asm 1: add <addt1=int64#3,<rt0=int64#11
+# asm 2: add <addt1=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt1=int64#12
+# asm 2: adc <addt0=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt2=int64#13
+# asm 2: adc <addt0=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt3=int64#14
+# asm 2: adc <addt0=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rt0 += addt0
+# asm 1: add <addt0=int64#2,<rt0=int64#11
+# asm 2: add <addt0=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: rz0 = rt0
+# asm 1: mov <rt0=int64#11,>rz0=int64#2
+# asm 2: mov <rt0=%r13,>rz0=%rsi
+mov %r13,%rsi
+
+# qhasm: rz1 = rt1
+# asm 1: mov <rt1=int64#12,>rz1=int64#3
+# asm 2: mov <rt1=%r14,>rz1=%rdx
+mov %r14,%rdx
+
+# qhasm: rz2 = rt2
+# asm 1: mov <rt2=int64#13,>rz2=int64#4
+# asm 2: mov <rt2=%r15,>rz2=%rcx
+mov %r15,%rcx
+
+# qhasm: rz3 = rt3
+# asm 1: mov <rt3=int64#14,>rz3=int64#5
+# asm 2: mov <rt3=%rbx,>rz3=%r8
+mov %rbx,%r8
+
+# qhasm: carry? rz0 += c0_stack
+# asm 1: addq <c0_stack=stack64#8,<rz0=int64#2
+# asm 2: addq <c0_stack=56(%rsp),<rz0=%rsi
+addq 56(%rsp),%rsi
+
+# qhasm: carry? rz1 += c1_stack + carry
+# asm 1: adcq <c1_stack=stack64#9,<rz1=int64#3
+# asm 2: adcq <c1_stack=64(%rsp),<rz1=%rdx
+adcq 64(%rsp),%rdx
+
+# qhasm: carry? rz2 += c2_stack + carry
+# asm 1: adcq <c2_stack=stack64#10,<rz2=int64#4
+# asm 2: adcq <c2_stack=72(%rsp),<rz2=%rcx
+adcq 72(%rsp),%rcx
+
+# qhasm: carry? rz3 += c3_stack + carry
+# asm 1: adcq <c3_stack=stack64#11,<rz3=int64#5
+# asm 2: adcq <c3_stack=80(%rsp),<rz3=%r8
+adcq 80(%rsp),%r8
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rz0 += addt1
+# asm 1: add <addt1=int64#7,<rz0=int64#2
+# asm 2: add <addt1=%rax,<rz0=%rsi
+add %rax,%rsi
+
+# qhasm: carry? rz1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz1=int64#3
+# asm 2: adc <addt0=%r9,<rz1=%rdx
+adc %r9,%rdx
+
+# qhasm: carry? rz2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz2=int64#4
+# asm 2: adc <addt0=%r9,<rz2=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? rz3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz3=int64#5
+# asm 2: adc <addt0=%r9,<rz3=%r8
+adc %r9,%r8
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rz0 += addt0
+# asm 1: add <addt0=int64#6,<rz0=int64#2
+# asm 2: add <addt0=%r9,<rz0=%rsi
+add %r9,%rsi
+
+# qhasm: carry? rt0 -= c0_stack
+# asm 1: subq <c0_stack=stack64#8,<rt0=int64#11
+# asm 2: subq <c0_stack=56(%rsp),<rt0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rt1 -= c1_stack - carry
+# asm 1: sbbq <c1_stack=stack64#9,<rt1=int64#12
+# asm 2: sbbq <c1_stack=64(%rsp),<rt1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rt2 -= c2_stack - carry
+# asm 1: sbbq <c2_stack=stack64#10,<rt2=int64#13
+# asm 2: sbbq <c2_stack=72(%rsp),<rt2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rt3 -= c3_stack - carry
+# asm 1: sbbq <c3_stack=stack64#11,<rt3=int64#14
+# asm 2: sbbq <c3_stack=80(%rsp),<rt3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#6
+# asm 2: mov $0,>subt0=%r9
+mov $0,%r9
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#7
+# asm 2: mov $38,>subt1=%rax
+mov $38,%rax
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#6,<subt1=int64#7
+# asm 2: cmovae <subt0=%r9,<subt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rt0 -= subt1
+# asm 1: sub <subt1=int64#7,<rt0=int64#11
+# asm 2: sub <subt1=%rax,<rt0=%r13
+sub %rax,%r13
+
+# qhasm: carry? rt1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt1=int64#12
+# asm 2: sbb <subt0=%r9,<rt1=%r14
+sbb %r9,%r14
+
+# qhasm: carry? rt2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt2=int64#13
+# asm 2: sbb <subt0=%r9,<rt2=%r15
+sbb %r9,%r15
+
+# qhasm: carry? rt3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt3=int64#14
+# asm 2: sbb <subt0=%r9,<rt3=%rbx
+sbb %r9,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#7,<subt0=int64#6
+# asm 2: cmovc <subt1=%rax,<subt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rt0 -= subt0
+# asm 1: sub <subt0=int64#6,<rt0=int64#11
+# asm 2: sub <subt0=%r9,<rt0=%r13
+sub %r9,%r13
+
+# qhasm: *(uint64 *)(rp + 32) = rz0
+# asm 1: movq <rz0=int64#2,32(<rp=int64#1)
+# asm 2: movq <rz0=%rsi,32(<rp=%rdi)
+movq %rsi,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = rz1
+# asm 1: movq <rz1=int64#3,40(<rp=int64#1)
+# asm 2: movq <rz1=%rdx,40(<rp=%rdi)
+movq %rdx,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = rz2
+# asm 1: movq <rz2=int64#4,48(<rp=int64#1)
+# asm 2: movq <rz2=%rcx,48(<rp=%rdi)
+movq %rcx,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = rz3
+# asm 1: movq <rz3=int64#5,56(<rp=int64#1)
+# asm 2: movq <rz3=%r8,56(<rp=%rdi)
+movq %r8,56(%rdi)
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#11,96(<rp=int64#1)
+# asm 2: movq <rt0=%r13,96(<rp=%rdi)
+movq %r13,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#12,104(<rp=int64#1)
+# asm 2: movq <rt1=%r14,104(<rp=%rdi)
+movq %r14,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#13,112(<rp=int64#1)
+# asm 2: movq <rt2=%r15,112(<rp=%rdi)
+movq %r15,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#14,120(<rp=int64#1)
+# asm 2: movq <rt3=%rbx,120(<rp=%rdi)
+movq %rbx,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_base.c b/ext/ed25519-amd64-asm/ge25519_base.c
new file mode 100644
index 00000000..a7ae9786
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base.c
@@ -0,0 +1,7 @@
+#include "ge25519.h"
+
+const ge25519 ge25519_base = {{{0xC9562D608F25D51A, 0x692CC7609525A7B2, 0xC0A4E231FDD6DC5C, 0x216936D3CD6E53FE}},
+ {{0x6666666666666658, 0x6666666666666666, 0x6666666666666666, 0x6666666666666666}},
+ {{0x0000000000000001, 0x0000000000000000, 0x0000000000000000, 000000000000000000}},
+ {{0x6DDE8AB3A5B7DDA3, 0x20F09F80775152F5, 0x66EA4E8E64ABE37D, 0x67875F0FD78B7665}}};
+
diff --git a/ext/ed25519-amd64-asm/ge25519_base_niels.data b/ext/ed25519-amd64-asm/ge25519_base_niels.data
new file mode 100644
index 00000000..8e3300cf
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base_niels.data
@@ -0,0 +1,1536 @@
+{{{0x9d103905d740913e, 0xfd399f05d140beb3, 0xa5c18434688f8a09, 0x44fd2f9298f81267}},
+ {{0x2fbc93c6f58c3b85, 0xcf932dc6fb8c0e19, 0x270b4898643d42c2, 0x07cf9d3a33d4ba65}},
+ {{0xdbbd15674b6fbb59, 0x41e13f00eea2a5ea, 0xcdd49d1cc957c6fa, 0x4f0ebe1faf16ecca}}},
+{{{0x8a99a56042b4d5a8, 0x8f2b810c4e60acf6, 0xe09e236bb16e37aa, 0x6bb595a669c92555}},
+ {{0x9224e7fc933c71d7, 0x9f469d967a0ff5b5, 0x5aa69a65e1d60702, 0x590c063fa87d2e2e}},
+ {{0x6e347eaadad36802, 0xbaf3599383ee4805, 0x3bcabe10e6076826, 0x49314f0a165ed1b8}}},
+{{{0x56611fe8a4fcd265, 0x3bd353fde5c1ba7d, 0x8131f31a214bd6bd, 0x2ab91587555bda62}},
+ {{0xaf25b0a84cee9730, 0x025a8430e8864b8a, 0xc11b50029f016732, 0x7a164e1b9a80f8f4}},
+ {{0x9bf211f4f1674834, 0xb84e6b17f62df895, 0xd7de6f075b722a4e, 0x549a04b963bb2a21}}},
+{{{0x95fe050a056818bf, 0x327e89715660faa9, 0xc3e8e3cd06a05073, 0x27933f4c7445a49a}},
+ {{0x287351b98efc099f, 0x6765c6f47dfd2538, 0xca348d3dfb0a9265, 0x680e910321e58727}},
+ {{0xbf1e45ece51426b0, 0xe32bc63d6dba0f94, 0xe42974d58cf852c0, 0x44f079b1b0e64c18}}},
+{{{0x7f9182c3a447d6ba, 0xd50014d14b2729b7, 0xe33cf11cb864a087, 0x154a7e73eb1b55f3}},
+ {{0xa212bc4408a5bb33, 0x8d5048c3c75eed02, 0xdd1beb0c5abfec44, 0x2945ccf146e206eb}},
+ {{0xc832a179e7d003b3, 0x5f729d0a00124d7e, 0x62c1d4a10e6d8ff3, 0x68b8ac5938b27a98}}},
+{{{0x499806b67b7d8ca4, 0x575be28427d22739, 0xbb085ce7204553b9, 0x38b64c41ae417884}},
+ {{0x3a0ceeeb77157131, 0x9b27158900c8af88, 0x8065b668da59a736, 0x51e57bb6a2cc38bd}},
+ {{0x8f9dad91689de3a4, 0x175f2428f8fb9137, 0x050ab5329fcfb988, 0x7865dfa21354c09f}}},
+{{{0xba6f2c9aaa3221b1, 0x6ca021533bba23a7, 0x9dea764f92192c3a, 0x1d6edd5d2e5317e0}},
+ {{0x6b1a5cd0944ea3bf, 0x7470353ab39dc0d2, 0x71b2528228542e49, 0x461bea69283c927e}},
+ {{0x217a8aacab0fda36, 0xa528c6543d3549c8, 0x37d05b8b13ab7568, 0x233cef623a2cbc37}}},
+{{{0xe2a75dedf39234d9, 0x963d7680e1b558f9, 0x2c2741ac6e3c23fb, 0x3a9024a1320e01c3}},
+ {{0x59b7596604dd3e8f, 0x6cb30377e288702c, 0xb1339c665ed9c323, 0x0915e76061bce52f}},
+ {{0xdf7de835a834a37e, 0x8be19cda689857ea, 0x2c1185367167b326, 0x589eb3d9dbefd5c2}}},
+{{{0x7ec851ca553e2df3, 0xa71284cba64878b3, 0xe6b5e4193288d1e7, 0x4cf210ec5a9a8883}},
+ {{0x322d04a52d9021f6, 0xb9c19f3375c6bf9c, 0x587a3a4342d20b09, 0x143b1cf8aa64fe61}},
+ {{0x9f867c7d968acaab, 0x5f54258e27092729, 0xd0a7d34bea180975, 0x21b546a3374126e1}}},
+{{{0xa94ff858a2888343, 0xce0ed4565313ed3c, 0xf55c3dcfb5bf34fa, 0x0a653ca5c9eab371}},
+ {{0x490a7a45d185218f, 0x9a15377846049335, 0x0060ea09cc31e1f6, 0x7e041577f86ee965}},
+ {{0x66b2a496ce5b67f3, 0xff5492d8bd569796, 0x503cec294a592cd0, 0x566943650813acb2}}},
+{{{0xb818db0c26620798, 0x5d5c31d9606e354a, 0x0982fa4f00a8cdc7, 0x17e12bcd4653e2d4}},
+ {{0x5672f9eb1dabb69d, 0xba70b535afe853fc, 0x47ac0f752796d66d, 0x32a5351794117275}},
+ {{0xd3a644a6df648437, 0x703b6559880fbfdd, 0xcb852540ad3a1aa5, 0x0900b3f78e4c6468}}},
+{{{0x0a851b9f679d651b, 0xe108cb61033342f2, 0xd601f57fe88b30a3, 0x371f3acaed2dd714}},
+ {{0xed280fbec816ad31, 0x52d9595bd8e6efe3, 0x0fe71772f6c623f5, 0x4314030b051e293c}},
+ {{0xd560005efbf0bcad, 0x8eb70f2ed1870c5e, 0x201f9033d084e6a0, 0x4c3a5ae1ce7b6670}}},
+{{{0x4138a434dcb8fa95, 0x870cf67d6c96840b, 0xde388574297be82c, 0x7c814db27262a55a}},
+ {{0xbaf875e4c93da0dd, 0xb93282a771b9294d, 0x80d63fb7f4c6c460, 0x6de9c73dea66c181}},
+ {{0x478904d5a04df8f2, 0xfafbae4ab10142d3, 0xf6c8ac63555d0998, 0x5aac4a412f90b104}}},
+{{{0xc64f326b3ac92908, 0x5551b282e663e1e0, 0x476b35f54a1a4b83, 0x1b9da3fe189f68c2}},
+ {{0x603a0d0abd7f5134, 0x8089c932e1d3ae46, 0xdf2591398798bd63, 0x1c145cd274ba0235}},
+ {{0x32e8386475f3d743, 0x365b8baf6ae5d9ef, 0x825238b6385b681e, 0x234929c1167d65e1}}},
+{{{0x984decaba077ade8, 0x383f77ad19eb389d, 0xc7ec6b7e2954d794, 0x59c77b3aeb7c3a7a}},
+ {{0x48145cc21d099fcf, 0x4535c192cc28d7e5, 0x80e7c1e548247e01, 0x4a5f28743b2973ee}},
+ {{0xd3add725225ccf62, 0x911a3381b2152c5d, 0xd8b39fad5b08f87d, 0x6f05606b4799fe3b}}},
+{{{0x9ffe9e92177ba962, 0x98aee71d0de5cae1, 0x3ff4ae942d831044, 0x714de12e58533ac8}},
+ {{0x5b433149f91b6483, 0xadb5dc655a2cbf62, 0x87fa8412632827b3, 0x60895e91ab49f8d8}},
+ {{0xe9ecf2ed0cf86c18, 0xb46d06120735dfd4, 0xbc9da09804b96be7, 0x73e2e62fd96dc26b}}},
+{{{0xed5b635449aa515e, 0xa865c49f0bc6823a, 0x850c1fe95b42d1c4, 0x30d76d6f03d315b9}},
+ {{0x2eccdd0e632f9c1d, 0x51d0b69676893115, 0x52dfb76ba8637a58, 0x6dd37d49a00eef39}},
+ {{0x6c4444172106e4c7, 0xfb53d680928d7f69, 0xb4739ea4694d3f26, 0x10c697112e864bb0}}},
+{{{0x6493c4277dbe5fde, 0x265d4fad19ad7ea2, 0x0e00dfc846304590, 0x25e61cabed66fe09}},
+ {{0x0ca62aa08358c805, 0x6a3d4ae37a204247, 0x7464d3a63b11eddc, 0x03bf9baf550806ef}},
+ {{0x3f13e128cc586604, 0x6f5873ecb459747e, 0xa0b63dedcc1268f5, 0x566d78634586e22c}}},
+{{{0x1637a49f9cc10834, 0xbc8e56d5a89bc451, 0x1cb5ec0f7f7fd2db, 0x33975bca5ecc35d9}},
+ {{0xa1054285c65a2fd0, 0x6c64112af31667c3, 0x680ae240731aee58, 0x14fba5f34793b22a}},
+ {{0x3cd746166985f7d4, 0x593e5e84c9c80057, 0x2fc3f2b67b61131e, 0x14829cea83fc526c}}},
+{{{0xff437b8497dd95c2, 0x6c744e30aa4eb5a7, 0x9e0c5d613c85e88b, 0x2fd9c71e5f758173}},
+ {{0x21e70b2f4e71ecb8, 0xe656ddb940a477e3, 0xbf6556cece1d4f80, 0x05fc3bc4535d7b7e}},
+ {{0x24b8b3ae52afdedd, 0x3495638ced3b30cf, 0x33a4bc83a9be8195, 0x373767475c651f04}}},
+{{{0x2fba99fd40d1add9, 0xb307166f96f4d027, 0x4363f05215f03bae, 0x1fbea56c3b18f999}},
+ {{0x634095cb14246590, 0xef12144016c15535, 0x9e38140c8910bc60, 0x6bf5905730907c8c}},
+ {{0x0fa778f1e1415b8a, 0x06409ff7bac3a77e, 0x6f52d7b89aa29a50, 0x02521cf67a635a56}}},
+{{{0x513fee0b0a9d5294, 0x8f98e75c0fdf5a66, 0xd4618688bfe107ce, 0x3fa00a7e71382ced}},
+ {{0xb1146720772f5ee4, 0xe8f894b196079ace, 0x4af8224d00ac824a, 0x001753d9f7cd6cc4}},
+ {{0x3c69232d963ddb34, 0x1dde87dab4973858, 0xaad7d1f9a091f285, 0x12b5fe2fa048edb6}}},
+{{{0x71f0fbc496fce34d, 0x73b9826badf35bed, 0xd2047261ff28c561, 0x749b76f96fb1206f}},
+ {{0xdf2b7c26ad6f1e92, 0x4b66d323504b8913, 0x8c409dc0751c8bc3, 0x6f7e93c20796c7b8}},
+ {{0x1f5af604aea6ae05, 0xc12351f1bee49c99, 0x61a808b5eeff6b66, 0x0fcec10f01e02151}}},
+{{{0x644d58a649fe1e44, 0x21fcaea231ad777e, 0x02441c5a887fd0d2, 0x4901aa7183c511f3}},
+ {{0x3df2d29dc4244e45, 0x2b020e7493d8de0a, 0x6cc8067e820c214d, 0x413779166feab90a}},
+ {{0x08b1b7548c1af8f0, 0xce0f7a7c246299b4, 0xf760b0f91e06d939, 0x41bb887b726d1213}}},
+{{{0x9267806c567c49d8, 0x066d04ccca791e6a, 0xa69f5645e3cc394b, 0x5c95b686a0788cd2}},
+ {{0x97d980e0aa39f7d2, 0x35d0384252c6b51c, 0x7d43f49307cd55aa, 0x56bd36cfb78ac362}},
+ {{0x2ac519c10d14a954, 0xeaf474b494b5fa90, 0xe6af8382a9f87a5a, 0x0dea6db1879be094}}},
+{{{0xaa66bf547344e5ab, 0xda1258888f1b4309, 0x5e87d2b3fd564b2f, 0x5b2c78885483b1dd}},
+ {{0x15baeb74d6a8797a, 0x7ef55cf1fac41732, 0x29001f5a3c8b05c5, 0x0ad7cc8752eaccfb}},
+ {{0x52151362793408cf, 0xeb0f170319963d94, 0xa833b2fa883d9466, 0x093a7fa775003c78}}},
+{{{0xe5107de63a16d7be, 0xa377ffdc9af332cf, 0x70d5bf18440b677f, 0x6a252b19a4a31403}},
+ {{0xb8e9604460a91286, 0x7f3fd8047778d3de, 0x67d01e31bf8a5e2d, 0x7b038a06c27b653e}},
+ {{0x9ed919d5d36990f3, 0x5213aebbdb4eb9f2, 0xc708ea054cb99135, 0x58ded57f72260e56}}},
+{{{0x78e79dade9413d77, 0xf257f9d59729e67d, 0x59db910ee37aa7e6, 0x6aa11b5bbb9e039c}},
+ {{0xda6d53265b0fd48b, 0x8960823193bfa988, 0xd78ac93261d57e28, 0x79f2942d3a5c8143}},
+ {{0x97da2f25b6c88de9, 0x251ba7eaacf20169, 0x09b44f87ef4eb4e4, 0x7d90ab1bbc6a7da5}}},
+{{{0x9acca683a7016bfe, 0x90505f4df2c50b6d, 0x6b610d5fcce435aa, 0x19a10d446198ff96}},
+ {{0x1a07a3f496b3c397, 0x11ceaa188f4e2532, 0x7d9498d5a7751bf0, 0x19ed161f508dd8a0}},
+ {{0x560a2cd687dce6ca, 0x7f3568c48664cf4d, 0x8741e95222803a38, 0x483bdab1595653fc}}},
+{{{0xfa780f148734fa49, 0x106f0b70360534e0, 0x2210776fe3e307bd, 0x3286c109dde6a0fe}},
+ {{0xd6cf4d0ab4da80f6, 0x82483e45f8307fe0, 0x05005269ae6f9da4, 0x1c7052909cf7877a}},
+ {{0x32ee7de2874e98d4, 0x14c362e9b97e0c60, 0x5781dcde6a60a38a, 0x217dd5eaaa7aa840}}},
+{{{0x9db7c4d0248e1eb0, 0xe07697e14d74bf52, 0x1e6a9b173c562354, 0x7fa7c21f795a4965}},
+ {{0x8bdf1fb9be8c0ec8, 0x00bae7f8e30a0282, 0x4963991dad6c4f6c, 0x07058a6e5df6f60a}},
+ {{0xe9eb02c4db31f67f, 0xed25fd8910bcfb2b, 0x46c8131f5c5cddb4, 0x33b21c13a0cb9bce}}},
+{{{0x360692f8087d8e31, 0xf4dcc637d27163f7, 0x25a4e62065ea5963, 0x659bf72e5ac160d9}},
+ {{0x9aafb9b05ee38c5b, 0xbf9d2d4e071a13c7, 0x8eee6e6de933290a, 0x1c3bab17ae109717}},
+ {{0x1c9ab216c7cab7b0, 0x7d65d37407bbc3cc, 0x52744750504a58d5, 0x09f2606b131a2990}}},
+{{{0x40e87d44744346be, 0x1d48dad415b52b25, 0x7c3a8a18a13b603e, 0x4eb728c12fcdbdf7}},
+ {{0x7e234c597c6691ae, 0x64889d3d0a85b4c8, 0xdae2c90c354afae7, 0x0a871e070c6a9e1d}},
+ {{0x3301b5994bbc8989, 0x736bae3a5bdd4260, 0x0d61ade219d59e3c, 0x3ee7300f2685d464}}},
+{{{0xf5d255e49e7dd6b7, 0x8016115c610b1eac, 0x3c99975d92e187ca, 0x13815762979125c2}},
+ {{0x43fa7947841e7518, 0xe5c6fa59639c46d7, 0xa1065e1de3052b74, 0x7d47c6a2cfb89030}},
+ {{0x3fdad0148ef0d6e0, 0x9d3e749a91546f3c, 0x71ec621026bb8157, 0x148cf58d34c9ec80}}},
+{{{0x46a492f67934f027, 0x469984bef6840aa9, 0x5ca1bc2a89611854, 0x3ff2fa1ebd5dbbd4}},
+ {{0xe2572f7d9ae4756d, 0x56c345bb88f3487f, 0x9fd10b6d6960a88d, 0x278febad4eaea1b9}},
+ {{0xb1aa681f8c933966, 0x8c21949c20290c98, 0x39115291219d3c52, 0x4104dd02fe9c677b}}},
+{{{0x72b2bf5e1124422a, 0xa1fa0c3398a33ab5, 0x94cb6101fa52b666, 0x2c863b00afaf53d5}},
+ {{0x81214e06db096ab8, 0x21a8b6c90ce44f35, 0x6524c12a409e2af5, 0x0165b5a48efca481}},
+ {{0xf190a474a0846a76, 0x12eff984cd2f7cc0, 0x695e290658aa2b8f, 0x591b67d9bffec8b8}}},
+{{{0x312f0d1c80b49bfa, 0x5979515eabf3ec8a, 0x727033c09ef01c88, 0x3de02ec7ca8f7bcb}},
+ {{0x99b9b3719f18b55d, 0xe465e5faa18c641e, 0x61081136c29f05ed, 0x489b4f867030128b}},
+ {{0xd232102d3aeb92ef, 0xe16253b46116a861, 0x3d7eabe7190baa24, 0x49f5fbba496cbebf}}},
+{{{0x30949a108a5bcfd4, 0xdc40dd70bc6473eb, 0x92c294c1307c0d1c, 0x5604a86dcbfa6e74}},
+ {{0x155d628c1e9c572e, 0x8a4d86acc5884741, 0x91a352f6515763eb, 0x06a1a6c28867515b}},
+ {{0x7288d1d47c1764b6, 0x72541140e0418b51, 0x9f031a6018acf6d1, 0x20989e89fe2742c6}}},
+{{{0x499777fd3a2dcc7f, 0x32857c2ca54fd892, 0xa279d864d207e3a0, 0x0403ed1d0ca67e29}},
+ {{0x1674278b85eaec2e, 0x5621dc077acb2bdf, 0x640a4c1661cbf45a, 0x730b9950f70595d3}},
+ {{0xc94b2d35874ec552, 0xc5e6c8cf98246f8d, 0xf7cb46fa16c035ce, 0x5bd7454308303dcc}}},
+{{{0x7f9ad19528b24cc2, 0x7f6b54656335c181, 0x66b8b66e4fc07236, 0x133a78007380ad83}},
+ {{0x85c4932115e7792a, 0xc64c89a2bdcdddc9, 0x9d1e3da8ada3d762, 0x5bb7db123067f82c}},
+ {{0x0961f467c6ca62be, 0x04ec21d6211952ee, 0x182360779bd54770, 0x740dca6d58f0e0d2}}},
+{{{0x50b70bf5d3f0af0b, 0x4feaf48ae32e71f7, 0x60e84ed3a55bbd34, 0x00ed489b3f50d1ed}},
+ {{0x3906c72aed261ae5, 0x9ab68fd988e100f7, 0xf5e9059af3360197, 0x0e53dc78bf2b6d47}},
+ {{0xb90829bf7971877a, 0x5e4444636d17e631, 0x4d05c52e18276893, 0x27632d9a5a4a4af5}}},
+{{{0xd11ff05154b260ce, 0xd86dc38e72f95270, 0x601fcd0d267cc138, 0x2b67916429e90ccd}},
+ {{0xa98285d187eaffdb, 0xa5b4fbbbd8d0a864, 0xb658f27f022663f7, 0x3bbc2b22d99ce282}},
+ {{0xb917c952583c0a58, 0x653ff9b80fe4c6f3, 0x9b0da7d7bcdf3c0c, 0x43a0eeb6ab54d60e}}},
+{{{0x396966a46d4a5487, 0xf811a18aac2bb3ba, 0x66e4685b5628b26b, 0x70a477029d929b92}},
+ {{0x3ac6322357875fe8, 0xd9d4f4ecf5fbcb8f, 0x8dee8493382bb620, 0x50c5eaa14c799fdc}},
+ {{0xdd0edc8bd6f2fb3c, 0x54c63aa79cc7b7a0, 0xae0b032b2c8d9f1a, 0x6f9ce107602967fb}}},
+{{{0xad1054b1cde1c22a, 0xc4a8e90248eb32df, 0x5f3e7b33accdc0ea, 0x72364713fc79963e}},
+ {{0x139693063520e0b5, 0x437fcf7c88ea03fe, 0xf7d4c40bd3c959bc, 0x699154d1f893ded9}},
+ {{0x315d5c75b4b27526, 0xcccb842d0236daa5, 0x22f0c8a3345fee8e, 0x73975a617d39dbed}}},
+{{{0xe4024df96375da10, 0x78d3251a1830c870, 0x902b1948658cd91c, 0x7e18b10b29b7438a}},
+ {{0x6f37f392f4433e46, 0x0e19b9a11f566b18, 0x220fb78a1fd1d662, 0x362a4258a381c94d}},
+ {{0x9071d9132b6beb2f, 0x0f26e9ad28418247, 0xeab91ec9bdec925d, 0x4be65bc8f48af2de}}},
+{{{0x78487feba36e7028, 0x5f3f13001dd8ce34, 0x934fb12d4b30c489, 0x056c244d397f0a2b}},
+ {{0x1d50fba257c26234, 0x7bd4823adeb0678b, 0xc2b0dc6ea6538af5, 0x5665eec6351da73e}},
+ {{0xdb3ee00943bfb210, 0x4972018720800ac2, 0x26ab5d6173bd8667, 0x20b209c2ab204938}}},
+{{{0x549e342ac07fb34b, 0x02d8220821373d93, 0xbc262d70acd1f567, 0x7a92c9fdfbcac784}},
+ {{0x1fcca94516bd3289, 0x448d65aa41420428, 0x59c3b7b216a55d62, 0x49992cc64e612cd8}},
+ {{0x65bd1bea70f801de, 0x1befb7c0fe49e28a, 0xa86306cdb1b2ae4a, 0x3b7ac0cd265c2a09}}},
+{{{0x822bee438c01bcec, 0x530cb525c0fbc73b, 0x48519034c1953fe9, 0x265cc261e09a0f5b}},
+ {{0xf0d54e4f22ed39a7, 0xa2aae91e5608150a, 0xf421b2e9eddae875, 0x31bc531d6b7de992}},
+ {{0xdf3d134da980f971, 0x7a4fb8d1221a22a7, 0x3df7d42035aad6d8, 0x2a14edcc6a1a125e}}},
+{{{0xdf48ee0752cfce4e, 0xc3fffaf306ec08b7, 0x05710b2ab95459c4, 0x161d25fa963ea38d}},
+ {{0x231a8c570478433c, 0xb7b5270ec281439d, 0xdbaa99eae3d9079f, 0x2c03f5256c2b03d9}},
+ {{0x790f18757b53a47d, 0x307b0130cf0c5879, 0x31903d77257ef7f9, 0x699468bdbd96bbaf}}},
+{{{0xbd1f2f46f4dafecf, 0x7cef0114a47fd6f7, 0xd31ffdda4a47b37f, 0x525219a473905785}},
+ {{0xd8dd3de66aa91948, 0x485064c22fc0d2cc, 0x9b48246634fdea2f, 0x293e1c4e6c4a2e3a}},
+ {{0x376e134b925112e1, 0x703778b5dca15da0, 0xb04589af461c3111, 0x5b605c447f032823}}},
+{{{0xb965805920c47c89, 0xe7f0100c923b8fcc, 0x0001256502e2ef77, 0x24a76dcea8aeb3ee}},
+ {{0x3be9fec6f0e7f04c, 0x866a579e75e34962, 0x5542ef161e1de61a, 0x2f12fef4cc5abdd5}},
+ {{0x0a4522b2dfc0c740, 0x10d06e7f40c9a407, 0xc6cf144178cff668, 0x5e607b2518a43790}}},
+{{{0x58b31d8f6cdf1818, 0x35cfa74fc36258a2, 0xe1b3ff4f66e61d6e, 0x5067acab6ccdd5f7}},
+ {{0xa02c431ca596cf14, 0xe3c42d40aed3e400, 0xd24526802e0f26db, 0x201f33139e457068}},
+ {{0xfd527f6b08039d51, 0x18b14964017c0006, 0xd5220eb02e25a4a8, 0x397cba8862460375}}},
+{{{0x30c13093f05959b2, 0xe23aa18de9a97976, 0x222fd491721d5e26, 0x2339d320766e6c3a}},
+ {{0x7815c3fbc81379e7, 0xa6619420dde12af1, 0xffa9c0f885a8fdd5, 0x771b4022c1e1c252}},
+ {{0xd87dd986513a2fa7, 0xf5ac9b71f9d4cf08, 0xd06bc31b1ea283b3, 0x331a189219971a76}}},
+{{{0xf5166f45fb4f80c6, 0x9c36c7de61c775cf, 0xe3d4e81b9041d91c, 0x31167c6b83bdfe21}},
+ {{0x26512f3a9d7572af, 0x5bcbe28868074a9e, 0x84edc1c11180f7c4, 0x1ac9619ff649a67b}},
+ {{0xf22b3842524b1068, 0x5068343bee9ce987, 0xfc9d71844a6250c8, 0x612436341f08b111}}},
+{{{0xd99d41db874e898d, 0x09fea5f16c07dc20, 0x793d2c67d00f9bbc, 0x46ebe2309e5eff40}},
+ {{0x8b6349e31a2d2638, 0x9ddfb7009bd3fd35, 0x7f8bf1b8a3a06ba4, 0x1522aa3178d90445}},
+ {{0x2c382f5369614938, 0xdafe409ab72d6d10, 0xe8c83391b646f227, 0x45fe70f50524306c}}},
+{{{0xda4875a6960c0b8c, 0x5b68d076ef0e2f20, 0x07fb51cf3d0b8fd4, 0x428d1623a0e392d4}},
+ {{0x62f24920c8951491, 0x05f007c83f630ca2, 0x6fbb45d2f5c9d4b8, 0x16619f6db57a2245}},
+ {{0x084f4a4401a308fd, 0xa82219c376a5caac, 0xdeb8de4643d1bc7d, 0x1d81592d60bd38c6}}},
+{{{0xd833d7beec2a4c38, 0x2c9162830acc20ed, 0xe93a47aa92df7581, 0x702d67a3333c4a81}},
+ {{0x3a4a369a2f89c8a1, 0x63137a1d7c8de80d, 0xbcac008a78eda015, 0x2cb8b3a5b483b03f}},
+ {{0x36e417cbcb1b90a1, 0x33b3ddaa7f11794e, 0x3f510808885bc607, 0x24141dc0e6a8020d}}},
+{{{0x59f73c773fefee9d, 0xb3f1ef89c1cf989d, 0xe35dfb42e02e545f, 0x5766120b47a1b47c}},
+ {{0x91925dccbd83157d, 0x3ca1205322cc8094, 0x28e57f183f90d6e4, 0x1a4714cede2e767b}},
+ {{0xdb20ba0fb8b6b7ff, 0xb732c3b677511fa1, 0xa92b51c099f02d89, 0x4f3875ad489ca5f1}}},
+{{{0xc7fc762f4932ab22, 0x7ac0edf72f4c3c1b, 0x5f6b55aa9aa895e8, 0x3680274dad0a0081}},
+ {{0x79ed13f6ee73eec0, 0xa5c6526d69110bb1, 0xe48928c38603860c, 0x722a1446fd7059f5}},
+ {{0xd0959fe9a8cf8819, 0xd0a995508475a99c, 0x6eac173320b09cc5, 0x628ecf04331b1095}}},
+{{{0x98bcb118a9d0ddbc, 0xee449e3408b4802b, 0x87089226b8a6b104, 0x685f349a45c7915d}},
+ {{0x9b41acf85c74ccf1, 0xb673318108265251, 0x99c92aed11adb147, 0x7a47d70d34ecb40f}},
+ {{0x60a0c4cbcc43a4f5, 0x775c66ca3677bea9, 0xa17aa1752ff8f5ed, 0x11ded9020e01fdc0}}},
+{{{0x890e7809caefe704, 0x8728296de30e8c6c, 0x4c5cd2a392aeb1c9, 0x194263d15771531f}},
+ {{0x471f95b03bea93b7, 0x0552d7d43313abd3, 0xbd9370e2e17e3f7b, 0x7b120f1db20e5bec}},
+ {{0x17d2fb3d86502d7a, 0xb564d84450a69352, 0x7da962c8a60ed75d, 0x00d0f85b318736aa}}},
+{{{0x978b142e777c84fd, 0xf402644705a8c062, 0xa67ad51be7e612c7, 0x2f7b459698dd6a33}},
+ {{0xa6753c1efd7621c1, 0x69c0b4a7445671f5, 0x971f527405b23c11, 0x387bc74851a8c7cd}},
+ {{0x81894b4d4a52a9a8, 0xadd93e12f6b8832f, 0x184d8548b61bd638, 0x3f1c62dbd6c9f6cd}}},
+{{{0x2e8f1f0091910c1f, 0xa4df4fe0bff2e12c, 0x60c6560aee927438, 0x6338283facefc8fa}},
+ {{0x3fad3e40148f693d, 0x052656e194eb9a72, 0x2f4dcbfd184f4e2f, 0x406f8db1c482e18b}},
+ {{0x9e630d2c7f191ee4, 0x4fbf8301bc3ff670, 0x787d8e4e7afb73c4, 0x50d83d5be8f58fa5}}},
+{{{0x85683916c11a1897, 0x2d69a4efe506d008, 0x39af1378f664bd01, 0x65942131361517c6}},
+ {{0xc0accf90b4d3b66d, 0xa7059de561732e60, 0x033d1f7870c6b0ba, 0x584161cd26d946e4}},
+ {{0xbbf2b1a072d27ca2, 0xbf393c59fbdec704, 0xe98dbbcee262b81e, 0x02eebd0b3029b589}}},
+{{{0x61368756a60dac5f, 0x17e02f6aebabdc57, 0x7f193f2d4cce0f7d, 0x20234a7789ecdcf0}},
+ {{0x8765b69f7b85c5e8, 0x6ff0678bd168bab2, 0x3a70e77c1d330f9b, 0x3a5f6d51b0af8e7c}},
+ {{0x76d20db67178b252, 0x071c34f9d51ed160, 0xf62a4a20b3e41170, 0x7cd682353cffe366}}},
+{{{0x0be1a45bd887fab6, 0x2a846a32ba403b6e, 0xd9921012e96e6000, 0x2838c8863bdc0943}},
+ {{0xa665cd6068acf4f3, 0x42d92d183cd7e3d3, 0x5759389d336025d9, 0x3ef0253b2b2cd8ff}},
+ {{0xd16bb0cf4a465030, 0xfa496b4115c577ab, 0x82cfae8af4ab419d, 0x21dcb8a606a82812}}},
+{{{0x5c6004468c9d9fc8, 0x2540096ed42aa3cb, 0x125b4d4c12ee2f9c, 0x0bc3d08194a31dab}},
+ {{0x9a8d00fabe7731ba, 0x8203607e629e1889, 0xb2cc023743f3d97f, 0x5d840dbf6c6f678b}},
+ {{0x706e380d309fe18b, 0x6eb02da6b9e165c7, 0x57bbba997dae20ab, 0x3a4276232ac196dd}}},
+{{{0x4b42432c8a7084fa, 0x898a19e3dfb9e545, 0xbe9f00219c58e45d, 0x1ff177cea16debd1}},
+ {{0x3bf8c172db447ecb, 0x5fcfc41fc6282dbd, 0x80acffc075aa15fe, 0x0770c9e824e1a9f9}},
+ {{0xcf61d99a45b5b5fd, 0x860984e91b3a7924, 0xe7300919303e3e89, 0x39f264fd41500b1e}}},
+{{{0xa7ad3417dbe7e29c, 0xbd94376a2b9c139c, 0xa0e91b8e93597ba9, 0x1712d73468889840}},
+ {{0xd19b4aabfe097be1, 0xa46dfce1dfe01929, 0xc3c908942ca6f1ff, 0x65c621272c35f14e}},
+ {{0xe72b89f8ce3193dd, 0x4d103356a125c0bb, 0x0419a93d2e1cfe83, 0x22f9800ab19ce272}}},
+{{{0x605a368a3e9ef8cb, 0xe3e9c022a5504715, 0x553d48b05f24248f, 0x13f416cd647626e5}},
+ {{0x42029fdd9a6efdac, 0xb912cebe34a54941, 0x640f64b987bdf37b, 0x4171a4d38598cab4}},
+ {{0xfa2758aa99c94c8c, 0x23006f6fb000b807, 0xfbd291ddadda5392, 0x508214fa574bd1ab}}},
+{{{0xc20269153ed6fe4b, 0xa65a6739511d77c4, 0xcbde26462c14af94, 0x22f960ec6faba74b}},
+ {{0x461a15bb53d003d6, 0xb2102888bcf3c965, 0x27c576756c683a5a, 0x3a7758a4c86cb447}},
+ {{0x548111f693ae5076, 0x1dae21df1dfd54a6, 0x12248c90f3115e65, 0x5d9fd15f8de7f494}}},
+{{{0x031408d36d63727f, 0x6a379aefd7c7b533, 0xa9e18fc5ccaee24b, 0x332f35914f8fbed3}},
+ {{0x3f244d2aeed7521e, 0x8e3a9028432e9615, 0xe164ba772e9c16d4, 0x3bc187fa47eb98d8}},
+ {{0x6d470115ea86c20c, 0x998ab7cb6c46d125, 0xd77832b53a660188, 0x450d81ce906fba03}}},
+{{{0xf8ae4d2ad8453902, 0x7018058ee8db2d1d, 0xaab3995fc7d2c11e, 0x53b16d2324ccca79}},
+ {{0x23264d66b2cae0b5, 0x7dbaed33ebca6576, 0x030ebed6f0d24ac8, 0x2a887f78f7635510}},
+ {{0x2a23b9e75c012d4f, 0x0c974651cae1f2ea, 0x2fb63273675d70ca, 0x0ba7250b864403f5}}},
+{{{0xbb0d18fd029c6421, 0xbc2d142189298f02, 0x8347f8e68b250e96, 0x7b9f2fe8032d71c9}},
+ {{0xdd63589386f86d9c, 0x61699176e13a85a4, 0x2e5111954eaa7d57, 0x32c21b57fb60bdfb}},
+ {{0xd87823cd319e0780, 0xefc4cfc1897775c5, 0x4854fb129a0ab3f7, 0x12c49d417238c371}}},
+{{{0x0950b533ffe83769, 0x21861c1d8e1d6bd1, 0xf022d8381302e510, 0x2509200c6391cab4}},
+ {{0x09b3a01783799542, 0x626dd08faad5ee3f, 0xba00bceeeb70149f, 0x1421b246a0a444c9}},
+ {{0x4aa43a8e8c24a7c7, 0x04c1f540d8f05ef5, 0xadba5e0c0b3eb9dc, 0x2ab5504448a49ce3}}},
+{{{0x2ed227266f0f5dec, 0x9824ee415ed50824, 0x807bec7c9468d415, 0x7093bae1b521e23f}},
+ {{0xdc07ac631c5d3afa, 0x58615171f9df8c6c, 0x72a079d89d73e2b0, 0x7301f4ceb4eae15d}},
+ {{0x6409e759d6722c41, 0xa674e1cf72bf729b, 0xbc0a24eb3c21e569, 0x390167d24ebacb23}}},
+{{{0x27f58e3bba353f1c, 0x4c47764dbf6a4361, 0xafbbc4e56e562650, 0x07db2ee6aae1a45d}},
+ {{0xd7bb054ba2f2120b, 0xe2b9ceaeb10589b7, 0x3fe8bac8f3c0edbe, 0x4cbd40767112cb69}},
+ {{0x0b603cc029c58176, 0x5988e3825cb15d61, 0x2bb61413dcf0ad8d, 0x7b8eec6c74183287}}},
+{{{0xe4ca40782cd27cb0, 0xdaf9c323fbe967bd, 0xb29bd34a8ad41e9e, 0x72810497626ede4d}},
+ {{0x32fee570fc386b73, 0xda8b0141da3a8cc7, 0x975ffd0ac8968359, 0x6ee809a1b132a855}},
+ {{0x9444bb31fcfd863a, 0x2fe3690a3e4e48c5, 0xdc29c867d088fa25, 0x13bd1e38d173292e}}},
+{{{0xd32b4cd8696149b5, 0xe55937d781d8aab7, 0x0bcb2127ae122b94, 0x41e86fcfb14099b0}},
+ {{0x223fb5cf1dfac521, 0x325c25316f554450, 0x030b98d7659177ac, 0x1ed018b64f88a4bd}},
+ {{0x3630dfa1b802a6b0, 0x880f874742ad3bd5, 0x0af90d6ceec5a4d4, 0x746a247a37cdc5d9}}},
+{{{0xd531b8bd2b7b9af6, 0x5005093537fc5b51, 0x232fcf25c593546d, 0x20a365142bb40f49}},
+ {{0x6eccd85278d941ed, 0x2254ae83d22f7843, 0xc522d02e7bbfcdb7, 0x681e3351bff0e4e2}},
+ {{0x8b64b59d83034f45, 0x2f8b71f21fa20efb, 0x69249495ba6550e4, 0x539ef98e45d5472b}}},
+{{{0x6e7bb6a1a6205275, 0xaa4f21d7413c8e83, 0x6f56d155e88f5cb2, 0x2de25d4ba6345be1}},
+ {{0xd074d8961cae743f, 0xf86d18f5ee1c63ed, 0x97bdc55be7f4ed29, 0x4cbad279663ab108}},
+ {{0x80d19024a0d71fcd, 0xc525c20afb288af8, 0xb1a3974b5f3a6419, 0x7d7fbcefe2007233}}},
+{{{0xfaef1e6a266b2801, 0x866c68c4d5739f16, 0xf68a2fbc1b03762c, 0x5975435e87b75a8d}},
+ {{0xcd7c5dc5f3c29094, 0xc781a29a2a9105ab, 0x80c61d36421c3058, 0x4f9cd196dcd8d4d7}},
+ {{0x199297d86a7b3768, 0xd0d058241ad17a63, 0xba029cad5c1c0c17, 0x7ccdd084387a0307}}},
+{{{0xdca6422c6d260417, 0xae153d50948240bd, 0xa9c0c1b4fb68c677, 0x428bd0ed61d0cf53}},
+ {{0x9b0c84186760cc93, 0xcdae007a1ab32a99, 0xa88dec86620bda18, 0x3593ca848190ca44}},
+ {{0x9213189a5e849aa7, 0xd4d8c33565d8facd, 0x8c52545b53fdbbd1, 0x27398308da2d63e6}}},
+{{{0x42c38d28435ed413, 0xbd50f3603278ccc9, 0xbb07ab1a79da03ef, 0x269597aebe8c3355}},
+ {{0xb9a10e4c0a702453, 0x0fa25866d57d1bde, 0xffb9d9b5cd27daf7, 0x572c2945492c33fd}},
+ {{0xc77fc745d6cd30be, 0xe4dfe8d3e3baaefb, 0xa22c8830aa5dda0c, 0x7f985498c05bca80}}},
+{{{0x3849ce889f0be117, 0x8005ad1b7b54a288, 0x3da3c39f23fc921c, 0x76c2ec470a31f304}},
+ {{0xd35615520fbf6363, 0x08045a45cf4dfba6, 0xeec24fbc873fa0c2, 0x30f2653cd69b12e7}},
+ {{0x8a08c938aac10c85, 0x46179b60db276bcb, 0xa920c01e0e6fac70, 0x2f1273f1596473da}}},
+{{{0x4739fc7c8ae01e11, 0xfd5274904a6aab9f, 0x41d98a8287728f2e, 0x5d9e572ad85b69f2}},
+ {{0x30488bd755a70bc0, 0x06d6b5a4f1d442e7, 0xead1a69ebc596162, 0x38ac1997edc5f784}},
+ {{0x0666b517a751b13b, 0x747d06867e9b858c, 0xacacc011454dde49, 0x22dfcd9cbfe9e69c}}},
+{{{0x8ddbd2e0c30d0cd9, 0xad8e665facbb4333, 0x8f6b258c322a961f, 0x6b2916c05448c1c7}},
+ {{0x56ec59b4103be0a1, 0x2ee3baecd259f969, 0x797cb29413f5cd32, 0x0fe9877824cde472}},
+ {{0x7edb34d10aba913b, 0x4ea3cd822e6dac0e, 0x66083dff6578f815, 0x4c303f307ff00a17}}},
+{{{0xd30a3bd617b28c85, 0xc5d377b739773bea, 0xc6c6e78c1e6a5cbf, 0x0d61b8f78b2ab7c4}},
+ {{0x29fc03580dd94500, 0xecd27aa46fbbec93, 0x130a155fc2e2a7f8, 0x416b151ab706a1d5}},
+ {{0x56a8d7efe9c136b0, 0xbd07e5cd58e44b20, 0xafe62fda1b57e0ab, 0x191a2af74277e8d2}}},
+{{{0xd550095bab6f4985, 0x04f4cd5b4fbfaf1a, 0x9d8e2ed12a0c7540, 0x2bc24e04b2212286}},
+ {{0x09d4b60b2fe09a14, 0xc384f0afdbb1747e, 0x58e2ea8978b5fd6e, 0x519ef577b5e09b0a}},
+ {{0x1863d7d91124cca9, 0x7ac08145b88a708e, 0x2bcd7309857031f5, 0x62337a6e8ab8fae5}}},
+{{{0x4bcef17f06ffca16, 0xde06e1db692ae16a, 0x0753702d614f42b0, 0x5f6041b45b9212d0}},
+ {{0xd1ab324e1b3a1273, 0x18947cf181055340, 0x3b5d9567a98c196e, 0x7fa00425802e1e68}},
+ {{0x7d531574028c2705, 0x80317d69db0d75fe, 0x30fface8ef8c8ddd, 0x7e9de97bb6c3e998}}},
+{{{0x1558967b9e6585a3, 0x97c99ce098e98b92, 0x10af149b6eb3adad, 0x42181fe8f4d38cfa}},
+ {{0xf004be62a24d40dd, 0xba0659910452d41f, 0x81c45ee162a44234, 0x4cb829d8a22266ef}},
+ {{0x1dbcaa8407b86681, 0x081f001e8b26753b, 0x3cd7ce6a84048e81, 0x78af11633f25f22c}}},
+{{{0x8416ebd40b50babc, 0x1508722628208bee, 0xa3148fafb9c1c36d, 0x0d07daacd32d7d5d}},
+ {{0x3241c00e7d65318c, 0xe6bee5dcd0e86de7, 0x118b2dc2fbc08c26, 0x680d04a7fc603dc3}},
+ {{0xf9c2414a695aa3eb, 0xdaa42c4c05a68f21, 0x7c6c23987f93963e, 0x210e8cd30c3954e3}}},
+{{{0xac4201f210a71c06, 0x6a65e0aef3bfb021, 0xbc42c35c393632f7, 0x56ea8db1865f0742}},
+ {{0x2b50f16137fe6c26, 0xe102bcd856e404d8, 0x12b0f1414c561f6b, 0x51b17bc8d028ec91}},
+ {{0xfff5fb4bcf535119, 0xf4989d79df1108a0, 0xbdfcea659a3ba325, 0x18a11f1174d1a6f2}}},
+{{{0x407375ab3f6bba29, 0x9ec3b6d8991e482e, 0x99c80e82e55f92e9, 0x307c13b6fb0c0ae1}},
+ {{0xfbd63cdad27a5f2c, 0xf00fc4bc8aa106d7, 0x53fb5c1a8e64a430, 0x04eaabe50c1a2e85}},
+ {{0x24751021cb8ab5e7, 0xfc2344495c5010eb, 0x5f1e717b4e5610a1, 0x44da5f18c2710cd5}}},
+{{{0x033cc55ff1b82eb5, 0xb15ae36d411cae52, 0xba40b6198ffbacd3, 0x768edce1532e861f}},
+ {{0x9156fe6b89d8eacc, 0xe6b79451e23126a1, 0xbd7463d93944eb4e, 0x726373f6767203ae}},
+ {{0xe305ca72eb7ef68a, 0x662cf31f70eadb23, 0x18f026fdb4c45b68, 0x513b5384b5d2ecbd}}},
+{{{0x46d46280c729989e, 0x4b93fbd05368a5dd, 0x63df3f81d1765a89, 0x34cebd64b9a0a223}},
+ {{0x5e2702878af34ceb, 0x900b0409b946d6ae, 0x6512ebf7dabd8512, 0x61d9b76988258f81}},
+ {{0xa6c5a71349b7d94b, 0xa3f3d15823eb9446, 0x0416fbd277484834, 0x69d45e6f2c70812f}}},
+{{{0xce16f74bc53c1431, 0x2b9725ce2072edde, 0xb8b9c36fb5b23ee7, 0x7e2e0e450b5cc908}},
+ {{0x9fe62b434f460efb, 0xded303d4a63607d6, 0xf052210eb7a0da24, 0x237e7dbe00545b93}},
+ {{0x013575ed6701b430, 0x231094e69f0bfd10, 0x75320f1583e47f22, 0x71afa699b11155e3}}},
+{{{0x65ce6f9b3953b61d, 0xc65839eaafa141e6, 0x0f435ffda9f759fe, 0x021142e9c2b1c28e}},
+ {{0xea423c1c473b50d6, 0x51e87a1f3b38ef10, 0x9b84bf5fb2c9be95, 0x00731fbc78f89a1c}},
+ {{0xe430c71848f81880, 0xbf960c225ecec119, 0xb6dae0836bba15e3, 0x4c4d6f3347e15808}}},
+{{{0x18f7eccfc17d1fc9, 0x6c75f5a651403c14, 0xdbde712bf7ee0cdf, 0x193fddaaa7e47a22}},
+ {{0x2f0cddfc988f1970, 0x6b916227b0b9f51b, 0x6ec7b6c4779176be, 0x38bf9500a88f9fa8}},
+ {{0x1fd2c93c37e8876f, 0xa2f61e5a18d1462c, 0x5080f58239241276, 0x6a6fb99ebf0d4969}}},
+{{{0x6a46c1bb560855eb, 0x2416bb38f893f09d, 0xd71d11378f71acc1, 0x75f76914a31896ea}},
+ {{0xeeb122b5b6e423c6, 0x939d7010f286ff8e, 0x90a92a831dcf5d8c, 0x136fda9f42c5eb10}},
+ {{0xf94cdfb1a305bdd1, 0x0f364b9d9ff82c08, 0x2a87d8a5c3bb588a, 0x022183510be8dcba}}},
+{{{0x4af766385ead2d14, 0xa08ed880ca7c5830, 0x0d13a6e610211e3d, 0x6a071ce17b806c03}},
+ {{0x9d5a710143307a7f, 0xb063de9ec47da45f, 0x22bbfe52be927ad3, 0x1387c441fd40426c}},
+ {{0xb5d3c3d187978af8, 0x722b5a3d7f0e4413, 0x0d7b4848bb477ca0, 0x3171b26aaf1edc92}}},
+{{{0xa92f319097564ca8, 0xff7bb84c2275e119, 0x4f55fe37a4875150, 0x221fd4873cf0835a}},
+ {{0xa60db7d8b28a47d1, 0xa6bf14d61770a4f1, 0xd4a1f89353ddbd58, 0x6c514a63344243e9}},
+ {{0x2322204f3a156341, 0xfb73e0e9ba0a032d, 0xfce0dd4c410f030e, 0x48daa596fb924aaa}}},
+{{{0x6eca8e665ca59cc7, 0xa847254b2e38aca0, 0x31afc708d21e17ce, 0x676dd6fccad84af7}},
+ {{0x14f61d5dc84c9793, 0x9941f9e3ef418206, 0xcdf5b88f346277ac, 0x58c837fa0e8a79a9}},
+ {{0x0cf9688596fc9058, 0x1ddcbbf37b56a01b, 0xdcc2e77d4935d66a, 0x1c4f73f2c6a57f0a}}},
+{{{0x0e7a4fbd305fa0bb, 0x829d4ce054c663ad, 0xf421c3832fe33848, 0x795ac80d1bf64c42}},
+ {{0xb36e706efc7c3484, 0x73dfc9b4c3c1cf61, 0xeb1d79c9781cc7e5, 0x70459adb7daf675c}},
+ {{0x1b91db4991b42bb3, 0x572696234b02dcca, 0x9fdf9ee51f8c78dc, 0x5fe162848ce21fd3}}},
+{{{0xe2790aae4d077c41, 0x8b938270db7469a3, 0x6eb632dc8abd16a2, 0x720814ecaa064b72}},
+ {{0x315c29c795115389, 0xd7e0e507862f74ce, 0x0c4a762185927432, 0x72de6c984a25a1e4}},
+ {{0xae9ab553bf6aa310, 0x050a50a9806d6e1b, 0x92bb7403adff5139, 0x0394d27645be618b}}},
+{{{0x4d572251857eedf4, 0xe3724edde19e93c5, 0x8a71420e0b797035, 0x3b3c833687abe743}},
+ {{0xf5396425b23545a4, 0x15a7a27e98fbb296, 0xab6c52bc636fdd86, 0x79d995a8419334ee}},
+ {{0xcd8a8ea61195dd75, 0xa504d8a81dd9a82f, 0x540dca81a35879b6, 0x60dd16a379c86a8a}}},
+{{{0x35a2c8487381e559, 0x596ffea6d78082cb, 0xcb9771ebdba7b653, 0x5a08b5019b4da685}},
+ {{0x3501d6f8153e47b8, 0xb7a9675414a2f60c, 0x112ee8b6455d9523, 0x4e62a3c18112ea8a}},
+ {{0xc8d4ac04516ab786, 0x595af3215295b23d, 0xd6edd234db0230c1, 0x0929efe8825b41cc}}},
+{{{0x5f0601d1cbd0f2d3, 0x736e412f6132bb7f, 0x83604432238dde87, 0x1e3a5272f5c0753c}},
+ {{0x8b3172b7ad56651d, 0x01581b7a3fabd717, 0x2dc94df6424df6e4, 0x30376e5d2c29284f}},
+ {{0xd2918da78159a59c, 0x6bdc1cd93f0713f3, 0x565f7a934acd6590, 0x53daacec4cb4c128}}},
+{{{0x4ca73bd79cc8a7d6, 0x4d4a738f47e9a9b2, 0xf4cbf12942f5fe00, 0x01a13ff9bdbf0752}},
+ {{0x99852bc3852cfdb0, 0x2cc12e9559d6ed0b, 0x70f9e2bf9b5ac27b, 0x4f3b8c117959ae99}},
+ {{0x55b6c9c82ff26412, 0x1ac4a8c91fb667a8, 0xd527bfcfeb778bf2, 0x303337da7012a3be}}},
+{{{0x955422228c1c9d7c, 0x01fac1371a9b340f, 0x7e8d9177925b48d7, 0x53f8ad5661b3e31b}},
+ {{0x976d3ccbfad2fdd1, 0xcb88839737a640a8, 0x2ff00c1d6734cb25, 0x269ff4dc789c2d2b}},
+ {{0x0c003fbdc08d678d, 0x4d982fa37ead2b17, 0xc07e6bcdb2e582f1, 0x296c7291df412a44}}},
+{{{0x7903de2b33daf397, 0xd0ff0619c9a624b3, 0x8a1d252b555b3e18, 0x2b6d581c52e0b7c0}},
+ {{0xdfb23205dab8b59e, 0x465aeaa0c8092250, 0xd133c1189a725d18, 0x2327370261f117d1}},
+ {{0x3d0543d3623e7986, 0x679414c2c278a354, 0xae43f0cc726196f6, 0x7836c41f8245eaba}}},
+{{{0xe7a254db49e95a81, 0x5192d5d008b0ad73, 0x4d20e5b1d00afc07, 0x5d55f8012cf25f38}},
+ {{0xca651e848011937c, 0xc6b0c46e6ef41a28, 0xb7021ba75f3f8d52, 0x119dff99ead7b9fd}},
+ {{0x43eadfcbf4b31d4d, 0xc6503f7411148892, 0xfeee68c5060d3b17, 0x329293b3dd4a0ac8}}},
+{{{0x4e59214fe194961a, 0x49be7dc70d71cd4f, 0x9300cfd23b50f22d, 0x4789d446fc917232}},
+ {{0x2879852d5d7cb208, 0xb8dedd70687df2e7, 0xdc0bffab21687891, 0x2b44c043677daa35}},
+ {{0x1a1c87ab074eb78e, 0xfac6d18e99daf467, 0x3eacbbcd484f9067, 0x60c52eef2bb9a4e4}}},
+{{{0x0b5d89bc3bfd8bf1, 0xb06b9237c9f3551a, 0x0e4c16b0d53028f5, 0x10bc9c312ccfcaab}},
+ {{0x702bc5c27cae6d11, 0x44c7699b54a48cab, 0xefbc4056ba492eb2, 0x70d77248d9b6676d}},
+ {{0xaa8ae84b3ec2a05b, 0x98699ef4ed1781e0, 0x794513e4708e85d1, 0x63755bd3a976f413}}},
+{{{0xb55fa03e2ad10853, 0x356f75909ee63569, 0x9ff9f1fdbe69b890, 0x0d8cc1c48bc16f84}},
+ {{0x3dc7101897f1acb7, 0x5dda7d5ec165bbd8, 0x508e5b9c0fa1020f, 0x2763751737c52a56}},
+ {{0x029402d36eb419a9, 0xf0b44e7e77b460a5, 0xcfa86230d43c4956, 0x70c2dd8a7ad166e7}}},
+{{{0x656194509f6fec0e, 0xee2e7ea946c6518d, 0x9733c1f367e09b5c, 0x2e0fac6363948495}},
+ {{0x91d4967db8ed7e13, 0x74252f0ad776817a, 0xe40982e00d852564, 0x32b8613816a53ce5}},
+ {{0x79e7f7bee448cd64, 0x6ac83a67087886d0, 0xf89fd4d9a0e4db2e, 0x4179215c735a4f41}}},
+{{{0x8c7094e7d7dced2a, 0x97fb8ac347d39c70, 0xe13be033a906d902, 0x700344a30cd99d76}},
+ {{0xe4ae33b9286bcd34, 0xb7ef7eb6559dd6dc, 0x278b141fb3d38e1f, 0x31fa85662241c286}},
+ {{0xaf826c422e3622f4, 0xc12029879833502d, 0x9bc1b7e12b389123, 0x24bb2312a9952489}}},
+{{{0xb1a8ed1732de67c3, 0x3cb49418461b4948, 0x8ebd434376cfbcd2, 0x0fee3e871e188008}},
+ {{0x41f80c2af5f85c6b, 0x687284c304fa6794, 0x8945df99a3ba1bad, 0x0d1d2af9ffeb5d16}},
+ {{0xa9da8aa132621edf, 0x30b822a159226579, 0x4004197ba79ac193, 0x16acd79718531d76}}},
+{{{0x72df72af2d9b1d3d, 0x63462a36a432245a, 0x3ecea07916b39637, 0x123e0ef6b9302309}},
+ {{0xc959c6c57887b6ad, 0x94e19ead5f90feba, 0x16e24e62a342f504, 0x164ed34b18161700}},
+ {{0x487ed94c192fe69a, 0x61ae2cea3a911513, 0x877bf6d3b9a4de27, 0x78da0fc61073f3eb}}},
+{{{0x5bf15d28e52bc66a, 0x2c47e31870f01a8e, 0x2419afbc06c28bdd, 0x2d25deeb256b173a}},
+ {{0xa29f80f1680c3a94, 0x71f77e151ae9e7e6, 0x1100f15848017973, 0x054aa4b316b38ddd}},
+ {{0xdfc8468d19267cb8, 0x0b28789c66e54daf, 0x2aeb1d2a666eec17, 0x134610a6ab7da760}}},
+{{{0xcaf55ec27c59b23f, 0x99aeed3e154d04f2, 0x68441d72e14141f4, 0x140345133932a0a2}},
+ {{0xd91430e0dc028c3c, 0x0eb955a85217c771, 0x4b09e1ed2c99a1fa, 0x42881af2bd6a743c}},
+ {{0x7bfec69aab5cad3d, 0xc23e8cd34cb2cfad, 0x685dd14bfb37d6a2, 0x0ad6d64415677a18}}},
+{{{0x781a439e417becb5, 0x4ac5938cd10e0266, 0x5da385110692ac24, 0x11b065a2ade31233}},
+ {{0x7914892847927e9f, 0x33dad6ef370aa877, 0x1f8f24fa11122703, 0x5265ac2f2adf9592}},
+ {{0x405fdd309afcb346, 0xd9723d4428e63f54, 0x94c01df05f65aaae, 0x43e4dc3ae14c0809}}},
+{{{0xbc12c7f1a938a517, 0x473028ab3180b2e1, 0x3f78571efbcd254a, 0x74e534426ff6f90f}},
+ {{0xea6f7ac3adc2c6a3, 0xd0e928f6e9717c94, 0xe2d379ead645eaf5, 0x46dd8785c51ffbbe}},
+ {{0x709801be375c8898, 0x4b06dab5e3fd8348, 0x75880ced27230714, 0x2b09468fdd2f4c42}}},
+{{{0x97c749eeb701cb96, 0x83f438d4b6a369c3, 0x62962b8b9a402cd9, 0x6976c7509888df7b}},
+ {{0x5b97946582ffa02a, 0xda096a51fea8f549, 0xa06351375f77af9b, 0x1bcfde61201d1e76}},
+ {{0x4a4a5490246a59a2, 0xd63ebddee87fdd90, 0xd9437c670d2371fa, 0x69e87308d30f8ed6}}},
+{{{0x435a8bb15656beb0, 0xf8fac9ba4f4d5bca, 0xb9b278c41548c075, 0x3eb0ef76e892b622}},
+ {{0x0f80bf028bc80303, 0x6aae16b37a18cefb, 0xdd47ea47d72cd6a3, 0x61943588f4ed39aa}},
+ {{0xd26e5c3e91039f85, 0xc0e9e77df6f33aa9, 0xe8968c5570066a93, 0x3c34d1881faaaddd}}},
+{{{0x3f9d2b5ea09f9ec0, 0x1dab3b6fb623a890, 0xa09ba3ea72d926c4, 0x374193513fd8b36d}},
+ {{0xbd5b0b8f2fffe0d9, 0x6aa254103ed24fb9, 0x2ac7d7bcb26821c4, 0x605b394b60dca36a}},
+ {{0xb4e856e45a9d1ed2, 0xefe848766c97a9a2, 0xb104cf641e5eee7d, 0x2f50b81c88a71c8f}}},
+{{{0x31723c61fc6811bb, 0x9cb450486211800f, 0x768933d347995753, 0x3491a53502752fcd}},
+ {{0x2b552ca0a7da522a, 0x3230b336449b0250, 0xf2c4c5bca4b99fb9, 0x7b2c674958074a22}},
+ {{0xd55165883ed28cdf, 0x12d84fd2d362de39, 0x0a874ad3e3378e4f, 0x000d2b1f7c763e74}}},
+{{{0x3d420811d06d4a67, 0xbefc048590e0ffe3, 0xf870c6b7bd487bde, 0x6e2a7316319afa28}},
+ {{0x9624778c3e94a8ab, 0x0ad6f3cee9a78bec, 0x948ac7810d743c4f, 0x76627935aaecfccc}},
+ {{0x56a8ac24d6d59a9f, 0xc8db753e3096f006, 0x477f41e68f4c5299, 0x588d851cf6c86114}}},
+{{{0x51138ec78df6b0fe, 0x5397da89e575f51b, 0x09207a1d717af1b9, 0x2102fdba2b20d650}},
+ {{0xcd2a65e777d1f515, 0x548991878faa60f1, 0xb1b73bbcdabc06e5, 0x654878cba97cc9fb}},
+ {{0x969ee405055ce6a1, 0x36bca7681251ad29, 0x3a1af517aa7da415, 0x0ad725db29ecb2ba}}},
+{{{0xdc4267b1834e2457, 0xb67544b570ce1bc5, 0x1af07a0bf7d15ed7, 0x4aefcffb71a03650}},
+ {{0xfec7bc0c9b056f85, 0x537d5268e7f5ffd7, 0x77afc6624312aefa, 0x4f675f5302399fd9}},
+ {{0xc32d36360415171e, 0xcd2bef118998483b, 0x870a6eadd0945110, 0x0bccbb72a2a86561}}},
+{{{0x185e962feab1a9c8, 0x86e7e63565147dcd, 0xb092e031bb5b6df2, 0x4024f0ab59d6b73e}},
+ {{0x186d5e4c50fe1296, 0xe0397b82fee89f7e, 0x3bc7f6c5507031b0, 0x6678fd69108f37c2}},
+ {{0x1586fa31636863c2, 0x07f68c48572d33f2, 0x4f73cc9f789eaefc, 0x2d42e2108ead4701}}},
+{{{0x97f5131594dfd29b, 0x6155985d313f4c6a, 0xeba13f0708455010, 0x676b2608b8d2d322}},
+ {{0x21717b0d0f537593, 0x914e690b131e064c, 0x1bb687ae752ae09f, 0x420bf3a79b423c6e}},
+ {{0x8138ba651c5b2b47, 0x8671b6ec311b1b80, 0x7bff0cb1bc3135b0, 0x745d2ffa9c0cf1e0}}},
+{{{0xbf525a1e2bc9c8bd, 0xea5b260826479d81, 0xd511c70edf0155db, 0x1ae23ceb960cf5d0}},
+ {{0x6036df5721d34e6a, 0xb1db8827997bb3d0, 0xd3c209c3c8756afa, 0x06e15be54c1dc839}},
+ {{0x5b725d871932994a, 0x32351cb5ceb1dab0, 0x7dc41549dab7ca05, 0x58ded861278ec1f7}}},
+{{{0xd8173793f266c55c, 0xc8c976c5cc454e49, 0x5ce382f8bc26c3a8, 0x2ff39de85485f6f9}},
+ {{0x2dfb5ba8b6c2c9a8, 0x48eeef8ef52c598c, 0x33809107f12d1573, 0x08ba696b531d5bd8}},
+ {{0x77ed3eeec3efc57a, 0x04e05517d4ff4811, 0xea3d7a3ff1a671cb, 0x120633b4947cfe54}}},
+{{{0x0b94987891610042, 0x4ee7b13cecebfae8, 0x70be739594f0a4c0, 0x35d30a99b4d59185}},
+ {{0x82bd31474912100a, 0xde237b6d7e6fbe06, 0xe11e761911ea79c6, 0x07433be3cb393bde}},
+ {{0xff7944c05ce997f4, 0x575d3de4b05c51a3, 0x583381fd5a76847c, 0x2d873ede7af6da9f}}},
+{{{0x157a316443373409, 0xfab8b7eef4aa81d9, 0xb093fee6f5a64806, 0x2e773654707fa7b6}},
+ {{0xaa6202e14e5df981, 0xa20d59175015e1f5, 0x18a275d3bae21d6c, 0x0543618a01600253}},
+ {{0x0deabdf4974c23c1, 0xaa6f0a259dce4693, 0x04202cb8a29aba2c, 0x4b1443362d07960d}}},
+{{{0x47b837f753242cec, 0x256dc48cc04212f2, 0xe222fbfbe1d928c5, 0x48ea295bad8a2c07}},
+ {{0x299b1c3f57c5715e, 0x96cb929e6b686d90, 0x3004806447235ab3, 0x2c435c24a44d9fe1}},
+ {{0x0607c97c80f8833f, 0x0e851578ca25ec5b, 0x54f7450b161ebb6f, 0x7bcb4792a0def80e}}},
+{{{0x8487e3d02bc73659, 0x4baf8445059979df, 0xd17c975adcad6fbf, 0x57369f0bdefc96b6}},
+ {{0x1cecd0a0045224c2, 0x757f1b1b69e53952, 0x775b7a925289f681, 0x1b6cc62016736148}},
+ {{0xf1a9990175638698, 0x353dd1beeeaa60d3, 0x849471334c9ba488, 0x63fa6e6843ade311}}},
+{{{0xd15c20536597c168, 0x9f73740098d28789, 0x18aee7f13257ba1f, 0x3418bfda07346f14}},
+ {{0x2195becdd24b5eb7, 0x5e41f18cc0cd44f9, 0xdf28074441ca9ede, 0x07073b98f35b7d67}},
+ {{0xd03c676c4ce530d4, 0x0b64c0473b5df9f4, 0x065cef8b19b3a31e, 0x3084d661533102c9}}},
+{{{0xe1f6b79ebf8469ad, 0x15801004e2663135, 0x9a498330af74181b, 0x3ba2504f049b673c}},
+ {{0x9a6ce876760321fd, 0x7fe2b5109eb63ad8, 0x00e7d4ae8ac80592, 0x73d86b7abb6f723a}},
+ {{0x0b52b5606dba5ab6, 0xa9134f0fbbb1edab, 0x30a9520d9b04a635, 0x6813b8f37973e5db}}},
+{{{0x9854b054334127c1, 0x105d047882fbff25, 0xdb49f7f944186f4f, 0x1768e838bed0b900}},
+ {{0xf194ca56f3157e29, 0x136d35705ef528a5, 0xdd4cef778b0599bc, 0x7d5472af24f833ed}},
+ {{0xd0ef874daf33da47, 0x00d3be5db6e339f9, 0x3f2a8a2f9c9ceece, 0x5d1aeb792352435a}}},
+{{{0xf59e6bb319cd63ca, 0x670c159221d06839, 0xb06d565b2150cab6, 0x20fb199d104f12a3}},
+ {{0x12c7bfaeb61ba775, 0xb84e621fe263bffd, 0x0b47a5c35c840dcf, 0x7e83be0bccaf8634}},
+ {{0x61943dee6d99c120, 0x86101f2e460b9fe0, 0x6bb2f1518ee8598d, 0x76b76289fcc475cc}}},
+{{{0x791b4cc1756286fa, 0xdbced317d74a157c, 0x7e732421ea72bde6, 0x01fe18491131c8e9}},
+ {{0x4245f1a1522ec0b3, 0x558785b22a75656d, 0x1d485a2548a1b3c0, 0x60959eccd58fe09f}},
+ {{0x3ebfeb7ba8ed7a09, 0x49fdc2bbe502789c, 0x44ebce5d3c119428, 0x35e1eb55be947f4a}}},
+{{{0xdbdae701c5738dd3, 0xf9c6f635b26f1bee, 0x61e96a8042f15ef4, 0x3aa1d11faf60a4d8}},
+ {{0x14fd6dfa726ccc74, 0x3b084cfe2f53b965, 0xf33ae4f552a2c8b4, 0x59aab07a0d40166a}},
+ {{0x77bcec4c925eac25, 0x1848718460137738, 0x5b374337fea9f451, 0x1865e78ec8e6aa46}}},
+{{{0xccc4b7c7b66e1f7a, 0x44157e25f50c2f7e, 0x3ef06dfc713eaf1c, 0x582f446752da63f7}},
+ {{0x967c54e91c529ccb, 0x30f6269264c635fb, 0x2747aff478121965, 0x17038418eaf66f5c}},
+ {{0xc6317bd320324ce4, 0xa81042e8a4488bc4, 0xb21ef18b4e5a1364, 0x0c2a1c4bcda28dc9}}},
+{{{0xd24dc7d06f1f0447, 0xb2269e3edb87c059, 0xd15b0272fbb2d28f, 0x7c558bd1c6f64877}},
+ {{0xedc4814869bd6945, 0x0d6d907dbe1c8d22, 0xc63bd212d55cc5ab, 0x5a6a9b30a314dc83}},
+ {{0xd0ec1524d396463d, 0x12bb628ac35a24f0, 0xa50c3a791cbc5fa4, 0x0404a5ca0afbafc3}}},
+{{{0x8c1f40070aa743d6, 0xccbad0cb5b265ee8, 0x574b046b668fd2de, 0x46395bfdcadd9633}},
+ {{0x62bc9e1b2a416fd1, 0xb5c6f728e350598b, 0x04343fd83d5d6967, 0x39527516e7f8ee98}},
+ {{0x117fdb2d1a5d9a9c, 0x9c7745bcd1005c2a, 0xefd4bef154d56fea, 0x76579a29e822d016}}},
+{{{0x45b68e7e49c02a17, 0x23cd51a2bca9a37f, 0x3ed65f11ec224c1b, 0x43a384dc9e05bdb1}},
+ {{0x333cb51352b434f2, 0xd832284993de80e1, 0xb5512887750d35ce, 0x02c514bb2a2777c1}},
+ {{0x684bd5da8bf1b645, 0xfb8bd37ef6b54b53, 0x313916d7a9b0d253, 0x1160920961548059}}},
+{{{0xb44d166929dacfaa, 0xda529f4c8413598f, 0xe9ef63ca453d5559, 0x351e125bc5698e0b}},
+ {{0x7a385616369b4dcd, 0x75c02ca7655c3563, 0x7dc21bf9d4f18021, 0x2f637d7491e6e042}},
+ {{0xd4b49b461af67bbe, 0xd603037ac8ab8961, 0x71dee19ff9a699fb, 0x7f182d06e7ce2a9a}}},
+{{{0x7a7c8e64ab0168ec, 0xcb5a4a5515edc543, 0x095519d347cd0eda, 0x67d4ac8c343e93b0}},
+ {{0x09454b728e217522, 0xaa58e8f4d484b8d8, 0xd358254d7f46903c, 0x44acc043241c5217}},
+ {{0x1c7d6bbb4f7a5777, 0x8b35fed4918313e1, 0x4adca1c6c96b4684, 0x556d1c8312ad71bd}}},
+{{{0x17ef40e30c8d3982, 0x31f7073e15a3fa34, 0x4f21f3cb0773646e, 0x746c6c6d1d824eff}},
+ {{0x81f06756b11be821, 0x0faff82310a3f3dd, 0xf8b2d0556a99465d, 0x097abe38cc8c7f05}},
+ {{0x0c49c9877ea52da4, 0x4c4369559bdc1d43, 0x022c3809f7ccebd2, 0x577e14a34bee84bd}}},
+{{{0xf0e268ac61a73b0a, 0xf2fafa103791a5f5, 0xc1e13e826b6d00e9, 0x60fa7ee96fd78f42}},
+ {{0x94fecebebd4dd72b, 0xf46a4fda060f2211, 0x124a5977c0c8d1ff, 0x705304b8fb009295}},
+ {{0xb63d1d354d296ec6, 0xf3c3053e5fad31d8, 0x670b958cb4bd42ec, 0x21398e0ca16353fd}}},
+{{{0x216ab2ca8da7d2ef, 0x366ad9dd99f42827, 0xae64b9004fdd3c75, 0x403a395b53909e62}},
+ {{0x86c5fc16861b7e9a, 0xf6a330476a27c451, 0x01667267a1e93597, 0x05ffb9cd6082dfeb}},
+ {{0xa617fa9ff53f6139, 0x60f2b5e513e66cb6, 0xd7a8beefb3448aa4, 0x7a2932856f5ea192}}},
+{{{0x0b39d761b02de888, 0x5f550e7ed2414e1f, 0xa6bfa45822e1a940, 0x050a2f7dfd447b99}},
+ {{0xb89c444879639302, 0x4ae4f19350c67f2c, 0xf0b35da8c81af9c6, 0x39d0003546871017}},
+ {{0x437c3b33a650db77, 0x6bafe81dbac52bb2, 0xfe99402d2db7d318, 0x2b5b7eec372ba6ce}}},
+{{{0xb3bc4bbd83f50eef, 0x508f0c998c927866, 0x43e76587c8b7e66e, 0x0f7655a3a47f98d9}},
+ {{0xa694404d613ac8f4, 0x500c3c2bfa97e72c, 0x874104d21fcec210, 0x1b205fb38604a8ee}},
+ {{0x55ecad37d24b133c, 0x441e147d6038c90b, 0x656683a1d62c6fee, 0x0157d5dc87e0ecae}}},
+{{{0xf2a7af510354c13d, 0xd7a0b145aa372b60, 0x2869b96a05a3d470, 0x6528e42d82460173}},
+ {{0x95265514d71eb524, 0xe603d8815df14593, 0x147cdf410d4de6b7, 0x5293b1730437c850}},
+ {{0x23d0e0814bccf226, 0x92c745cd8196fb93, 0x8b61796c59541e5b, 0x40a44df0c021f978}}},
+{{{0xdaa869894f20ea6a, 0xea14a3d14c620618, 0x6001fccb090bf8be, 0x35f4e822947e9cf0}},
+ {{0x86c96e514bc5d095, 0xf20d4098fca6804a, 0x27363d89c826ea5d, 0x39ca36565719cacf}},
+ {{0x97506f2f6f87b75c, 0xc624aea0034ae070, 0x1ec856e3aad34dd6, 0x055b0be0e440e58f}}},
+{{{0x6469a17d89735d12, 0xdb6f27d5e662b9f1, 0x9fcba3286a395681, 0x363b8004d269af25}},
+ {{0x4d12a04b6ea33da2, 0x57cf4c15e36126dd, 0x90ec9675ee44d967, 0x64ca348d2a985aac}},
+ {{0x99588e19e4c4912d, 0xefcc3b4e1ca5ce6b, 0x4522ea60fa5b98d5, 0x7064bbab1de4a819}}},
+{{{0xb919e1515a770641, 0xa9a2e2c74e7f8039, 0x7527250b3df23109, 0x756a7330ac27b78b}},
+ {{0xa290c06142542129, 0xf2e2c2aebe8d5b90, 0xcf2458db76abfe1b, 0x02157ade83d626bf}},
+ {{0x3e46972a1b9a038b, 0x2e4ee66a7ee03fb4, 0x81a248776edbb4ca, 0x1a944ee88ecd0563}}},
+{{{0xd5a91d1151039372, 0x2ed377b799ca26de, 0xa17202acfd366b6b, 0x0730291bd6901995}},
+ {{0xbb40a859182362d6, 0xb99f55778a4d1abb, 0x8d18b427758559f6, 0x26c20fe74d26235a}},
+ {{0x648d1d9fe9cc22f5, 0x66bc561928dd577c, 0x47d3ed21652439d1, 0x49d271acedaf8b49}}},
+{{{0x89f5058a382b33f3, 0x5ae2ba0bad48c0b4, 0x8f93b503a53db36e, 0x5aa3ed9d95a232e6}},
+ {{0x2798aaf9b4b75601, 0x5eac72135c8dad72, 0xd2ceaa6161b7a023, 0x1bbfb284e98f7d4e}},
+ {{0x656777e9c7d96561, 0xcb2b125472c78036, 0x65053299d9506eee, 0x4a07e14e5e8957cc}}},
+{{{0x4ee412cb980df999, 0xa315d76f3c6ec771, 0xbba5edde925c77fd, 0x3f0bac391d313402}},
+ {{0x240b58cdc477a49b, 0xfd38dade6447f017, 0x19928d32a7c86aad, 0x50af7aed84afa081}},
+ {{0x6e4fde0115f65be5, 0x29982621216109b2, 0x780205810badd6d9, 0x1921a316baebd006}}},
+{{{0x89422f7edfb870fc, 0x2c296beb4f76b3bd, 0x0738f1d436c24df7, 0x6458df41e273aeb0}},
+ {{0xd75aad9ad9f3c18b, 0x566a0eef60b1c19c, 0x3e9a0bac255c0ed9, 0x7b049deca062c7f5}},
+ {{0xdccbe37a35444483, 0x758879330fedbe93, 0x786004c312c5dd87, 0x6093dccbc2950e64}}},
+{{{0x1ff39a8585e0706d, 0x36d0a5d8b3e73933, 0x43b9f2e1718f453b, 0x57d1ea084827a97c}},
+ {{0x6bdeeebe6084034b, 0x3199c2b6780fb854, 0x973376abb62d0695, 0x6e3180c98b647d90}},
+ {{0xee7ab6e7a128b071, 0xa4c1596d93a88baa, 0xf7b4de82b2216130, 0x363e999ddd97bd18}}},
+{{{0x96a843c135ee1fc4, 0x976eb35508e4c8cf, 0xb42f6801b58cd330, 0x48ee9b78693a052b}},
+ {{0x2f1848dce24baec6, 0x769b7255babcaf60, 0x90cb3c6e3cefe931, 0x231f979bc6f9b355}},
+ {{0x5c31de4bcc2af3c6, 0xb04bb030fe208d1f, 0xb78d7009c14fb466, 0x079bfa9b08792413}}},
+{{{0xe3903a51da300df4, 0x843964233da95ab0, 0xed3cf12d0b356480, 0x038c77f684817194}},
+ {{0xf3c9ed80a2d54245, 0x0aa08b7877f63952, 0xd76dac63d1085475, 0x1ef4fb159470636b}},
+ {{0x854e5ee65b167bec, 0x59590a4296d0cdc2, 0x72b2df3498102199, 0x575ee92a4a0bff56}}},
+{{{0xd4c080908a182fcf, 0x30e170c299489dbd, 0x05babd5752f733de, 0x43d4e7112cd3fd00}},
+ {{0x5d46bc450aa4d801, 0xc3af1227a533b9d8, 0x389e3b262b8906c2, 0x200a1e7e382f581b}},
+ {{0x518db967eaf93ac5, 0x71bc989b056652c0, 0xfe2b85d9567197f5, 0x050eca52651e4e38}}},
+{{{0xc3431ade453f0c9c, 0xe9f5045eff703b9b, 0xfcd97ac9ed847b3d, 0x4b0ee6c21c58f4c6}},
+ {{0x97ac397660e668ea, 0x9b19bbfe153ab497, 0x4cb179b534eca79f, 0x6151c09fa131ae57}},
+ {{0x3af55c0dfdf05d96, 0xdd262ee02ab4ee7a, 0x11b2bb8712171709, 0x1fef24fa800f030b}}},
+{{{0xb496123a6b6c6609, 0xa750fe8580ab5938, 0xf471bf39b7c27a5f, 0x507903ce77ac193c}},
+ {{0xff91a66a90166220, 0xf22552ae5bf1e009, 0x7dff85d87f90df7c, 0x4f620ffe0c736fb9}},
+ {{0x62f90d65dfde3e34, 0xcf28c592b9fa5fad, 0x99c86ef9c6164510, 0x25d448044a256c84}}},
+{{{0xbd68230ec7e9b16f, 0x0eb1b9c1c1c5795d, 0x7943c8c495b6b1ff, 0x2f9faf620bbacf5e}},
+ {{0x2c7c4415c9022b55, 0x56a0d241812eb1fe, 0xf02ea1c9d7b65e0d, 0x4180512fd5323b26}},
+ {{0xa4ff3e698a48a5db, 0xba6a3806bd95403b, 0x9f7ce1af47d5b65d, 0x15e087e55939d2fb}}},
+{{{0x12207543745c1496, 0xdaff3cfdda38610c, 0xe4e797272c71c34f, 0x39c07b1934bdede9}},
+ {{0x8894186efb963f38, 0x48a00e80dc639bd5, 0xa4e8092be96c1c99, 0x5a097d54ca573661}},
+ {{0x2d45892b17c9e755, 0xd033fd7289308df8, 0x6c2fe9d9525b8bd9, 0x2edbecf1c11cc079}}},
+{{{0x1616a4e3c715a0d2, 0x53623cb0f8341d4d, 0x96ef5329c7e899cb, 0x3d4e8dbba668baa6}},
+ {{0xee0f0fddd087a25f, 0x9c7531555c3e34ee, 0x660c572e8fab3ab5, 0x0854fc44544cd3b2}},
+ {{0x61eba0c555edad19, 0x24b533fef0a83de6, 0x3b77042883baa5f8, 0x678f82b898a47e8d}}},
+{{{0xb1491d0bd6900c54, 0x3539722c9d132636, 0x4db928920b362bc9, 0x4d7cd1fea68b69df}},
+ {{0x1e09d94057775696, 0xeed1265c3cd951db, 0xfa9dac2b20bce16f, 0x0f7f76e0e8d089f4}},
+ {{0x36d9ebc5d485b00c, 0xa2596492e4adb365, 0xc1659480c2119ccd, 0x45306349186e0d5f}}},
+{{{0x94ddd0c1a6cdff1d, 0x55f6f115e84213ae, 0x6c935f85992fcf6a, 0x067ee0f54a37f16f}},
+ {{0x96a414ec2b072491, 0x1bb2218127a7b65b, 0x6d2849596e8a4af0, 0x65f3b08ccd27765f}},
+ {{0xecb29fff199801f7, 0x9d361d1fa2a0f72f, 0x25f11d2375fd2f49, 0x124cefe80fe10fe2}}},
+{{{0x4c126cf9d18df255, 0xc1d471e9147a63b6, 0x2c6d3c73f3c93b5f, 0x6be3a6a2e3ff86a2}},
+ {{0x1518e85b31b16489, 0x8faadcb7db710bfb, 0x39b0bdf4a14ae239, 0x05f4cbea503d20c1}},
+ {{0xce040e9ec04145bc, 0xc71ff4e208f6834c, 0xbd546e8dab8847a3, 0x64666aa0a4d2aba5}}},
+{{{0x6841435a7c06d912, 0xca123c21bb3f830b, 0xd4b37b27b1cbe278, 0x1d753b84c76f5046}},
+ {{0xb0c53bf73337e94c, 0x7cb5697e11e14f15, 0x4b84abac1930c750, 0x28dd4abfe0640468}},
+ {{0x7dc0b64c44cb9f44, 0x18a3e1ace3925dbf, 0x7a3034862d0457c4, 0x4c498bf78a0c892e}}},
+{{{0x37d653fb1aa73196, 0x0f9495303fd76418, 0xad200b09fb3a17b2, 0x544d49292fc8613e}},
+ {{0x22d2aff530976b86, 0x8d90b806c2d24604, 0xdca1896c4de5bae5, 0x28005fe6c8340c17}},
+ {{0x6aefba9f34528688, 0x5c1bff9425107da1, 0xf75bbbcd66d94b36, 0x72e472930f316dfa}}},
+{{{0x2695208c9781084f, 0xb1502a0b23450ee1, 0xfd9daea603efde02, 0x5a9d2e8c2733a34c}},
+ {{0x07f3f635d32a7627, 0x7aaa4d865f6566f0, 0x3c85e79728d04450, 0x1fee7f000fe06438}},
+ {{0x765305da03dbf7e5, 0xa4daf2491434cdbd, 0x7b4ad5cdd24a88ec, 0x00f94051ee040543}}},
+{{{0x8d356b23c3d330b2, 0xf21c8b9bb0471b06, 0xb36c316c6e42b83c, 0x07d79c7e8beab10d}},
+ {{0xd7ef93bb07af9753, 0x583ed0cf3db766a7, 0xce6998bf6e0b1ec5, 0x47b7ffd25dd40452}},
+ {{0x87fbfb9cbc08dd12, 0x8a066b3ae1eec29b, 0x0d57242bdb1fc1bf, 0x1c3520a35ea64bb6}}},
+{{{0x80d253a6bccba34a, 0x3e61c3a13838219b, 0x90c3b6019882e396, 0x1c3d05775d0ee66f}},
+ {{0xcda86f40216bc059, 0x1fbb231d12bcd87e, 0xb4956a9e17c70990, 0x38750c3b66d12e55}},
+ {{0x692ef1409422e51a, 0xcbc0c73c2b5df671, 0x21014fe7744ce029, 0x0621e2c7d330487c}}},
+{{{0xaf9860cc8259838d, 0x90ea48c1c69f9adc, 0x6526483765581e30, 0x0007d6097bd3a5bc}},
+ {{0xb7ae1796b0dbf0f3, 0x54dfafb9e17ce196, 0x25923071e9aaa3b4, 0x5d8e589ca1002e9d}},
+ {{0xc0bf1d950842a94b, 0xb2d3c363588f2e3e, 0x0a961438bb51e2ef, 0x1583d7783c1cbf86}}},
+{{{0xeceea2ef5da27ae1, 0x597c3a1455670174, 0xc9a62a126609167a, 0x252a5f2e81ed8f70}},
+ {{0x90034704cc9d28c7, 0x1d1b679ef72cc58f, 0x16e12b5fbe5b8726, 0x4958064e83c5580a}},
+ {{0x0d2894265066e80d, 0xfcc3f785307c8c6b, 0x1b53da780c1112fd, 0x079c170bd843b388}}},
+{{{0x0506ece464fa6fff, 0xbee3431e6205e523, 0x3579422451b8ea42, 0x6dec05e34ac9fb00}},
+ {{0xcdd6cd50c0d5d056, 0x9af7686dbb03573b, 0x3ca6723ff3c3ef48, 0x6768c0d7317b8acc}},
+ {{0x94b625e5f155c1b3, 0x417bf3a7997b7b91, 0xc22cbddc6d6b2600, 0x51445e14ddcd52f4}}},
+{{{0x57502b4b3b144951, 0x8e67ff6b444bbcb3, 0xb8bd6927166385db, 0x13186f31e39295c8}},
+ {{0x893147ab2bbea455, 0x8c53a24f92079129, 0x4b49f948be30f7a7, 0x12e990086e4fd43d}},
+ {{0xf10c96b37fdfbb2e, 0x9f9a935e121ceaf9, 0xdf1136c43a5b983f, 0x77b2e3f05d3e99af}}},
+{{{0xfd0d75879cf12657, 0xe82fef94e53a0e29, 0xcc34a7f05bbb4be7, 0x0b251172a50c38a2}},
+ {{0x9532f48fcc5cd29b, 0x2ba851bea3ce3671, 0x32dacaa051122941, 0x478d99d9350004f2}},
+ {{0x1d5ad94890bb02c0, 0x50e208b10ec25115, 0xa26a22894ef21702, 0x4dc923343b524805}}},
+{{{0xe3828c400f8086b6, 0x3f77e6f7979f0dc8, 0x7ef6de304df42cb4, 0x5265797cb6abd784}},
+ {{0x3ad3e3ebf36c4975, 0xd75d25a537862125, 0xe873943da025a516, 0x6bbc7cb4c411c847}},
+ {{0x3c6f9cd1d4a50d56, 0xb6244077c6feab7e, 0x6ff9bf483580972e, 0x00375883b332acfb}}},
+{{{0x0001b2cd28cb0940, 0x63fb51a06f1c24c9, 0xb5ad8691dcd5ca31, 0x67238dbd8c450660}},
+ {{0xc98bec856c75c99c, 0xe44184c000e33cf4, 0x0a676b9bba907634, 0x669e2cb571f379d7}},
+ {{0xcb116b73a49bd308, 0x025aad6b2392729e, 0xb4793efa3f55d9b1, 0x72a1056140678bb9}}},
+{{{0xa2b6812b1cc9249d, 0x62866eee21211f58, 0x2cb5c5b85df10ece, 0x03a6b259e263ae00}},
+ {{0x0d8d2909e2e505b6, 0x98ca78abc0291230, 0x77ef5569a9b12327, 0x7c77897b81439b47}},
+ {{0xf1c1b5e2de331cb5, 0x5a9f5d8e15fca420, 0x9fa438f17bd932b1, 0x2a381bf01c6146e7}}},
+{{{0xac9b9879cfc811c1, 0x8b7d29813756e567, 0x50da4e607c70edfc, 0x5dbca62f884400b6}},
+ {{0xf7c0be32b534166f, 0x27e6ca6419cf70d4, 0x934df7d7a957a759, 0x5701461dabdec2aa}},
+ {{0x2c6747402c915c25, 0x1bdcd1a80b0d340a, 0x5e5601bd07b43f5f, 0x2555b4e05539a242}}},
+{{{0x6fc09f5266ddd216, 0xdce560a7c8e37048, 0xec65939da2df62fd, 0x7a869ae7e52ed192}},
+ {{0x78409b1d87e463d4, 0xad4da95acdfb639d, 0xec28773755259b9c, 0x69c806e9c31230ab}},
+ {{0x7b48f57414bb3f22, 0x68c7cee4aedccc88, 0xed2f936179ed80be, 0x25d70b885f77bc4b}}},
+{{{0x4151c3d9762bf4de, 0x083f435f2745d82b, 0x29775a2e0d23ddd5, 0x138e3a6269a5db24}},
+ {{0x98459d29bb1ae4d4, 0x56b9c4c739f954ec, 0x832743f6c29b4b3e, 0x21ea8e2798b6878a}},
+ {{0x87bef4b46a5a7b9c, 0xd2299d1b5fc1d062, 0x82409818dd321648, 0x5c5abeb1e5a2e03d}}},
+{{{0x14722af4b73c2ddb, 0xbc470c5f5a05060d, 0x00943eac2581b02e, 0x0e434b3b1f499c8f}},
+ {{0x02cde6de1306a233, 0x7b5a52a2116f8ec7, 0xe1c681f4c1163b5b, 0x241d350660d32643}},
+ {{0x6be4404d0ebc52c7, 0xae46233bb1a791f5, 0x2aec170ed25db42b, 0x1d8dfd966645d694}}},
+{{{0x296fa9c59c2ec4de, 0xbc8b61bf4f84f3cb, 0x1c7706d917a8f908, 0x63b795fc7ad3255d}},
+ {{0xd598639c12ddb0a4, 0xa5d19f30c024866b, 0xd17c2f0358fce460, 0x07a195152e095e8a}},
+ {{0xa8368f02389e5fc8, 0x90433b02cf8de43b, 0xafa1fd5dc5412643, 0x3e8fe83d032f0137}}},
+{{{0x2f8b15b90570a294, 0x94f2427067084549, 0xde1c5ae161bbfd84, 0x75ba3b797fac4007}},
+ {{0x08704c8de8efd13c, 0xdfc51a8e33e03731, 0xa59d5da51260cde3, 0x22d60899a6258c86}},
+ {{0x6239dbc070cdd196, 0x60fe8a8b6c7d8a9a, 0xb38847bceb401260, 0x0904d07b87779e5e}}},
+{{{0xb4ce1fd4ddba919c, 0xcf31db3ec74c8daa, 0x2c63cc63ad86cc51, 0x43e2143fbc1dde07}},
+ {{0xf4322d6648f940b9, 0x06952f0cbd2d0c39, 0x167697ada081f931, 0x6240aacebaf72a6c}},
+ {{0xf834749c5ba295a0, 0xd6947c5bca37d25a, 0x66f13ba7e7c9316a, 0x56bdaf238db40cac}}},
+{{{0x362ab9e3f53533eb, 0x338568d56eb93d40, 0x9e0e14521d5a5572, 0x1d24a86d83741318}},
+ {{0x1310d36cc19d3bb2, 0x062a6bb7622386b9, 0x7c9b8591d7a14f5c, 0x03aa31507e1e5754}},
+ {{0xf4ec7648ffd4ce1f, 0xe045eaf054ac8c1c, 0x88d225821d09357c, 0x43b261dc9aeb4859}}},
+{{{0xe55b1e1988bb79bb, 0xa09ed07dc17a359d, 0xb02c2ee2603dea33, 0x326055cf5b276bc2}},
+ {{0x19513d8b6c951364, 0x94fe7126000bf47b, 0x028d10ddd54f9567, 0x02b4d5e242940964}},
+ {{0xb4a155cb28d18df2, 0xeacc4646186ce508, 0xc49cf4936c824389, 0x27a6c809ae5d3410}}},
+{{{0x8ba6ebcd1f0db188, 0x37d3d73a675a5be8, 0xf22edfa315f5585a, 0x2cb67174ff60a17e}},
+ {{0xcd2c270ac43d6954, 0xdd4a3e576a66cab2, 0x79fa592469d7036c, 0x221503603d8c2599}},
+ {{0x59eecdf9390be1d0, 0xa9422044728ce3f1, 0x82891c667a94f0f4, 0x7b1df4b73890f436}}},
+{{{0xe492f2e0b3b2a224, 0x7c6c9e062b551160, 0x15eb8fe20d7f7b0e, 0x61fcef2658fc5992}},
+ {{0x5f2e221807f8f58c, 0xe3555c9fd49409d4, 0xb2aaa88d1fb6a630, 0x68698245d352e03d}},
+ {{0xdbb15d852a18187a, 0xf3e4aad386ddacd7, 0x44bae2810ff6c482, 0x46cf4c473daf01cf}}},
+{{{0x426525ed9ec4e5f9, 0x0e5eda0116903303, 0x72b1a7f2cbe5cadc, 0x29387bcd14eb5f40}},
+ {{0x213c6ea7f1498140, 0x7c1e7ef8392b4854, 0x2488c38c5629ceba, 0x1065aae50d8cc5bb}},
+ {{0x1c2c4525df200d57, 0x5c3b2dd6bfca674a, 0x0a07e7b1e1834030, 0x69a198e64f1ce716}}},
+{{{0x7afcd613efa9d697, 0x0cc45aa41c067959, 0xa56fe104c1fada96, 0x3a73b70472e40365}},
+ {{0x7b26e56b9e2d4734, 0xc4c7132b81c61675, 0xef5c9525ec9cde7f, 0x39c80b16e71743ad}},
+ {{0x0f196e0d1b826c68, 0xf71ff0e24960e3db, 0x6113167023b7436c, 0x0cf0ea5877da7282}}},
+{{{0x196c80a4ddd4ccbd, 0x22e6f55d95f2dd9d, 0xc75e33c740d6c71b, 0x7bb51279cb3c042f}},
+ {{0xe332ced43ba6945a, 0xde0b1361e881c05d, 0x1ad40f095e67ed3b, 0x5da8acdab8c63d5d}},
+ {{0xc4b6664a3a70159f, 0x76194f0f0a904e14, 0xa5614c39a4096c13, 0x6cd0ff50979feced}}},
+{{{0xc0e067e78f4428ac, 0x14835ab0a61135e3, 0xf21d14f338062935, 0x6390a4c8df04849c}},
+ {{0x7fecfabdb04ba18e, 0xd0fc7bfc3bddbcf7, 0xa41d486e057a131c, 0x641a4391f2223a61}},
+ {{0xc5c6b95aa606a8db, 0x914b7f9eb06825f1, 0x2a731f6b44fc9eff, 0x30ddf38562705cfc}}},
+{{{0x4e3dcbdad1bff7f9, 0xc9118e8220645717, 0xbacccebc0f189d56, 0x1b4822e9d4467668}},
+ {{0x33bef2bd68bcd52c, 0xc649dbb069482ef2, 0xb5b6ee0c41cb1aee, 0x5c294d270212a7e5}},
+ {{0xab360a7f25563781, 0x2512228a480f7958, 0xc75d05276114b4e3, 0x222d9625d976fe2a}}},
+{{{0x1c717f85b372ace1, 0x81930e694638bf18, 0x239cad056bc08b58, 0x0b34271c87f8fff4}},
+ {{0x0f94be7e0a344f85, 0xeb2faa8c87f22c38, 0x9ce1e75e4ee16f0f, 0x43e64e5418a08dea}},
+ {{0x8155e2521a35ce63, 0xbe100d4df912028e, 0xbff80bf8a57ddcec, 0x57342dc96d6bc6e4}}},
+{{{0xefeef065c8ce5998, 0xbf029510b5cbeaa2, 0x8c64a10620b7c458, 0x35134fb231c24855}},
+ {{0xf3c3bcb71e707bf6, 0x351d9b8c7291a762, 0x00502e6edad69a33, 0x522f521f1ec8807f}},
+ {{0x272c1f46f9a3902b, 0xc91ba3b799657bcc, 0xae614b304f8a1c0e, 0x7afcaad70b99017b}}},
+{{{0xc25ded54a4b8be41, 0x902d13e11bb0e2dd, 0x41f43233cde82ab2, 0x1085faa5c3aae7cb}},
+ {{0xa88141ecef842b6b, 0x55e7b14797abe6c5, 0x8c748f9703784ffe, 0x5b50a1f7afcd00b7}},
+ {{0x9b840f66f1361315, 0x18462242701003e9, 0x65ed45fae4a25080, 0x0a2862393fda7320}}},
+{{{0x46ab13c8347cbc9d, 0x3849e8d499c12383, 0x4cea314087d64ac9, 0x1f354134b1a29ee7}},
+ {{0x960e737b6ecb9d17, 0xfaf24948d67ceae1, 0x37e7a9b4d55e1b89, 0x5cb7173cb46c59eb}},
+ {{0x4a89e68b82b7abf0, 0xf41cd9279ba6b7b9, 0x16e6c210e18d876f, 0x7cacdb0f7f1b09c6}}},
+{{{0x9062b2e0d91a78bc, 0x47c9889cc8509667, 0x9df54a66405070b8, 0x7369e6a92493a1bf}},
+ {{0xe1014434dcc5caed, 0x47ed5d963c84fb33, 0x70019576ed86a0e7, 0x25b2697bd267f9e4}},
+ {{0x9d673ffb13986864, 0x3ca5fbd9415dc7b8, 0xe04ecc3bdf273b5e, 0x1420683db54e4cd2}}},
+{{{0xb478bd1e249dd197, 0x620c35005e58c102, 0xfb02d32fccbaac5c, 0x60b63bebf508a72d}},
+ {{0x34eebb6fc1cc5ad0, 0x6a1b0ce99646ac8b, 0xd3b0da49a66bde53, 0x31e83b4161d081c1}},
+ {{0x97e8c7129e062b4f, 0x49e48f4f29320ad8, 0x5bece14b6f18683f, 0x55cf1eb62d550317}}},
+{{{0x5879101065c23d58, 0x8b9d086d5094819c, 0xe2402fa912c55fa7, 0x669a6564570891d4}},
+ {{0x3076b5e37df58c52, 0xd73ab9dde799cc36, 0xbd831ce34913ee20, 0x1a56fbaa62ba0133}},
+ {{0x943e6b505c9dc9ec, 0x302557bba77c371a, 0x9873ae5641347651, 0x13c4836799c58a5c}}},
+{{{0x423a5d465ab3e1b9, 0xfc13c187c7f13f61, 0x19f83664ecb5b9b6, 0x66f80c93a637b607}},
+ {{0xc4dcfb6a5d8bd080, 0xdeebc4ec571a4842, 0xd4b2e883b8e55365, 0x50bdc87dc8e5b827}},
+ {{0x606d37836edfe111, 0x32353e15f011abd9, 0x64b03ac325b73b96, 0x1dd56444725fd5ae}}},
+{{{0x8fa47ff83362127d, 0xbc9f6ac471cd7c15, 0x6e71454349220c8b, 0x0e645912219f732e}},
+ {{0xc297e60008bac89a, 0x7d4cea11eae1c3e0, 0xf3e38be19fe7977c, 0x3a3a450f63a305cd}},
+ {{0x078f2f31d8394627, 0x389d3183de94a510, 0xd1e36c6d17996f80, 0x318c8d9393a9a87b}}},
+{{{0xf2745d032afffe19, 0x0c9f3c497f24db66, 0xbc98d3e3ba8598ef, 0x224c7c679a1d5314}},
+ {{0x5d669e29ab1dd398, 0xfc921658342d9e3b, 0x55851dfdf35973cd, 0x509a41c325950af6}},
+ {{0xbdc06edca6f925e9, 0x793ef3f4641b1f33, 0x82ec12809d833e89, 0x05bff02328a11389}}},
+{{{0x3632137023cae00b, 0x544acf0ad1accf59, 0x96741049d21a1c88, 0x780b8cc3fa2a44a7}},
+ {{0x6881a0dd0dc512e4, 0x4fe70dc844a5fafe, 0x1f748e6b8f4a5240, 0x576277cdee01a3ea}},
+ {{0x1ef38abc234f305f, 0x9a577fbd1405de08, 0x5e82a51434e62a0d, 0x5ff418726271b7a1}}},
+{{{0x398e080c1789db9d, 0xa7602025f3e778f5, 0xfa98894c06bd035d, 0x106a03dc25a966be}},
+ {{0xe5db47e813b69540, 0xf35d2a3b432610e1, 0xac1f26e938781276, 0x29d4db8ca0a0cb69}},
+ {{0xd9ad0aaf333353d0, 0x38669da5acd309e5, 0x3c57658ac888f7f0, 0x4ab38a51052cbefa}}},
+{{{0xdfdacbee4324c0e9, 0x054442883f955bb7, 0xdef7aaa8ea31609f, 0x68aee70642287cff}},
+ {{0xf68fe2e8809de054, 0xe3bc096a9c82bad1, 0x076353d40aadbf45, 0x7b9b1fb5dea1959e}},
+ {{0xf01cc8f17471cc0c, 0x95242e37579082bb, 0x27776093d3e46b5f, 0x2d13d55a28bd85fb}}},
+{{{0xfac5d2065b35b8da, 0xa8da8a9a85624bb7, 0xccd2ca913d21cd0f, 0x6b8341ee8bf90d58}},
+ {{0xbf019cce7aee7a52, 0xa8ded2b6e454ead3, 0x3c619f0b87a8bb19, 0x3619b5d7560916d8}},
+ {{0x3579f26b0282c4b2, 0x64d592f24fafefae, 0xb7cded7b28c8c7c0, 0x6a927b6b7173a8d7}}},
+{{{0x1f6db24f986e4656, 0x1021c02ed1e9105b, 0xf8ff3fff2cc0a375, 0x1d2a6bf8c6c82592}},
+ {{0x8d7040863ece88eb, 0xf0e307a980eec08c, 0xac2250610d788fda, 0x056d92a43a0d478d}},
+ {{0x1b05a196fc3da5a1, 0x77d7a8c243b59ed0, 0x06da3d6297d17918, 0x66fbb494f12353f7}}},
+{{{0x751a50b9d85c0fb8, 0xd1afdc258bcf097b, 0x2f16a6a38309a969, 0x14ddff9ee5b00659}},
+ {{0xd6d70996f12309d6, 0xdbfb2385e9c3d539, 0x46d602b0f7552411, 0x270a0b0557843e0c}},
+ {{0x61ff0640a7862bcc, 0x81cac09a5f11abfe, 0x9047830455d12abb, 0x19a4bde1945ae873}}},
+{{{0x9b9f26f520a6200a, 0x64804443cf13eaf8, 0x8a63673f8631edd3, 0x72bbbce11ed39dc1}},
+ {{0x40c709dec076c49f, 0x657bfaf27f3e53f6, 0x40662331eca042c4, 0x14b375487eb4df04}},
+ {{0xae853c94ab66dc47, 0xeb62343edf762d6e, 0xf08e0e186fb2f7d1, 0x4f0b1c02700ab37a}}},
+{{{0xe1706787d81951fa, 0xa10a2c8eb290c77b, 0xe7382fa03ed66773, 0x0a4d84710bcc4b54}},
+ {{0x79fd21ccc1b2e23f, 0x4ae7c281453df52a, 0xc8172ec9d151486b, 0x68abe9443e0a7534}},
+ {{0xda12c6c407831dcb, 0x0da230d74d5c510d, 0x4ab1531e6bd404e1, 0x4106b166bcf440ef}}},
+{{{0x02e57a421cd23668, 0x4ad9fb5d0eaef6fd, 0x954e6727b1244480, 0x7f792f9d2699f331}},
+ {{0xa485ccd539e4ecf2, 0x5aa3f3ad0555bab5, 0x145e3439937df82d, 0x1238b51e1214283f}},
+ {{0x0b886b925fd4d924, 0x60906f7a3626a80d, 0xecd367b4b98abd12, 0x2876beb1def344cf}}},
+{{{0xdc84e93563144691, 0x632fe8a0d61f23f4, 0x4caa800612a9a8d5, 0x48f9dbfa0e9918d3}},
+ {{0xd594b3333a8a85f8, 0x4ea37689e78d7d58, 0x73bf9f455e8e351f, 0x5507d7d2bc41ebb4}},
+ {{0x1ceb2903299572fc, 0x7c8ccaa29502d0ee, 0x91bfa43411cce67b, 0x5784481964a831e7}}},
+{{{0xda7c2b256768d593, 0x98c1c0574422ca13, 0xf1a80bd5ca0ace1d, 0x29cdd1adc088a690}},
+ {{0xd6cfd1ef5fddc09c, 0xe82b3efdf7575dce, 0x25d56b5d201634c2, 0x3041c6bb04ed2b9b}},
+ {{0x0ff2f2f9d956e148, 0xade797759f356b2e, 0x1a4698bb5f6c025c, 0x104bbd6814049a7b}}},
+{{{0x51f0fd3168f1ed67, 0x2c811dcdd86f3bc2, 0x44dc5c4304d2f2de, 0x5be8cc57092a7149}},
+ {{0xa95d9a5fd67ff163, 0xe92be69d4cc75681, 0xb7f8024cde20f257, 0x204f2a20fb072df5}},
+ {{0xc8143b3d30ebb079, 0x7589155abd652e30, 0x653c3c318f6d5c31, 0x2570fb17c279161f}}},
+{{{0x3efa367f2cb61575, 0xf5f96f761cd6026c, 0xe8c7142a65b52562, 0x3dcb65ea53030acd}},
+ {{0x192ea9550bb8245a, 0xc8e6fba88f9050d1, 0x7986ea2d88a4c935, 0x241c5f91de018668}},
+ {{0x28d8172940de6caa, 0x8fbf2cf022d9733a, 0x16d7fcdd235b01d1, 0x08420edd5fcdf0e5}}},
+{{{0xcdff20ab8362fa4a, 0x57e118d4e21a3e6e, 0xe3179617fc39e62b, 0x0d9a53efbc1769fd}},
+ {{0x0358c34e04f410ce, 0xb6135b5a276e0685, 0x5d9670c7ebb91521, 0x04d654f321db889c}},
+ {{0x5e7dc116ddbdb5d5, 0x2954deb68da5dd2d, 0x1cb608173334a292, 0x4a7a4f2618991ad7}}},
+{{{0xf4a718025fb15f95, 0x3df65f346b5c1b8f, 0xcdfcf08500e01112, 0x11b50c4cddd31848}},
+ {{0x24c3b291af372a4b, 0x93da8270718147f2, 0xdd84856486899ef2, 0x4a96314223e0ee33}},
+ {{0xa6e8274408a4ffd6, 0x738e177e9c1576d9, 0x773348b63d02b3f2, 0x4f4bce4dce6bcc51}}},
+{{{0xa71fce5ae2242584, 0x26ea725692f58a9e, 0xd21a09d71cea3cf4, 0x73fcdd14b71c01e6}},
+ {{0x30e2616ec49d0b6f, 0xe456718fcaec2317, 0x48eb409bf26b4fa6, 0x3042cee561595f37}},
+ {{0x427e7079449bac41, 0x855ae36dbce2310a, 0x4cae76215f841a7c, 0x389e740c9a9ce1d6}}},
+{{{0x64fcb3ae34dcb9ce, 0x97500323e348d0ad, 0x45b3f07d62c6381b, 0x61545379465a6788}},
+ {{0xc9bd78f6570eac28, 0xe55b0b3227919ce1, 0x65fc3eaba19b91ed, 0x25c425e5d6263690}},
+ {{0x3f3e06a6f1d7de6e, 0x3ef976278e062308, 0x8c14f6264e8a6c77, 0x6539a08915484759}}},
+{{{0xe9d21f74c3d2f773, 0xc150544125c46845, 0x624e5ce8f9b99e33, 0x11c5e4aac5cd186c}},
+ {{0xddc4dbd414bb4a19, 0x19b2bc3c98424f8e, 0x48a89fd736ca7169, 0x0f65320ef019bd90}},
+ {{0xd486d1b1cafde0c6, 0x4f3fe6e3163b5181, 0x59a8af0dfaf2939a, 0x4cabc7bdec33072a}}},
+{{{0x16faa8fb532f7428, 0xdbd42ea046a4e272, 0x5337653b8b9ea480, 0x4065947223973f03}},
+ {{0xf7c0a19c1a54a044, 0x4a1c5e2477bd9fbb, 0xa6e3ca115af22972, 0x1819bb953f2e9e0d}},
+ {{0x498fbb795e042e84, 0x7d0dd89a7698b714, 0x8bfb0ba427fe6295, 0x36ba82e721200524}}},
+{{{0xd60ecbb74245ec41, 0xfd9be89e34348716, 0xc9240afee42284de, 0x4472f648d0531db4}},
+ {{0xc8d69d0a57274ed5, 0x45ba803260804b17, 0xdf3cda102255dfac, 0x77d221232709b339}},
+ {{0x498a6d7064ad94d8, 0xa5b5c8fd9af62263, 0x8ca8ed0545c141f4, 0x2c63bec3662d358c}}},
+{{{0x7fe60d8bea787955, 0xb9dc117eb5f401b7, 0x91c7c09a19355cce, 0x22692ef59442bedf}},
+ {{0x9a518b3a8586f8bf, 0x9ee71af6cbb196f0, 0xaa0625e6a2385cf2, 0x1deb2176ddd7c8d1}},
+ {{0x8563d19a2066cf6c, 0x401bfd8c4dcc7cd7, 0xd976a6becd0d8f62, 0x67cfd773a278b05e}}},
+{{{0x8dec31faef3ee475, 0x99dbff8a9e22fd92, 0x512d11594e26cab1, 0x0cde561eec4310b9}},
+ {{0x2d5fa9855a4e586a, 0x65f8f7a449beab7e, 0xaa074dddf21d33d3, 0x185cba721bcb9dee}},
+ {{0x93869da3f4e3cb41, 0xbf0392f540f7977e, 0x026204fcd0463b83, 0x3ec91a769eec6eed}}},
+{{{0x1e9df75bf78166ad, 0x4dfda838eb0cd7af, 0xba002ed8c1eaf988, 0x13fedb3e11f33cfc}},
+ {{0x0fad2fb7b0a3402f, 0x46615ecbfb69f4a8, 0xf745bcc8c5f8eaa6, 0x7a5fa8794a94e896}},
+ {{0x52958faa13cd67a1, 0x965ee0818bdbb517, 0x16e58daa2e8845b3, 0x357d397d5499da8f}}},
+{{{0x1ebfa05fb0bace6c, 0xc934620c1caf9a1e, 0xcc771cc41d82b61a, 0x2d94a16aa5f74fec}},
+ {{0x481dacb4194bfbf8, 0x4d77e3f1bae58299, 0x1ef4612e7d1372a0, 0x3a8d867e70ff69e1}},
+ {{0x6f58cd5d55aff958, 0xba3eaa5c75567721, 0x75c123999165227d, 0x69be1343c2f2b35e}}},
+{{{0x0e091d5ee197c92a, 0x4f51019f2945119f, 0x143679b9f034e99c, 0x7d88112e4d24c696}},
+ {{0x82bbbdac684b8de3, 0xa2f4c7d03fca0718, 0x337f92fbe096aaa8, 0x200d4d8c63587376}},
+ {{0x208aed4b4893b32b, 0x3efbf23ebe59b964, 0xd762deb0dba5e507, 0x69607bd681bd9d94}}},
+{{{0xf6be021068de1ce1, 0xe8d518e70edcbc1f, 0xe3effdd01b5505a5, 0x35f63353d3ec3fd0}},
+ {{0x3b7f3bd49323a902, 0x7c21b5566b2c6e53, 0xe5ba8ff53a7852a7, 0x28bc77a5838ece00}},
+ {{0x63ba78a8e25d8036, 0x63651e0094333490, 0x48d82f20288ce532, 0x3a31abfa36b57524}}},
+{{{0x239e9624089c0a2e, 0xc748c4c03afe4738, 0x17dbed2a764fa12a, 0x639b93f0321c8582}},
+ {{0xc08f788f3f78d289, 0xfe30a72ca1404d9f, 0xf2778bfccf65cc9d, 0x7ee498165acb2021}},
+ {{0x7bd508e39111a1c3, 0x2b2b90d480907489, 0xe7d2aec2ae72fd19, 0x0edf493c85b602a6}}},
+{{{0xaecc8158599b5a68, 0xea574f0febade20e, 0x4fe41d7422b67f07, 0x403b92e3019d4fb4}},
+ {{0x6767c4d284764113, 0xa090403ff7f5f835, 0x1c8fcffacae6bede, 0x04c00c54d1dfa369}},
+ {{0x4dc22f818b465cf8, 0x71a0f35a1480eff8, 0xaee8bfad04c7d657, 0x355bb12ab26176f4}}},
+{{{0xa71e64cc7493bbf4, 0xe5bd84d9eca3b0c3, 0x0a6bc50cfa05e785, 0x0f9b8132182ec312}},
+ {{0xa301dac75a8c7318, 0xed90039db3ceaa11, 0x6f077cbf3bae3f2d, 0x7518eaf8e052ad8e}},
+ {{0xa48859c41b7f6c32, 0x0f2d60bcf4383298, 0x1815a929c9b1d1d9, 0x47c3871bbb1755c4}}},
+{{{0x5144539771ec4f48, 0xf805b17dc98c5d6e, 0xf762c11a47c3c66b, 0x00b89b85764699dc}},
+ {{0xfbe65d50c85066b0, 0x62ecc4b0b3a299b0, 0xe53754ea441ae8e0, 0x08fea02ce8d48d5f}},
+ {{0x824ddd7668deead0, 0xc86445204b685d23, 0xb514cfcd5d89d665, 0x473829a74f75d537}}},
+{{{0x82d2da754679c418, 0xe63bd7d8b2618df0, 0x355eef24ac47eb0a, 0x2078684c4833c6b4}},
+ {{0x23d9533aad3902c9, 0x64c2ddceef03588f, 0x15257390cfe12fb4, 0x6c668b4d44e4d390}},
+ {{0x3b48cf217a78820c, 0xf76a0ab281273e97, 0xa96c65a78c8eed7b, 0x7411a6054f8a433f}}},
+{{{0x4d659d32b99dc86d, 0x044cdc75603af115, 0xb34c712cdcc2e488, 0x7c136574fb8134ff}},
+ {{0x579ae53d18b175b4, 0x68713159f392a102, 0x8455ecba1eef35f5, 0x1ec9a872458c398f}},
+ {{0xb8e6a4d400a2509b, 0x9b81d7020bc882b4, 0x57e7cc9bf1957561, 0x3add88a5c7cd6460}}},
+{{{0xab895770b635dcf2, 0x02dfef6cf66c1fbc, 0x85530268beb6d187, 0x249929fccc879e74}},
+ {{0x85c298d459393046, 0x8f7e35985ff659ec, 0x1d2ca22af2f66e3a, 0x61ba1131a406a720}},
+ {{0xa3d0a0f116959029, 0x023b6b6cba7ebd89, 0x7bf15a3e26783307, 0x5620310cbbd8ece7}}},
+{{{0x528993434934d643, 0xb9dbf806a51222f5, 0x8f6d878fc3f41c22, 0x37676a2a4d9d9730}},
+ {{0x6646b5f477e285d6, 0x40e8ff676c8f6193, 0xa6ec7311abb594dd, 0x7ec846f3658cec4d}},
+ {{0x9b5e8f3f1da22ec7, 0x130f1d776c01cd13, 0x214c8fcfa2989fb8, 0x6daaf723399b9dd5}}},
+{{{0x591e4a5610628564, 0x2a4bb87ca8b4df34, 0xde2a2572e7a38e43, 0x3cbdabd9fee5046e}},
+ {{0x81aebbdd2cd13070, 0x962e4325f85a0e9e, 0xde9391aacadffecb, 0x53177fda52c230e6}},
+ {{0xa7bc970650b9de79, 0x3d12a7fbc301b59b, 0x02652e68d36ae38c, 0x79d739835a6199dc}}},
+{{{0xd9354df64131c1bd, 0x758094a186ec5822, 0x4464ee12e459f3c2, 0x6c11fce4cb133282}},
+ {{0x21c9d9920d591737, 0x9bea41d2e9b46cd6, 0xe20e84200d89bfca, 0x79d99f946eae5ff8}},
+ {{0xf17b483568673205, 0x387deae83caad96c, 0x61b471fd56ffe386, 0x31741195b745a599}}},
+{{{0xe8d10190b77a360b, 0x99b983209995e702, 0xbd4fdff8fa0247aa, 0x2772e344e0d36a87}},
+ {{0x17f8ba683b02a047, 0x50212096feefb6c8, 0x70139be21556cbe2, 0x203e44a11d98915b}},
+ {{0xd6863eba37b9e39f, 0x105bc169723b5a23, 0x104f6459a65c0762, 0x567951295b4d38d4}}},
+{{{0x535fd60613037524, 0xe210adf6b0fbc26a, 0xac8d0a9b23e990ae, 0x47204d08d72fdbf9}},
+ {{0x07242eb30d4b497f, 0x1ef96306b9bccc87, 0x37950934d8116f45, 0x05468d6201405b04}},
+ {{0x00f565a9f93267de, 0xcecfd78dc0d58e8a, 0xa215e2dcf318e28e, 0x4599ee919b633352}}},
+{{{0xd3c220ca70e0e76b, 0xb12bea58ea9f3094, 0x294ddec8c3271282, 0x0c3539e1a1d1d028}},
+ {{0xac746d6b861ae579, 0x31ab0650f6aea9dc, 0x241d661140256d4c, 0x2f485e853d21a5de}},
+ {{0x329744839c0833f3, 0x6fe6257fd2abc484, 0x5327d1814b358817, 0x65712585893fe9bc}}},
+{{{0x9c102fb732a61161, 0xe48e10dd34d520a8, 0x365c63546f9a9176, 0x32f6fe4c046f6006}},
+ {{0x81c29f1bd708ee3f, 0xddcb5a05ae6407d0, 0x97aec1d7d2a3eba7, 0x1590521a91d50831}},
+ {{0x40a3a11ec7910acc, 0x9013dff8f16d27ae, 0x1a9720d8abb195d4, 0x1bb9fe452ea98463}}},
+{{{0xe9d1d950b3d54f9e, 0x2d5f9cbee00d33c1, 0x51c2c656a04fc6ac, 0x65c091ee3c1cbcc9}},
+ {{0xcf5e6c95cc36747c, 0x294201536b0bc30d, 0x453ac67cee797af0, 0x5eae6ab32a8bb3c9}},
+ {{0x7083661114f118ea, 0x2b37b87b94349cad, 0x7273f51cb4e99f40, 0x78a2a95823d75698}}},
+{{{0xa2b072e95c8c2ace, 0x69cffc96651e9c4b, 0x44328ef842e7b42b, 0x5dd996c122aadeb3}},
+ {{0xb4f23c425ef83207, 0xabf894d3c9a934b5, 0xd0708c1339fd87f7, 0x1876789117166130}},
+ {{0x925b5ef0670c507c, 0x819bc842b93c33bf, 0x10792e9a70dd003f, 0x59ad4b7a6e28dc74}}},
+{{{0x5f3a7562eb3dbe47, 0xf7ea38548ebda0b8, 0x00c3e53145747299, 0x1304e9e71627d551}},
+ {{0x583b04bfacad8ea2, 0x29b743e8148be884, 0x2b1e583b0810c5db, 0x2b5449e58eb3bbaa}},
+ {{0x789814d26adc9cfe, 0x3c1bab3f8b48dd0b, 0xda0fe1fff979c60a, 0x4468de2d7c2dd693}}},
+{{{0x51bb355e9419469e, 0x33e6dc4c23ddc754, 0x93a5b6d6447f9962, 0x6cce7c6ffb44bd63}},
+ {{0x4b9ad8c6f86307ce, 0x21113531435d0c28, 0xd4a866c5657a772c, 0x5da6427e63247352}},
+ {{0x1a94c688deac22ca, 0xb9066ef7bbae1ff8, 0x88ad8c388d59580f, 0x58f29abfe79f2ca8}}},
+{{{0xe90ecfab8de73e68, 0x54036f9f377e76a5, 0xf0495b0bbe015982, 0x577629c4a7f41e36}},
+ {{0x4b5a64bf710ecdf6, 0xb14ce538462c293c, 0x3643d056d50b3ab9, 0x6af93724185b4870}},
+ {{0x3220024509c6a888, 0xd2e036134b558973, 0x83e236233c33289f, 0x701f25bb0caec18f}}},
+{{{0xc3a8b0f8e4616ced, 0xf700660e9e25a87d, 0x61e3061ff4bca59c, 0x2e0c92bfbdc40be9}},
+ {{0x9d18f6d97cbec113, 0x844a06e674bfdbe4, 0x20f5b522ac4e60d6, 0x720a5bc050955e51}},
+ {{0x0c3f09439b805a35, 0xe84e8b376242abfc, 0x691417f35c229346, 0x0e9b9cbb144ef0ec}}},
+{{{0xfbbad48ffb5720ad, 0xee81916bdbf90d0e, 0xd4813152635543bf, 0x221104eb3f337bd8}},
+ {{0x8dee9bd55db1beee, 0xc9c3ab370a723fb9, 0x44a8f1bf1c68d791, 0x366d44191cfd3cde}},
+ {{0x9e3c1743f2bc8c14, 0x2eda26fcb5856c3b, 0xccb82f0e68a7fb97, 0x4167a4e6bc593244}}},
+{{{0x643b9d2876f62700, 0x5d1d9d400e7668eb, 0x1b4b430321fc0684, 0x7938bb7e2255246a}},
+ {{0xc2be2665f8ce8fee, 0xe967ff14e880d62c, 0xf12e6e7e2f364eee, 0x34b33370cb7ed2f6}},
+ {{0xcdc591ee8681d6cc, 0xce02109ced85a753, 0xed7485c158808883, 0x1176fc6e2dfe65e4}}},
+{{{0xb4af6cd05b9c619b, 0x2ddfc9f4b2a58480, 0x3d4fa502ebe94dc4, 0x08fc3a4c677d5f34}},
+ {{0xdb90e28949770eb8, 0x98fbcc2aacf440a3, 0x21354ffeded7879b, 0x1f6a3e54f26906b6}},
+ {{0x60a4c199d30734ea, 0x40c085b631165cd6, 0xe2333e23f7598295, 0x4f2fad0116b900d1}}},
+{{{0x44beb24194ae4e54, 0x5f541c511857ef6c, 0xa61e6b2d368d0498, 0x445484a4972ef7ab}},
+ {{0x962cd91db73bb638, 0xe60577aafc129c08, 0x6f619b39f3b61689, 0x3451995f2944ee81}},
+ {{0x9152fcd09fea7d7c, 0x4a816c94b0935cf6, 0x258e9aaa47285c40, 0x10b89ca6042893b7}}},
+{{{0x9b2a426e3b646025, 0x32127190385ce4cf, 0xa25cffc2dd6dea45, 0x06409010bea8de75}},
+ {{0xd67cded679d34aa0, 0xcc0b9ec0cc4db39f, 0xa535a456e35d190f, 0x2e05d9eaf61f6fef}},
+ {{0xc447901ad61beb59, 0x661f19bce5dc880a, 0x24685482b7ca6827, 0x293c778cefe07f26}}},
+{{{0x86809e7007069096, 0xaad75b15e4e50189, 0x07f35715a21a0147, 0x0487f3f112815d5e}},
+ {{0x16c795d6a11ff200, 0xcb70d0e2b15815c9, 0x89f293209b5395b5, 0x50b8c2d031e47b4f}},
+ {{0x48350c08068a4962, 0x6ffdd05351092c9a, 0x17af4f4aaf6fc8dd, 0x4b0553b53cdba58b}}},
+{{{0x9c65fcbe1b32ff79, 0xeb75ea9f03b50f9b, 0xfced2a6c6c07e606, 0x35106cd551717908}},
+ {{0xbf05211b27c152d4, 0x5ec26849bd1af639, 0x5e0b2caa8e6fab98, 0x054c8bdd50bd0840}},
+ {{0x38a0b12f1dcf073d, 0x4b60a8a3b7f6a276, 0xfed5ac25d3404f9a, 0x72e82d5e5505c229}}},
+{{{0x6b0b697ff0d844c8, 0xbb12f85cd979cb49, 0xd2a541c6c1da0f1f, 0x7b7c242958ce7211}},
+ {{0x00d9cdfd69771d02, 0x410276cd6cfbf17e, 0x4c45306c1cb12ec7, 0x2857bf1627500861}},
+ {{0x9f21903f0101689e, 0xd779dfd3bf861005, 0xa122ee5f3deb0f1b, 0x510df84b485a00d4}}},
+{{{0xa54133bb9277a1fa, 0x74ec3b6263991237, 0x1a3c54dc35d2f15a, 0x2d347144e482ba3a}},
+ {{0x24b3c887c70ac15e, 0xb0f3a557fb81b732, 0x9b2cde2fe578cc1b, 0x4cf7ed0703b54f8e}},
+ {{0x6bd47c6598fbee0f, 0x9e4733e2ab55be2d, 0x1093f624127610c5, 0x4e05e26ad0a1eaa4}}},
+{{{0xda9b6b624b531f20, 0x429a760e77509abb, 0xdbe9f522e823cb80, 0x618f1856880c8f82}},
+ {{0x1833c773e18fe6c0, 0xe3c4711ad3c87265, 0x3bfd3c4f0116b283, 0x1955875eb4cd4db8}},
+ {{0x6da6de8f0e399799, 0x7ad61aa440fda178, 0xb32cd8105e3563dd, 0x15f6beae2ae340ae}}},
+{{{0x862bcb0c31ec3a62, 0x810e2b451138f3c2, 0x788ec4b839dac2a4, 0x28f76867ae2a9281}},
+ {{0xba9a0f7b9245e215, 0xf368612dd98c0dbb, 0x2e84e4cbf220b020, 0x6ba92fe962d90eda}},
+ {{0x3e4df9655884e2aa, 0xbd62fbdbdbd465a5, 0xd7596caa0de9e524, 0x6e8042ccb2b1b3d7}}},
+{{{0xf10d3c29ce28ca6e, 0xbad34540fcb6093d, 0xe7426ed7a2ea2d3f, 0x08af9d4e4ff298b9}},
+ {{0x1530653616521f7e, 0x660d06b896203dba, 0x2d3989bc545f0879, 0x4b5303af78ebd7b0}},
+ {{0x72f8a6c3bebcbde8, 0x4f0fca4adc3a8e89, 0x6fa9d4e8c7bfdf7a, 0x0dcf2d679b624eb7}}},
+{{{0x3d5947499718289c, 0x12ebf8c524533f26, 0x0262bfcb14c3ef15, 0x20b878d577b7518e}},
+ {{0x753941be5a45f06e, 0xd07caeed6d9c5f65, 0x11776b9c72ff51b6, 0x17d2d1d9ef0d4da9}},
+ {{0x27f2af18073f3e6a, 0xfd3fe519d7521069, 0x22e3b72c3ca60022, 0x72214f63cc65c6a7}}},
+{{{0xb4e37f405307a693, 0xaba714d72f336795, 0xd6fbd0a773761099, 0x5fdf48c58171cbc9}},
+ {{0x1d9db7b9f43b29c9, 0xd605824a4f518f75, 0xf2c072bd312f9dc4, 0x1f24ac855a1545b0}},
+ {{0x24d608328e9505aa, 0x4748c1d10c1420ee, 0xc7ffe45c06fb25a2, 0x00ba739e2ae395e6}}},
+{{{0x592e98de5c8790d6, 0xe5bfb7d345c2a2df, 0x115a3b60f9b49922, 0x03283a3e67ad78f3}},
+ {{0xae4426f5ea88bb26, 0x360679d984973bfb, 0x5c9f030c26694e50, 0x72297de7d518d226}},
+ {{0x48241dc7be0cb939, 0x32f19b4d8b633080, 0xd3dfc90d02289308, 0x05e1296846271945}}},
+{{{0xba82eeb32d9c495a, 0xceefc8fcf12bb97c, 0xb02dabae93b5d1e0, 0x39c00c9c13698d9b}},
+ {{0xadbfbbc8242c4550, 0xbcc80cecd03081d9, 0x843566a6f5c8df92, 0x78cf25d38258ce4c}},
+ {{0x15ae6b8e31489d68, 0xaa851cab9c2bf087, 0xc9a75a97f04efa05, 0x006b52076b3ff832}}},
+{{{0x29e0cfe19d95781c, 0xb681df18966310e2, 0x57df39d370516b39, 0x4d57e3443bc76122}},
+ {{0xf5cb7e16b9ce082d, 0x3407f14c417abc29, 0xd4b36bce2bf4a7ab, 0x7de2e9561a9f75ce}},
+ {{0xde70d4f4b6a55ecb, 0x4801527f5d85db99, 0xdbc9c440d3ee9a81, 0x6b2a90af1a6029ed}}},
+{{{0x6923f4fc9ae61e97, 0x5735281de03f5fd1, 0xa764ae43e6edd12d, 0x5fd8f4e9d12d3e4a}},
+ {{0x77ebf3245bb2d80a, 0xd8301b472fb9079b, 0xc647e6f24cee7333, 0x465812c8276c2109}},
+ {{0x4d43beb22a1062d9, 0x7065fb753831dc16, 0x180d4a7bde2968d7, 0x05b32c2b1cb16790}}},
+{{{0xc8c05eccd24da8fd, 0xa1cf1aac05dfef83, 0xdbbeeff27df9cd61, 0x3b5556a37b471e99}},
+ {{0xf7fca42c7ad58195, 0x3214286e4333f3cc, 0xb6c29d0d340b979d, 0x31771a48567307e1}},
+ {{0x32b0c524e14dd482, 0xedb351541a2ba4b6, 0xa3d16048282b5af3, 0x4fc079d27a7336eb}}},
+{{{0x51c938b089bf2f7f, 0x2497bd6502dfe9a7, 0xffffc09c7880e453, 0x124567cecaf98e92}},
+ {{0xdc348b440c86c50d, 0x1337cbc9cc94e651, 0x6422f74d643e3cb9, 0x241170c2bae3cd08}},
+ {{0x3ff9ab860ac473b4, 0xf0911dee0113e435, 0x4ae75060ebc6c4af, 0x3f8612966c87000d}}},
+{{{0x0c9c5303f7957be4, 0xa3c31a20e085c145, 0xb0721d71d0850050, 0x0aba390eab0bf2da}},
+ {{0x529fdffe638c7bf3, 0xdf2b9e60388b4995, 0xe027b34f1bad0249, 0x7bc92fc9b9fa74ed}},
+ {{0x9f97ef2e801ad9f9, 0x83697d5479afda3a, 0xe906b3ffbd596b50, 0x02672b37dd3fb8e0}}},
+{{{0x48b2ca8b260885e4, 0xa4286bec82b34c1c, 0x937e1a2617f58f74, 0x741d1fcbab2ca2a5}},
+ {{0xee9ba729398ca7f5, 0xeb9ca6257a4849db, 0x29eb29ce7ec544e1, 0x232ca21ef736e2c8}},
+ {{0xbf61423d253fcb17, 0x08803ceafa39eb14, 0xf18602df9851c7af, 0x0400f3a049e3414b}}},
+{{{0xabce0476ba61c55b, 0x36a3d6d7c4d39716, 0x6eb259d5e8d82d09, 0x0c9176e984d756fb}},
+ {{0x2efba412a06e7b06, 0x146785452c8d2560, 0xdf9713ebd67a91c7, 0x32830ac7157eadf3}},
+ {{0x0e782a7ab73769e8, 0x04a05d7875b18e2c, 0x29525226ebcceae1, 0x0d794f8383eba820}}},
+{{{0xff35f5cb9e1516f4, 0xee805bcf648aae45, 0xf0d73c2bb93a9ef3, 0x097b0bf22092a6c2}},
+ {{0x7be44ce7a7a2e1ac, 0x411fd93efad1b8b7, 0x1734a1d70d5f7c9b, 0x0d6592233127db16}},
+ {{0xc48bab1521a9d733, 0xa6c2eaead61abb25, 0x625c6c1cc6cb4305, 0x7fc90fea93eb3a67}}},
+{{{0x0408f1fe1f5c5926, 0x1a8f2f5e3b258bf4, 0x40a951a2fdc71669, 0x6598ee93c98b577e}},
+ {{0xc527deb59c7cb23d, 0x955391695328404e, 0xd64392817ccf2c7a, 0x6ce97dabf7d8fa11}},
+ {{0x25b5a8e50ef7c48f, 0xeb6034116f2ce532, 0xc5e75173e53de537, 0x73119fa08c12bb03}}},
+{{{0xed30129453f1a4cb, 0xbce621c9c8f53787, 0xfacb2b1338bee7b9, 0x3025798a9ea8428c}},
+ {{0x7845b94d21f4774d, 0xbf62f16c7897b727, 0x671857c03c56522b, 0x3cd6a85295621212}},
+ {{0x3fecde923aeca999, 0xbdaa5b0062e8c12f, 0x67b99dfc96988ade, 0x3f52c02852661036}}},
+{{{0xffeaa48e2a1351c6, 0x28624754fa7f53d7, 0x0b5ba9e57582ddf1, 0x60c0104ba696ac59}},
+ {{0x9258bf99eec416c6, 0xac8a5017a9d2f671, 0x629549ab16dea4ab, 0x05d0e85c99091569}},
+ {{0x051de020de9cbe97, 0xfa07fc56b50bcf74, 0x378cec9f0f11df65, 0x36853c69ab96de4d}}},
+{{{0x36d9b8de78f39b2d, 0x7f42ed71a847b9ec, 0x241cd1d679bd3fde, 0x6a704fec92fbce6b}},
+ {{0x4433c0b0fac5e7be, 0x724bae854c08dcbe, 0xf1f24cc446978f9b, 0x4a0aff6d62825fc8}},
+ {{0xe917fb9e61095301, 0xc102df9402a092f8, 0xbf09e2f5fa66190b, 0x681109bee0dcfe37}}},
+{{{0x559a0cc9782a0dde, 0x551dcdb2ea718385, 0x7f62865b31ef238c, 0x504aa7767973613d}},
+ {{0x9c18fcfa36048d13, 0x29159db373899ddd, 0xdc9f350b9f92d0aa, 0x26f57eee878a19d4}},
+ {{0x0cab2cd55687efb1, 0x5180d162247af17b, 0x85c15a344f5a2467, 0x4041943d9dba3069}}},
+{{{0xc3c0eeba43ebcc96, 0x8d749c9c26ea9caf, 0xd9fa95ee1c77ccc6, 0x1420a1d97684340f}},
+ {{0x4b217743a26caadd, 0x47a6b424648ab7ce, 0xcb1d4f7a03fbc9e3, 0x12d931429800d019}},
+ {{0x00c67799d337594f, 0x5e3c5140b23aa47b, 0x44182854e35ff395, 0x1b4f92314359a012}}},
+{{{0x3e5c109d89150951, 0x39cefa912de9696a, 0x20eae43f975f3020, 0x239b572a7f132dae}},
+ {{0x33cf3030a49866b1, 0x251f73d2215f4859, 0xab82aa4051def4f6, 0x5ff191d56f9a23f6}},
+ {{0x819ed433ac2d9068, 0x2883ab795fc98523, 0xef4572805593eb3d, 0x020c526a758f36cb}}},
+{{{0x779834f89ed8dbbc, 0xc8f2aaf9dc7ca46c, 0xa9524cdca3e1b074, 0x02aacc4615313877}},
+ {{0xe931ef59f042cc89, 0x2c589c9d8e124bb6, 0xadc8e18aaec75997, 0x452cfe0a5602c50c}},
+ {{0x86a0f7a0647877df, 0xbbc464270e607c9f, 0xab17ea25f1fb11c9, 0x4cfb7d7b304b877b}}},
+{{{0x72b43d6cb89b75fe, 0x54c694d99c6adc80, 0xb8c3aa373ee34c9f, 0x14b4622b39075364}},
+ {{0xe28699c29789ef12, 0x2b6ecd71df57190d, 0xc343c857ecc970d0, 0x5b1d4cbc434d3ac5}},
+ {{0xb6fb2615cc0a9f26, 0x3a4f0e2bb88dcce5, 0x1301498b3369a705, 0x2f98f71258592dd1}}},
+{{{0x0c94a74cb50f9e56, 0x5b1ff4a98e8e1320, 0x9a2acc2182300f67, 0x3a6ae249d806aaf9}},
+ {{0x2e12ae444f54a701, 0xfcfe3ef0a9cbd7de, 0xcebf890d75835de0, 0x1d8062e9e7614554}},
+ {{0x657ada85a9907c5a, 0x1a0ea8b591b90f62, 0x8d0e1dfbdf34b4e9, 0x298b8ce8aef25ff3}}},
+{{{0x2a927953eff70cb2, 0x4b89c92a79157076, 0x9418457a30a7cf6a, 0x34b8a8404d5ce485}},
+ {{0x837a72ea0a2165de, 0x3fab07b40bcf79f6, 0x521636c77738ae70, 0x6ba6271803a7d7dc}},
+ {{0xc26eecb583693335, 0xd5a813df63b5fefd, 0xa293aa9aa4b22573, 0x71d62bdd465e1c6a}}},
+{{{0x6533cc28d378df80, 0xf6db43790a0fa4b4, 0xe3645ff9f701da5a, 0x74d5f317f3172ba4}},
+ {{0xcd2db5dab1f75ef5, 0xd77f95cf16b065f5, 0x14571fea3f49f085, 0x1c333621262b2b3d}},
+ {{0xa86fe55467d9ca81, 0x398b7c752b298c37, 0xda6d0892e3ac623b, 0x4aebcc4547e9d98c}}},
+{{{0x53175a7205d21a77, 0xb0c04422d3b934d4, 0xadd9f24bdd5deadc, 0x074f46e69f10ff8c}},
+ {{0x0de9b204a059a445, 0xe15cb4aa4b17ad0f, 0xe1bbec521f79c557, 0x2633f1b9d071081b}},
+ {{0xc1fb4177018b9910, 0xa6ea20dc6c0fe140, 0xd661f3e74354c6ff, 0x5ecb72e6f1a3407a}}},
+{{{0xa515a31b2259fb4e, 0x0960f3972bcac52f, 0xedb52fec8d3454cb, 0x382e2720c476c019}},
+ {{0xfeeae106e8e86997, 0x9863337f98d09383, 0x9470480eaa06ebef, 0x038b6898d4c5c2d0}},
+ {{0xf391c51d8ace50a6, 0x3142d0b9ae2d2948, 0xdb4d5a1a7f24ca80, 0x21aeba8b59250ea8}}},
+{{{0x24f13b34cf405530, 0x3c44ea4a43088af7, 0x5dd5c5170006a482, 0x118eb8f8890b086d}},
+ {{0x53853600f0087f23, 0x4c461879da7d5784, 0x6af303deb41f6860, 0x0a3c16c5c27c18ed}},
+ {{0x17e49c17cc947f3d, 0xccc6eda6aac1d27b, 0xdf6092ceb0f08e56, 0x4909b3e22c67c36b}}},
+{{{0x9c9c85ea63fe2e89, 0xbe1baf910e9412ec, 0x8f7baa8a86fbfe7b, 0x0fb17f9fef968b6c}},
+ {{0x59a16676706ff64e, 0x10b953dd0d86a53d, 0x5848e1e6ce5c0b96, 0x2d8b78e712780c68}},
+ {{0x79d5c62eafc3902b, 0x773a215289e80728, 0xc38ae640e10120b9, 0x09ae23717b2b1a6d}}},
+{{{0xbb6a192a4e4d083c, 0x34ace0630029e192, 0x98245a59aafabaeb, 0x6d9c8a9ada97faac}},
+ {{0x10ab8fa1ad32b1d0, 0xe9aced1be2778b24, 0xa8856bc0373de90f, 0x66f35ddddda53996}},
+ {{0xd27d9afb24997323, 0x1bb7e07ef6f01d2e, 0x2ba7472df52ecc7f, 0x03019b4f646f9dc8}}},
+{{{0x04a186b5565345cd, 0xeee76610bcc4116a, 0x689c73b478fb2a45, 0x387dcbff65697512}},
+ {{0xaf09b214e6b3dc6b, 0x3f7573b5ad7d2f65, 0xd019d988100a23b0, 0x392b63a58b5c35f7}},
+ {{0x4093addc9c07c205, 0xc565be15f532c37e, 0x63dbecfd1583402a, 0x61722b4aef2e032e}}},
+{{{0x0012aafeecbd47af, 0x55a266fb1cd46309, 0xf203eb680967c72c, 0x39633944ca3c1429}},
+ {{0xd6b07a5581cb0e3c, 0x290ff006d9444969, 0x08680b6a16dcda1f, 0x5568d2b75a06de59}},
+ {{0x8d0cb88c1b37cfe1, 0x05b6a5a3053818f3, 0xf2e9bc04b787d959, 0x6beba1249add7f64}}},
+{{{0x1d06005ca5b1b143, 0x6d4c6bb87fd1cda2, 0x6ef5967653fcffe7, 0x097c29e8c1ce1ea5}},
+ {{0x5c3cecb943f5a53b, 0x9cc9a61d06c08df2, 0xcfba639a85895447, 0x5a845ae80df09fd5}},
+ {{0x4ce97dbe5deb94ca, 0x38d0a4388c709c48, 0xc43eced4a169d097, 0x0a1249fff7e587c3}}},
+{{{0x12f0071b276d01c9, 0xe7b8bac586c48c70, 0x5308129b71d6fba9, 0x5d88fbf95a3db792}},
+ {{0x0b408d9e7354b610, 0x806b32535ba85b6e, 0xdbe63a034a58a207, 0x173bd9ddc9a1df2c}},
+ {{0x2b500f1efe5872df, 0x58d6582ed43918c1, 0xe6ed278ec9673ae0, 0x06e1cd13b19ea319}}},
+{{{0x40d0ad516f166f23, 0x118e32931fab6abe, 0x3fe35e14a04d088e, 0x3080603526e16266}},
+ {{0x472baf629e5b0353, 0x3baa0b90278d0447, 0x0c785f469643bf27, 0x7f3a6a1a8d837b13}},
+ {{0xf7e644395d3d800b, 0x95a8d555c901edf6, 0x68cd7830592c6339, 0x30d0fded2e51307e}}},
+{{{0xe0594d1af21233b3, 0x1bdbe78ef0cc4d9c, 0x6965187f8f499a77, 0x0a9214202c099868}},
+ {{0x9cb4971e68b84750, 0xa09572296664bbcf, 0x5c8de72672fa412b, 0x4615084351c589d9}},
+ {{0xbc9019c0aeb9a02e, 0x55c7110d16034cae, 0x0e6df501659932ec, 0x3bca0d2895ca5dfe}}},
+{{{0x40f031bc3c5d62a4, 0x19fc8b3ecff07a60, 0x98183da2130fb545, 0x5631deddae8f13cd}},
+ {{0x9c688eb69ecc01bf, 0xf0bc83ada644896f, 0xca2d955f5f7a9fe2, 0x4ea8b4038df28241}},
+ {{0x2aed460af1cad202, 0x46305305a48cee83, 0x9121774549f11a5f, 0x24ce0930542ca463}}},
+{{{0x1fe890f5fd06c106, 0xb5c468355d8810f2, 0x827808fe6e8caf3e, 0x41d4e3c28a06d74b}},
+ {{0x3fcfa155fdf30b85, 0xd2f7168e36372ea4, 0xb2e064de6492f844, 0x549928a7324f4280}},
+ {{0xf26e32a763ee1a2e, 0xae91e4b7d25ffdea, 0xbc3bd33bd17f4d69, 0x491b66dec0dcff6a}}},
+{{{0x98f5b13dc7ea32a7, 0xe3d5f8cc7e16db98, 0xac0abf52cbf8d947, 0x08f338d0c85ee4ac}},
+ {{0x75f04a8ed0da64a1, 0xed222caf67e2284b, 0x8234a3791f7b7ba4, 0x4cf6b8b0b7018b67}},
+ {{0xc383a821991a73bd, 0xab27bc01df320c7a, 0xc13d331b84777063, 0x530d4a82eb078a99}}},
+{{{0x004c3630e1f94825, 0x7e2d78268cab535a, 0xc7482323cc84ff8b, 0x65ea753f101770b9}},
+ {{0x6d6973456c9abf9e, 0x257fb2fc4900a880, 0x2bacf412c8cfb850, 0x0db3e7e00cbfbd5b}},
+ {{0x3d66fc3ee2096363, 0x81d62c7f61b5cb6b, 0x0fbe044213443b1a, 0x02a4ec1921e1a1db}}},
+{{{0x5ce6259a3b24b8a2, 0xb8577acc45afa0b8, 0xcccbe6e88ba07037, 0x3d143c51127809bf}},
+ {{0xf5c86162f1cf795f, 0x118c861926ee57f2, 0x172124851c063578, 0x36d12b5dec067fcf}},
+ {{0x126d279179154557, 0xd5e48f5cfc783a0a, 0x36bdb6e8df179bac, 0x2ef517885ba82859}}},
+{{{0x88bd438cd11e0d4a, 0x30cb610d43ccf308, 0xe09a0e3791937bcc, 0x4559135b25b1720c}},
+ {{0x1ea436837c6da1e9, 0xf9c189af1fb9bdbe, 0x303001fcce5dd155, 0x28a7c99ebc57be52}},
+ {{0xb8fd9399e8d19e9d, 0x908191cb962423ff, 0xb2b948d747c742a3, 0x37f33226d7fb44c4}}},
+{{{0x0dae8767b55f6e08, 0x4a43b3b35b203a02, 0xe3725a6e80af8c79, 0x0f7a7fd1705fa7a3}},
+ {{0x33912553c821b11d, 0x66ed42c241e301df, 0x066fcc11104222fd, 0x307a3b41c192168f}},
+ {{0x8eeb5d076eb55ce0, 0x2fc536bfaa0d925a, 0xbe81830fdcb6c6e8, 0x556c7045827baf52}}},
+{{{0x8e2b517302e9d8b7, 0xe3e52269248714e8, 0xbd4fbd774ca960b5, 0x6f4b4199c5ecada9}},
+ {{0xb94b90022bf44406, 0xabd4237eff90b534, 0x7600a960faf86d3a, 0x2f45abdac2322ee3}},
+ {{0x61af4912c8ef8a6a, 0xe58fa4fe43fb6e5e, 0xb5afcc5d6fd427cf, 0x6a5393281e1e11eb}}},
+{{{0xf3da5139a5d1ee89, 0x8145457cff936988, 0x3f622fed00e188c4, 0x0f513815db8b5a3d}},
+ {{0x0fff04fe149443cf, 0x53cac6d9865cddd7, 0x31385b03531ed1b7, 0x5846a27cacd1039d}},
+ {{0x4ff5cdac1eb08717, 0x67e8b29590f2e9bc, 0x44093b5e237afa99, 0x0d414bed8708b8b2}}},
+{{{0xcfb68265fd0e75f6, 0xe45b3e28bb90e707, 0x7242a8de9ff92c7a, 0x685b3201933202dd}},
+ {{0x81886a92294ac9e8, 0x23162b45d55547be, 0x94cfbc4403715983, 0x50eb8fdb134bc401}},
+ {{0xc0b73ec6d6b330cd, 0x84e44807132faff1, 0x732b7352c4a5dee1, 0x5d7c7cf1aa7cd2d2}}},
+{{{0xaf3b46bf7a4aafa2, 0xb78705ec4d40d411, 0x114f0c6aca7c15e3, 0x3f364faaa9489d4d}},
+ {{0x33d1013e9b73a562, 0x925cef5748ec26e1, 0xa7fce614dd468058, 0x78b0fad41e9aa438}},
+ {{0xbf56a431ed05b488, 0xa533e66c9c495c7e, 0xe8652baf87f3651a, 0x0241800059d66c33}}},
+{{{0xceb077fea37a5be4, 0xdb642f02e5a5eeb7, 0xc2e6d0c5471270b8, 0x4771b65538e4529c}},
+ {{0x28350c7dcf38ea01, 0x7c6cdbc0b2917ab6, 0xace7cfbe857082f7, 0x4d2845aba2d9a1e0}},
+ {{0xbb537fe0447070de, 0xcba744436dd557df, 0xd3b5a3473600dbcb, 0x4aeabbe6f9ffd7f8}}},
+{{{0x4630119e40d8f78c, 0xa01a9bc53c710e11, 0x486d2b258910dd79, 0x1e6c47b3db0324e5}},
+ {{0x6a2134bcc4a9c8f2, 0xfbf8fd1c8ace2e37, 0x000ae3049911a0ba, 0x046e3a616bc89b9e}},
+ {{0x14e65442f03906be, 0x4a019d54e362be2a, 0x68ccdfec8dc230c7, 0x7cfb7e3faf6b861c}}},
+{{{0x4637974e8c58aedc, 0xb9ef22fbabf041a4, 0xe185d956e980718a, 0x2f1b78fab143a8a6}},
+ {{0x96eebffb305b2f51, 0xd3f938ad889596b8, 0xf0f52dc746d5dd25, 0x57968290bb3a0095}},
+ {{0xf71ab8430a20e101, 0xf393658d24f0ec47, 0xcf7509a86ee2eed1, 0x7dc43e35dc2aa3e1}}},
+{{{0x85966665887dd9c3, 0xc90f9b314bb05355, 0xc6e08df8ef2079b1, 0x7ef72016758cc12f}},
+ {{0x5a782a5c273e9718, 0x3576c6995e4efd94, 0x0f2ed8051f237d3e, 0x044fb81d82d50a99}},
+ {{0xc1df18c5a907e3d9, 0x57b3371dce4c6359, 0xca704534b201bb49, 0x7f79823f9c30dd2e}}},
+{{{0x8334d239a3b513e8, 0xc13670d4b91fa8d8, 0x12b54136f590bd33, 0x0a4e0373d784d9b4}},
+ {{0x6a9c1ff068f587ba, 0x0827894e0050c8de, 0x3cbf99557ded5be7, 0x64a9b0431c06d6f0}},
+ {{0x2eb3d6a15b7d2919, 0xb0b4f6a0d53a8235, 0x7156ce4389a45d47, 0x071a7d0ace18346c}}},
+{{{0xd3072daac887ba0b, 0x01262905bfa562ee, 0xcf543002c0ef768b, 0x2c3bcc7146ea7e9c}},
+ {{0xcc0c355220e14431, 0x0d65950709b15141, 0x9af5621b209d5f36, 0x7c69bcf7617755d3}},
+ {{0x07f0d7eb04e8295f, 0x10db18252f50f37d, 0xe951a9a3171798d7, 0x6f5a9a7322aca51d}}},
+{{{0x8ba1000c2f41c6c5, 0xc49f79c10cfefb9b, 0x4efa47703cc51c9f, 0x494e21a2e147afca}},
+ {{0xe729d4eba3d944be, 0x8d9e09408078af9e, 0x4525567a47869c03, 0x02ab9680ee8d3b24}},
+ {{0xefa48a85dde50d9a, 0x219a224e0fb9a249, 0xfa091f1dd91ef6d9, 0x6b5d76cbea46bb34}}},
+{{{0x8857556cec0cd994, 0x6472dc6f5cd01dba, 0xaf0169148f42b477, 0x0ae333f685277354}},
+ {{0xe0f941171e782522, 0xf1e6ae74036936d3, 0x408b3ea2d0fcc746, 0x16fb869c03dd313e}},
+ {{0x288e199733b60962, 0x24fc72b4d8abe133, 0x4811f7ed0991d03e, 0x3f81e38b8f70d075}}},
+{{{0x7f910fcc7ed9affe, 0x545cb8a12465874b, 0xa8397ed24b0c4704, 0x50510fc104f50993}},
+ {{0x0adb7f355f17c824, 0x74b923c3d74299a4, 0xd57c3e8bcbf8eaf7, 0x0ad3e2d34cdedc3d}},
+ {{0x6f0c0fc5336e249d, 0x745ede19c331cfd9, 0xf2d6fd0009eefe1c, 0x127c158bf0fa1ebe}}},
+{{{0xf6197c422e9879a2, 0xa44addd452ca3647, 0x9b413fc14b4eaccb, 0x354ef87d07ef4f68}},
+ {{0xdea28fc4ae51b974, 0x1d9973d3744dfe96, 0x6240680b873848a8, 0x4ed82479d167df95}},
+ {{0xfee3b52260c5d975, 0x50352efceb41b0b8, 0x8808ac30a9f6653c, 0x302d92d20539236d}}},
+{{{0x4c59023fcb3efb7c, 0x6c2fcb99c63c2a94, 0xba4190e2c3c7e084, 0x0e545daea51874d9}},
+ {{0x957b8b8b0df53c30, 0x2a1c770a8e60f098, 0xbbc7a670345796de, 0x22a48f9a90c99bc9}},
+ {{0x6b7dc0dc8d3fac58, 0x5497cd6ce6e42bfd, 0x542f7d1bf400d305, 0x4159f47f048d9136}}},
+{{{0x20ad660839e31e32, 0xf81e1bd58405be50, 0xf8064056f4dabc69, 0x14d23dd4ce71b975}},
+ {{0x748515a8bbd24839, 0x77128347afb02b55, 0x50ba2ac649a2a17f, 0x060525513ad730f1}},
+ {{0xf2398e098aa27f82, 0x6d7982bb89a1b024, 0xfa694084214dd24c, 0x71ab966fa32301c3}}},
+{{{0x2dcbd8e34ded02fc, 0x1151f3ec596f22aa, 0xbca255434e0328da, 0x35768fbe92411b22}},
+ {{0xb1088a0702809955, 0x43b273ea0b43c391, 0xca9b67aefe0686ed, 0x605eecbf8335f4ed}},
+ {{0x83200a656c340431, 0x9fcd71678ee59c2f, 0x75d4613f71300f8a, 0x7a912faf60f542f9}}},
+{{{0xb204585e5edc1a43, 0x9f0e16ee5897c73c, 0x5b82c0ae4e70483c, 0x624a170e2bddf9be}},
+ {{0x253f4f8dfa2d5597, 0x25e49c405477130c, 0x00c052e5996b1102, 0x33cb966e33bb6c4a}},
+ {{0x597028047f116909, 0x828ac41c1e564467, 0x70417dbde6217387, 0x721627aefbac4384}}},
+{{{0x97d03bc38736add5, 0x2f1422afc532b130, 0x3aa68a057101bbc4, 0x4c946cf7e74f9fa7}},
+ {{0xfd3097bc410b2f22, 0xf1a05da7b5cfa844, 0x61289a1def57ca74, 0x245ea199bb821902}},
+ {{0xaedca66978d477f8, 0x1898ba3c29117fe1, 0xcf73f983720cbd58, 0x67da12e6b8b56351}}},
+{{{0x7067e187b4bd6e07, 0x6e8f0203c7d1fe74, 0x93c6aa2f38c85a30, 0x76297d1f3d75a78a}},
+ {{0x2b7ef3d38ec8308c, 0x828fd7ec71eb94ab, 0x807c3b36c5062abd, 0x0cb64cb831a94141}},
+ {{0x3030fc33534c6378, 0xb9635c5ce541e861, 0x15d9a9bed9b2c728, 0x49233ea3f3775dcb}}},
+{{{0x629398fa8dbffc3a, 0xe12fe52dd54db455, 0xf3be11dfdaf25295, 0x628b140dce5e7b51}},
+ {{0x7b3985fe1c9f249b, 0x4fd6b2d5a1233293, 0xceb345941adf4d62, 0x6987ff6f542de50c}},
+ {{0x47e241428f83753c, 0x6317bebc866af997, 0xdabb5b433d1a9829, 0x074d8d245287fb2d}}},
+{{{0x8337d9cd440bfc31, 0x729d2ca1af318fd7, 0xa040a4a4772c2070, 0x46002ef03a7349be}},
+ {{0x481875c6c0e31488, 0x219429b2e22034b4, 0x7223c98a31283b65, 0x3420d60b342277f9}},
+ {{0xfaa23adeaffe65f7, 0x78261ed45be0764c, 0x441c0a1e2f164403, 0x5aea8e567a87d395}}},
+{{{0x7813c1a2bca4283d, 0xed62f091a1863dd9, 0xaec7bcb8c268fa86, 0x10e5d3b76f1cae4c}},
+ {{0x2dbc6fb6e4e0f177, 0x04e1bf29a4bd6a93, 0x5e1966d4787af6e8, 0x0edc5f5eb426d060}},
+ {{0x5453bfd653da8e67, 0xe9dc1eec24a9f641, 0xbf87263b03578a23, 0x45b46c51361cba72}}},
+{{{0xa9402abf314f7fa1, 0xe257f1dc8e8cf450, 0x1dbbd54b23a8be84, 0x2177bfa36dcb713b}},
+ {{0xce9d4ddd8a7fe3e4, 0xab13645676620e30, 0x4b594f7bb30e9958, 0x5c1c0aef321229df}},
+ {{0x37081bbcfa79db8f, 0x6048811ec25f59b3, 0x087a76659c832487, 0x4ae619387d8ab5bb}}},
+{{{0x8ddbf6aa5344a32e, 0x7d88eab4b41b4078, 0x5eb0eb974a130d60, 0x1a00d91b17bf3e03}},
+ {{0x61117e44985bfb83, 0xfce0462a71963136, 0x83ac3448d425904b, 0x75685abe5ba43d64}},
+ {{0x6e960933eb61f2b2, 0x543d0fa8c9ff4952, 0xdf7275107af66569, 0x135529b623b0e6aa}}},
+{{{0x18f0dbd7add1d518, 0x979f7888cfc11f11, 0x8732e1f07114759b, 0x79b5b81a65ca3a01}},
+ {{0xf5c716bce22e83fe, 0xb42beb19e80985c1, 0xec9da63714254aae, 0x5972ea051590a613}},
+ {{0x0fd4ac20dc8f7811, 0x9a9ad294ac4d4fa8, 0xc01b2d64b3360434, 0x4f7e9c95905f3bdb}}},
+{{{0x62674bbc5781302e, 0xd8520f3989addc0f, 0x8c2999ae53fbd9c6, 0x31993ad92e638e4c}},
+ {{0x71c8443d355299fe, 0x8bcd3b1cdbebead7, 0x8092499ef1a49466, 0x1942eec4a144adc8}},
+ {{0x7dac5319ae234992, 0x2c1b3d910cea3e92, 0x553ce494253c1122, 0x2a0a65314ef9ca75}}},
+{{{0x2db7937ff7f927c2, 0xdb741f0617d0a635, 0x5982f3a21155af76, 0x4cf6e218647c2ded}},
+ {{0xcf361acd3c1c793a, 0x2f9ebcac5a35bc3b, 0x60e860e9a8cda6ab, 0x055dc39b6dea1a13}},
+ {{0xb119227cc28d5bb6, 0x07e24ebc774dffab, 0xa83c78cee4a32c89, 0x121a307710aa24b6}}},
+{{{0xe4db5d5e9f034a97, 0xe153fc093034bc2d, 0x460546919551d3b1, 0x333fc76c7a40e52d}},
+ {{0xd659713ec77483c9, 0x88bfe077b82b96af, 0x289e28231097bcd3, 0x527bb94a6ced3a9b}},
+ {{0x563d992a995b482e, 0x3405d07c6e383801, 0x485035de2f64d8e5, 0x6b89069b20a7a9f7}}},
+{{{0x812aa0416270220d, 0x995a89faf9245b4e, 0xffadc4ce5072ef05, 0x23bc2103aa73eb73}},
+ {{0x4082fa8cb5c7db77, 0x068686f8c734c155, 0x29e6c8d9f6e7a57e, 0x0473d308a7639bcf}},
+ {{0xcaee792603589e05, 0x2b4b421246dcc492, 0x02a1ef74e601a94f, 0x102f73bfde04341a}}},
+{{{0xb5a2d50c7ec20d3e, 0xc64bdd6ea0c97263, 0x56e89052c1ff734d, 0x4929c6f72b2ffaba}},
+ {{0x358ecba293a36247, 0xaf8f9862b268fd65, 0x412f7e9968a01c89, 0x5786f312cd754524}},
+ {{0x337788ffca14032c, 0xf3921028447f1ee3, 0x8b14071f231bccad, 0x4c817b4bf2344783}}},
+{{{0x0ff853852871b96e, 0xe13e9fab60c3f1bb, 0xeefd595325344402, 0x0a37c37075b7744b}},
+ {{0x413ba057a40b4484, 0xba4c2e1a4f5f6a43, 0x614ba0a5aee1d61c, 0x78a1531a8b05dc53}},
+ {{0x6cbdf1703ad0562b, 0x8ecf4830c92521a3, 0xdaebd303fd8424e7, 0x72ad82a42e5ec56f}}},
+{{{0x3f9e8e35bafb65f6, 0x39d69ec8f27293a1, 0x6cb8cd958cf6a3d0, 0x1734778173adae6d}},
+ {{0xc368939167024bc3, 0x8e69d16d49502fda, 0xfcf2ec3ce45f4b29, 0x065f669ea3b4cbc4}},
+ {{0x8a00aec75532db4d, 0xb869a4e443e31bb1, 0x4a0f8552d3a7f515, 0x19adeb7c303d7c08}}},
+{{{0xc720cb6153ead9a3, 0x55b2c97f512b636e, 0xb1e35b5fd40290b1, 0x2fd9ccf13b530ee2}},
+ {{0x9d05ba7d43c31794, 0x2470c8ff93322526, 0x8323dec816197438, 0x2852709881569b53}},
+ {{0x07bd475b47f796b8, 0xd2c7b013542c8f54, 0x2dbd23f43b24f87e, 0x6551afd77b0901d6}}},
+{{{0x4546baaf54aac27f, 0xf6f66fecb2a45a28, 0x582d1b5b562bcfe8, 0x44b123f3920f785f}},
+ {{0x68a24ce3a1d5c9ac, 0xbb77a33d10ff6461, 0x0f86ce4425d3166e, 0x56507c0950b9623b}},
+ {{0x1206f0b7d1713e63, 0x353fe3d915bafc74, 0x194ceb970ad9d94d, 0x62fadd7cf9d03ad3}}},
+{{{0xc6b5967b5598a074, 0x5efe91ce8e493e25, 0xd4b72c4549280888, 0x20ef1149a26740c2}},
+ {{0x3cd7bc61e7ce4594, 0xcd6b35a9b7dd267e, 0xa080abc84366ef27, 0x6ec7c46f59c79711}},
+ {{0x2f07ad636f09a8a2, 0x8697e6ce24205e7d, 0xc0aefc05ee35a139, 0x15e80958b5f9d897}}},
+{{{0x25a5ef7d0c3e235b, 0x6c39c17fbe134ee7, 0xc774e1342dc5c327, 0x021354b892021f39}},
+ {{0x4dd1ed355bb061c4, 0x42dc0cef941c0700, 0x61305dc1fd86340e, 0x56b2cc930e55a443}},
+ {{0x1df79da6a6bfc5a2, 0x02f3a2749fde4369, 0xb323d9f2cda390a7, 0x7be0847b8774d363}}},
+{{{0x8c99cc5a8b3f55c3, 0x0611d7253fded2a0, 0xed2995ff36b70a36, 0x1f699a54d78a2619}},
+ {{0x1466f5af5307fa11, 0x817fcc7ded6c0af2, 0x0a6de44ec3a4a3fb, 0x74071475bc927d0b}},
+ {{0xe77292f373e7ea8a, 0x296537d2cb045a31, 0x1bd0653ed3274fde, 0x2f9a2c4476bd2966}}},
+{{{0xeb18b9ab7f5745c6, 0x023a8aee5787c690, 0xb72712da2df7afa9, 0x36597d25ea5c013d}},
+ {{0xa2b4dae0b5511c9a, 0x7ac860292bffff06, 0x981f375df5504234, 0x3f6bd725da4ea12d}},
+ {{0x734d8d7b106058ac, 0xd940579e6fc6905f, 0x6466f8f99202932d, 0x7b7ecc19da60d6d0}}},
+{{{0x78c2373c695c690d, 0xdd252e660642906e, 0x951d44444ae12bd2, 0x4235ad7601743956}},
+ {{0x6dae4a51a77cfa9b, 0x82263654e7a38650, 0x09bbffcd8f2d82db, 0x03bedc661bf5caba}},
+ {{0x6258cb0d078975f5, 0x492942549189f298, 0xa0cab423e2e36ee4, 0x0e7ce2b0cdf066a1}}},
+{{{0xc494643ac48c85a3, 0xfd361df43c6139ad, 0x09db17dd3ae94d48, 0x666e0a5d8fb4674a}},
+ {{0xfea6fedfd94b70f9, 0xf130c051c1fcba2d, 0x4882d47e7f2fab89, 0x615256138aeceeb5}},
+ {{0x2abbf64e4870cb0d, 0xcd65bcf0aa458b6b, 0x9abe4eba75e8985d, 0x7f0bc810d514dee4}}},
+{{{0xb9006ba426f4136f, 0x8d67369e57e03035, 0xcbc8dfd94f463c28, 0x0d1f8dbcf8eedbf5}},
+ {{0x83ac9dad737213a0, 0x9ff6f8ba2ef72e98, 0x311e2edd43ec6957, 0x1d3a907ddec5ab75}},
+ {{0xba1693313ed081dc, 0x29329fad851b3480, 0x0128013c030321cb, 0x00011b44a31bfde3}}},
+{{{0x3fdfa06c3fc66c0c, 0x5d40e38e4dd60dd2, 0x7ae38b38268e4d71, 0x3ac48d916e8357e1}},
+ {{0x16561f696a0aa75c, 0xc1bf725c5852bd6a, 0x11a8dd7f9a7966ad, 0x63d988a2d2851026}},
+ {{0x00120753afbd232e, 0xe92bceb8fdd8f683, 0xf81669b384e72b91, 0x33fad52b2368a066}}},
+{{{0x540649c6c5e41e16, 0x0af86430333f7735, 0xb2acfcd2f305e746, 0x16c0f429a256dca7}},
+ {{0x8d2cc8d0c422cfe8, 0x072b4f7b05a13acb, 0xa3feb6e6ecf6a56f, 0x3cc355ccb90a71e2}},
+ {{0xe9b69443903e9131, 0xb8a494cb7a5637ce, 0xc87cd1a4baba9244, 0x631eaf426bae7568}}},
+{{{0xb3e90410da66fe9f, 0x85dd4b526c16e5a6, 0xbc3d97611ef9bf83, 0x5599648b1ea919b5}},
+ {{0x47d975b9a3700de8, 0x7280c5fbe2f80552, 0x53658f2732e45de1, 0x431f2c7f665f80b5}},
+ {{0xd6026344858f7b19, 0x14ab352fa1ea514a, 0x8900441a2090a9d7, 0x7b04715f91253b26}}},
+{{{0x83edbd28acf6ae43, 0x86357c8b7d5c7ab4, 0xc0404769b7eb2c44, 0x59b37bf5c2f6583f}},
+ {{0xb376c280c4e6bac6, 0x970ed3dd6d1d9b0b, 0xb09a9558450bf944, 0x48d0acfa57cde223}},
+ {{0xb60f26e47dabe671, 0xf1d1a197622f3a37, 0x4208ce7ee9960394, 0x16234191336d3bdb}}},
+{{{0xf19aeac733a63aef, 0x2c7fba5d4442454e, 0x5da87aa04795e441, 0x413051e1a4e0b0f5}},
+ {{0x852dd1fd3d578bbe, 0x2b65ce72c3286108, 0x658c07f4eace2273, 0x0933f804ec38ab40}},
+ {{0xa7ab69798d496476, 0x8121aadefcb5abc8, 0xa5dc12ef7b539472, 0x07fd47065e45351a}}},
+{{{0xc8583c3d258d2bcd, 0x17029a4daf60b73f, 0xfa0fc9d6416a3781, 0x1c1e5fba38b3fb23}},
+ {{0x304211559ae8e7c3, 0xf281b229944882a5, 0x8a13ac2e378250e4, 0x014afa0954ba48f4}},
+ {{0xcb3197001bb3666c, 0x330060524bffecb9, 0x293711991a88233c, 0x291884363d4ed364}}},
+{{{0x033c6805dc4babfa, 0x2c15bf5e5596ecc1, 0x1bc70624b59b1d3b, 0x3ede9850a19f0ec5}},
+ {{0xfb9d37c3bc1ab6eb, 0x02be14534d57a240, 0xf4d73415f8a5e1f6, 0x5964f4300ccc8188}},
+ {{0xe44a23152d096800, 0x5c08c55970866996, 0xdf2db60a46affb6e, 0x579155c1f856fd89}}},
+{{{0x96324edd12e0c9ef, 0x468b878df2420297, 0x199a3776a4f573be, 0x1e7fbcf18e91e92a}},
+ {{0xb5f16b630817e7a6, 0x808c69233c351026, 0x324a983b54cef201, 0x53c092084a485345}},
+ {{0xd2d41481f1cbafbf, 0x231d2db6716174e5, 0x0b7d7656e2a55c98, 0x3e955cd82aa495f6}}},
+{{{0xe48f535e3ed15433, 0xd075692a0d7270a3, 0x40fbd21daade6387, 0x14264887cf4495f5}},
+ {{0xab39f3ef61bb3a3f, 0x8eb400652eb9193e, 0xb5de6ecc38c11f74, 0x654d7e9626f3c49f}},
+ {{0xe564cfdd5c7d2ceb, 0x82eeafded737ccb9, 0x6107db62d1f9b0ab, 0x0b6baac3b4358dbb}}},
+{{{0x7ae62bcb8622fe98, 0x47762256ceb891af, 0x1a5a92bcf2e406b4, 0x7d29401784e41501}},
+ {{0x204abad63700a93b, 0xbe0023d3da779373, 0xd85f0346633ab709, 0x00496dc490820412}},
+ {{0x1c74b88dc27e6360, 0x074854268d14850c, 0xa145fb7b3e0dcb30, 0x10843f1b43803b23}}},
+{{{0xc5f90455376276dd, 0xce59158dd7645cd9, 0x92f65d511d366b39, 0x11574b6e526996c4}},
+ {{0xd56f672de324689b, 0xd1da8aedb394a981, 0xdd7b58fe9168cfed, 0x7ce246cd4d56c1e8}},
+ {{0xb8f4308e7f80be53, 0x5f3cb8cb34a9d397, 0x18a961bd33cc2b2c, 0x710045fb3a9af671}}},
+{{{0x73f93d36101b95eb, 0xfaef33794f6f4486, 0x5651735f8f15e562, 0x7fa3f19058b40da1}},
+ {{0xa03fc862059d699e, 0x2370cfa19a619e69, 0xc4fe3b122f823deb, 0x1d1b056fa7f0844e}},
+ {{0x1bc64631e56bf61f, 0xd379ab106e5382a3, 0x4d58c57e0540168d, 0x566256628442d8e4}}},
+{{{0xb9e499def6267ff6, 0x7772ca7b742c0843, 0x23a0153fe9a4f2b1, 0x2cdfdfecd5d05006}},
+ {{0xdd499cd61ff38640, 0x29cd9bc3063625a0, 0x51e2d8023dd73dc3, 0x4a25707a203b9231}},
+ {{0x2ab7668a53f6ed6a, 0x304242581dd170a1, 0x4000144c3ae20161, 0x5721896d248e49fc}}},
+{{{0x0b6e5517fd181bae, 0x9022629f2bb963b4, 0x5509bce932064625, 0x578edd74f63c13da}},
+ {{0x285d5091a1d0da4e, 0x4baa6fa7b5fe3e08, 0x63e5177ce19393b3, 0x03c935afc4b030fd}},
+ {{0x997276c6492b0c3d, 0x47ccc2c4dfe205fc, 0xdcd29b84dd623a3c, 0x3ec2ab590288c7a2}}},
+{{{0xa1a0d27be4d87bb9, 0xa98b4deb61391aed, 0x99a0ddd073cb9b83, 0x2dd5c25a200fcace}},
+ {{0xa7213a09ae32d1cb, 0x0f2b87df40f5c2d5, 0x0baea4c6e81eab29, 0x0e1bf66c6adbac5e}},
+ {{0xe2abd5e9792c887e, 0x1a020018cb926d5d, 0xbfba69cdbaae5f1e, 0x730548b35ae88f5f}}},
+{{{0xc43551a3cba8b8ee, 0x65a26f1db2115f16, 0x760f4f52ab8c3850, 0x3043443b411db8ca}},
+ {{0x805b094ba1d6e334, 0xbf3ef17709353f19, 0x423f06cb0622702b, 0x585a2277d87845dd}},
+ {{0xa18a5f8233d48962, 0x6698c4b5ec78257f, 0xa78e6fa5373e41ff, 0x7656278950ef981f}}},
+{{{0x38c3cf59d51fc8c0, 0x9bedd2fd0506b6f2, 0x26bf109fab570e8f, 0x3f4160a8c1b846a6}},
+ {{0xe17073a3ea86cf9d, 0x3a8cfbb707155fdc, 0x4853e7fc31838a8e, 0x28bbf484b613f616}},
+ {{0xf2612f5c6f136c7c, 0xafead107f6dd11be, 0x527e9ad213de6f33, 0x1e79cb358188f75d}}},
+{{{0x013436c3eef7e3f1, 0x828b6a7ffe9e10f8, 0x7ff908e5bcf9defc, 0x65d7951b3a3b3831}},
+ {{0x77e953d8f5e08181, 0x84a50c44299dded9, 0xdc6c2d0c864525e5, 0x478ab52d39d1f2f4}},
+ {{0x66a6a4d39252d159, 0xe5dde1bc871ac807, 0xb82c6b40a6c1c96f, 0x16d87a411a212214}}},
+{{{0xb3bd7e5a42066215, 0x879be3cd0c5a24c1, 0x57c05db1d6f994b7, 0x28f87c8165f38ca6}},
+ {{0xfba4d5e2d54e0583, 0xe21fafd72ebd99fa, 0x497ac2736ee9778f, 0x1f990b577a5a6dde}},
+ {{0xa3344ead1be8f7d6, 0x7d1e50ebacea798f, 0x77c6569e520de052, 0x45882fe1534d6d3e}}},
+{{{0x6669345d757983d6, 0x62b6ed1117aa11a6, 0x7ddd1857985e128f, 0x688fe5b8f626f6dd}},
+ {{0xd8ac9929943c6fe4, 0xb5f9f161a38392a2, 0x2699db13bec89af3, 0x7dcf843ce405f074}},
+ {{0x6c90d6484a4732c0, 0xd52143fdca563299, 0xb3be28c3915dc6e1, 0x6739687e7327191b}}},
+{{{0x9f65c5ea200814cf, 0x840536e169a31740, 0x8b0ed13925c8b4ad, 0x0080dbafe936361d}},
+ {{0x8ce5aad0c9cb971f, 0x1156aaa99fd54a29, 0x41f7247015af9b78, 0x1fe8cca8420f49aa}},
+ {{0x72a1848f3c0cc82a, 0x38c560c2877c9e54, 0x5004e228ce554140, 0x042418a103429d71}}},
+{{{0x899dea51abf3ff5f, 0x9b93a8672fc2d8ba, 0x2c38cb97be6ebd5c, 0x114d578497263b5d}},
+ {{0x58e84c6f20816247, 0x8db2b2b6e36fd793, 0x977182561d484d85, 0x0822024f8632abd7}},
+ {{0xb301bb7c6b1beca3, 0x55393f6dc6eb1375, 0x910d281097b6e4eb, 0x1ad4548d9d479ea3}}},
+{{{0xcd5a7da0389a48fd, 0xb38fa4aa9a78371e, 0xc6d9761b2cdb8e6c, 0x35cf51dbc97e1443}},
+ {{0xa06fe66d0fe9fed3, 0xa8733a401c587909, 0x30d14d800df98953, 0x41ce5876c7b30258}},
+ {{0x59ac3bc5d670c022, 0xeae67c109b119406, 0x9798bdf0b3782fda, 0x651e3201fd074092}}},
+{{{0xd63d8483ef30c5cf, 0x4cd4b4962361cc0c, 0xee90e500a48426ac, 0x0af51d7d18c14eeb}},
+ {{0xa57ba4a01efcae9e, 0x769f4beedc308a94, 0xd1f10eeb3603cb2e, 0x4099ce5e7e441278}},
+ {{0x1ac98e4f8a5121e9, 0x7dae9544dbfa2fe0, 0x8320aa0dd6430df9, 0x667282652c4a2fb5}}},
+{{{0x874621f4d86bc9ab, 0xb54c7bbe56fe6fea, 0x077a24257fadc22c, 0x1ab53be419b90d39}},
+ {{0xada8b6e02946db23, 0x1c0ce51a7b253ab7, 0x8448c85a66dd485b, 0x7f1fc025d0675adf}},
+ {{0xd8ee1b18319ea6aa, 0x004d88083a21f0da, 0x3bd6aa1d883a4f4b, 0x4db9a3a6dfd9fd14}}},
+{{{0x8ce7b23bb99c0755, 0x35c5d6edc4f50f7a, 0x7e1e2ed2ed9b50c3, 0x36305f16e8934da1}},
+ {{0xd95b00bbcbb77c68, 0xddbc846a91f17849, 0x7cf700aebe28d9b3, 0x5ce1285c85d31f3e}},
+ {{0x31b6972d98b0bde8, 0x7d920706aca6de5b, 0xe67310f8908a659f, 0x50fac2a6efdf0235}}},
+{{{0xf3d3a9f35b880f5a, 0xedec050cdb03e7c2, 0xa896981ff9f0b1a2, 0x49a4ae2bac5e34a4}},
+ {{0x295b1c86f6f449bc, 0x51b2e84a1f0ab4dd, 0xc001cb30aa8e551d, 0x6a28d35944f43662}},
+ {{0x28bb12ee04a740e0, 0x14313bbd9bce8174, 0x72f5b5e4e8c10c40, 0x7cbfb19936adcd5b}}},
+{{{0xa311ddc26b89792d, 0x1b30b4c6da512664, 0x0ca77b4ccf150859, 0x1de443df1b009408}},
+ {{0x8e793a7acc36e6e0, 0xf9fab7a37d586eed, 0x3a4f9692bae1f4e4, 0x1c14b03eff5f447e}},
+ {{0x19647bd114a85291, 0x57b76cb21034d3af, 0x6329db440f9d6dfa, 0x5ef43e586a571493}}},
+{{{0xef782014385675a6, 0xa2649f30aafda9e8, 0x4cd1eb505cdfa8cb, 0x46115aba1d4dc0b3}},
+ {{0xa66dcc9dc80c1ac0, 0x97a05cf41b38a436, 0xa7ebf3be95dbd7c6, 0x7da0b8f68d7e7dab}},
+ {{0xd40f1953c3b5da76, 0x1dac6f7321119e9b, 0x03cc6021feb25960, 0x5a5f887e83674b4b}}},
+{{{0x8f6301cf70a13d11, 0xcfceb815350dd0c4, 0xf70297d4a4bca47e, 0x3669b656e44d1434}},
+ {{0x9e9628d3a0a643b9, 0xb5c3cb00e6c32064, 0x9b5302897c2dec32, 0x43e37ae2d5d1c70c}},
+ {{0x387e3f06eda6e133, 0x67301d5199a13ac0, 0xbd5ad8f836263811, 0x6a21e6cd4fd5e9be}}},
+{{{0xf1c6170a3046e65f, 0x58712a2a00d23524, 0x69dbbd3c8c82b755, 0x586bf9f1a195ff57}},
+ {{0xef4129126699b2e3, 0x71d30847708d1301, 0x325432d01182b0bd, 0x45371b07001e8b36}},
+ {{0xa6db088d5ef8790b, 0x5278f0dc610937e5, 0xac0349d261a16eb8, 0x0eafb03790e52179}}},
+{{{0x960555c13748042f, 0x219a41e6820baa11, 0x1c81f73873486d0c, 0x309acc675a02c661}},
+ {{0x5140805e0f75ae1d, 0xec02fbe32662cc30, 0x2cebdf1eea92396d, 0x44ae3344c5435bb3}},
+ {{0x9cf289b9bba543ee, 0xf3760e9d5ac97142, 0x1d82e5c64f9360aa, 0x62d5221b7f94678f}}},
+{{{0x524c299c18d0936d, 0xc86bb56c8a0c1a0c, 0xa375052edb4a8631, 0x5c0efde4bc754562}},
+ {{0x7585d4263af77a3c, 0xdfae7b11fee9144d, 0xa506708059f7193d, 0x14f29a5383922037}},
+ {{0xdf717edc25b2d7f5, 0x21f970db99b53040, 0xda9234b7c3ed4c62, 0x5e72365c7bee093e}}},
+{{{0x575bfc074571217f, 0x3779675d0694d95b, 0x9a0a37bbf4191e33, 0x77f1104c47b4eabc}},
+ {{0x7d9339062f08b33e, 0x5b9659e5df9f32be, 0xacff3dad1f9ebdfd, 0x70b20555cb7349b7}},
+ {{0xbe5113c555112c4c, 0x6688423a9a881fcd, 0x446677855e503b47, 0x0e34398f4a06404a}}},
+{{{0xb67d22d93ecebde8, 0x09b3e84127822f07, 0x743fa61fb05b6d8d, 0x5e5405368a362372}},
+ {{0x18930b093e4b1928, 0x7de3e10e73f3f640, 0xf43217da73395d6f, 0x6f8aded6ca379c3e}},
+ {{0xe340123dfdb7b29a, 0x487b97e1a21ab291, 0xf9967d02fde6949e, 0x780de72ec8d3de97}}},
+{{{0x0ae28545089ae7bc, 0x388ddecf1c7f4d06, 0x38ac15510a4811b8, 0x0eb28bf671928ce4}},
+ {{0x671feaf300f42772, 0x8f72eb2a2a8c41aa, 0x29a17fd797373292, 0x1defc6ad32b587a6}},
+ {{0xaf5bbe1aef5195a7, 0x148c1277917b15ed, 0x2991f7fb7ae5da2e, 0x467d201bf8dd2867}}},
+{{{0x7906ee72f7bd2e6b, 0x05d270d6109abf4e, 0x8d5cfe45b941a8a4, 0x44c218671c974287}},
+ {{0x745f9d56296bc318, 0x993580d4d8152e65, 0xb0e5b13f5839e9ce, 0x51fc2b28d43921c0}},
+ {{0x1b8fd11795e2a98c, 0x1c4e5ee12b6b6291, 0x5b30e7107424b572, 0x6e6b9de84c4f4ac6}}},
+{{{0xdff25fce4b1de151, 0xd841c0c7e11c4025, 0x2554b3c854749c87, 0x2d292459908e0df9}},
+ {{0x6b7c5f10f80cb088, 0x736b54dc56e42151, 0xc2b620a5c6ef99c4, 0x5f4c802cc3a06f42}},
+ {{0x9b65c8f17d0752da, 0x881ce338c77ee800, 0xc3b514f05b62f9e3, 0x66ed5dd5bec10d48}}},
+{{{0x7d38a1c20bb2089d, 0x808334e196ccd412, 0xc4a70b8c6c97d313, 0x2eacf8bc03007f20}},
+ {{0xf0adf3c9cbca047d, 0x81c3b2cbf4552f6b, 0xcfda112d44735f93, 0x1f23a0c77e20048c}},
+ {{0xf235467be5bc1570, 0x03d2d9020dbab38c, 0x27529aa2fcf9e09e, 0x0840bef29d34bc50}}},
+{{{0x796dfb35dc10b287, 0x27176bcd5c7ff29d, 0x7f3d43e8c7b24905, 0x0304f5a191c54276}},
+ {{0xcd54e06b7f37e4eb, 0x8cc15f87f5e96cca, 0xb8248bb0d3597dce, 0x246affa06074400c}},
+ {{0x37d88e68fbe45321, 0x86097548c0d75032, 0x4e9b13ef894a0d35, 0x25a83cac5753d325}}},
+{{{0x10222f48eed8165e, 0x623fc1234b8bcf3a, 0x1e145c09c221e8f0, 0x7ccfa59fca782630}},
+ {{0x9f0f66293952b6e2, 0x33db5e0e0934267b, 0xff45252bd609fedc, 0x06be10f5c506e0c9}},
+ {{0x1a9615a9b62a345f, 0x22050c564a52fecc, 0xa7a2788528bc0dfe, 0x5e82770a1a1ee71d}}},
+{{{0x35425183ad896a5c, 0xe8673afbe78d52f6, 0x2c66f25f92a35f64, 0x09d04f3b3b86b102}},
+ {{0xe802e80a42339c74, 0x34175166a7fffae5, 0x34865d1f1c408cae, 0x2cca982c605bc5ee}},
+ {{0xfd2d5d35197dbe6e, 0x207c2eea8be4ffa3, 0x2613d8db325ae918, 0x7a325d1727741d3e}}},
+{{{0xd036b9bbd16dfde2, 0xa2055757c497a829, 0x8e6cc966a7f12667, 0x4d3b1a791239c180}},
+ {{0xecd27d017e2a076a, 0xd788689f1636495e, 0x52a61af0919233e5, 0x2a479df17bb1ae64}},
+ {{0x9e5eee8e33db2710, 0x189854ded6c43ca5, 0xa41c22c592718138, 0x27ad5538a43a5e9b}}},
+{{{0x2746dd4b15350d61, 0xd03fcbc8ee9521b7, 0xe86e365a138672ca, 0x510e987f7e7d89e2}},
+ {{0xcb5a7d638e47077c, 0x8db7536120a1c059, 0x549e1e4d8bedfdcc, 0x080153b7503b179d}},
+ {{0xdda69d930a3ed3e3, 0x3d386ef1cd60a722, 0xc817ad58bdaa4ee6, 0x23be8d554fe7372a}}},
+{{{0x95fe919a74ef4fad, 0x3a827becf6a308a2, 0x964e01d309a47b01, 0x71c43c4f5ba3c797}},
+ {{0xbc1ef4bd567ae7a9, 0x3f624cb2d64498bd, 0xe41064d22c1f4ec8, 0x2ef9c5a5ba384001}},
+ {{0xb6fd6df6fa9e74cd, 0xf18278bce4af267a, 0x8255b3d0f1ef990e, 0x5a758ca390c5f293}}},
+{{{0xa2b72710d9462495, 0x3aa8c6d2d57d5003, 0xe3d400bfa0b487ca, 0x2dbae244b3eb72ec}},
+ {{0x8ce0918b1d61dc94, 0x8ded36469a813066, 0xd4e6a829afe8aad3, 0x0a738027f639d43f}},
+ {{0x980f4a2f57ffe1cc, 0x00670d0de1839843, 0x105c3f4a49fb15fd, 0x2698ca635126a69c}}},
+{{{0xe765318832b0ba78, 0x381831f7925cff8b, 0x08a81b91a0291fcc, 0x1fb43dcc49caeb07}},
+ {{0x2e3d702f5e3dd90e, 0x9e3f0918e4d25386, 0x5e773ef6024da96a, 0x3c004b0c4afa3332}},
+ {{0x9aa946ac06f4b82b, 0x1ca284a5a806c4f3, 0x3ed3265fc6cd4787, 0x6b43fd01cd1fd217}}},
+{{{0xc7a75d4b4697c544, 0x15fdf848df0fffbf, 0x2868b9ebaa46785a, 0x5a68d7105b52f714}},
+ {{0xb5c742583e760ef3, 0x75dc52b9ee0ab990, 0xbf1427c2072b923f, 0x73420b2d6ff0d9f0}},
+ {{0xaf2cf6cb9e851e06, 0x8f593913c62238c4, 0xda8ab89699fbf373, 0x3db5632fea34bc9e}}},
+{{{0xf46eee2bf75dd9d8, 0x0d17b1f6396759a5, 0x1bf2d131499e7273, 0x04321adf49d75f13}},
+ {{0x2e4990b1829825d5, 0xedeaeb873e9a8991, 0xeef03d394c704af8, 0x59197ea495df2b0e}},
+ {{0x04e16019e4e55aae, 0xe77b437a7e2f92e9, 0xc7ce2dc16f159aa4, 0x45eafdc1f4d70cc0}}},
+{{{0x698401858045d72b, 0x4c22faa2cf2f0651, 0x941a36656b222dc6, 0x5a5eebc80362dade}},
+ {{0xb60e4624cfccb1ed, 0x59dbc292bd5c0395, 0x31a09d1ddc0481c9, 0x3f73ceea5d56d940}},
+ {{0xb7a7bfd10a4e8dc6, 0xbe57007e44c9b339, 0x60c1207f1557aefa, 0x26058891266218db}}},
+{{{0x59f704a68360ff04, 0xc3d93fde7661e6f4, 0x831b2a7312873551, 0x54ad0c2e4e615d57}},
+ {{0x4c818e3cc676e542, 0x5e422c9303ceccad, 0xec07cccab4129f08, 0x0dedfa10b24443b8}},
+ {{0xee3b67d5b82b522a, 0x36f163469fa5c1eb, 0xa5b4d2f26ec19fd3, 0x62ecb2baa77a9408}}},
+{{{0xe5ed795261152b3d, 0x4962357d0eddd7d1, 0x7482c8d0b96b4c71, 0x2e59f919a966d8be}},
+ {{0x92072836afb62874, 0x5fcd5e8579e104a5, 0x5aad01adc630a14a, 0x61913d5075663f98}},
+ {{0x0dc62d361a3231da, 0xfa47583294200270, 0x02d801513f9594ce, 0x3ddbc2a131c05d5c}}},
+{{{0x3f50a50a4ffb81ef, 0xb1e035093bf420bf, 0x9baa8e1cc6aa2cd0, 0x32239861fa237a40}},
+ {{0xfb735ac2004a35d1, 0x31de0f433a6607c3, 0x7b8591bfc528d599, 0x55be9a25f5bb050c}},
+ {{0x0d005acd33db3dbf, 0x0111b37c80ac35e2, 0x4892d66c6f88ebeb, 0x770eadb16508fbcd}}},
+{{{0x8451f9e05e4e89dd, 0xc06302ffbc793937, 0x5d22749556a6495c, 0x09a6755ca05603fb}},
+ {{0xf1d3b681a05071b9, 0x2207659a3592ff3a, 0x5f0169297881e40e, 0x16bedd0e86ba374e}},
+ {{0x5ecccc4f2c2737b5, 0x43b79e0c2dccb703, 0x33e008bc4ec43df3, 0x06c1b840f07566c0}}},
+{{{0x7688a5c6a388f877, 0x02a96c14deb2b6ac, 0x64c9f3431b8c2af8, 0x3628435554a1eed6}},
+ {{0x69ee9e7f9b02805c, 0xcbff828a547d1640, 0x3d93a869b2430968, 0x46b7b8cd3fe26972}},
+ {{0xe9812086fe7eebe0, 0x4cba6be72f515437, 0x1d04168b516efae9, 0x5ea1391043982cb9}}},
+{{{0x49125c9cf4702ee1, 0x4520b71f8b25b32d, 0x33193026501fef7e, 0x656d8997c8d2eb2b}},
+ {{0x6f2b3be4d5d3b002, 0xafec33d96a09c880, 0x035f73a4a8bcc4cc, 0x22c5b9284662198b}},
+ {{0xcb58c8fe433d8939, 0x89a0cb2e6a8d7e50, 0x79ca955309fbbe5a, 0x0c626616cd7fc106}}},
+{{{0x1ffeb80a4879b61f, 0x6396726e4ada21ed, 0x33c7b093368025ba, 0x471aa0c6f3c31788}},
+ {{0x8fdfc379fbf454b1, 0x45a5a970f1a4b771, 0xac921ef7bad35915, 0x42d088dca81c2192}},
+ {{0x8fda0f37a0165199, 0x0adadb77c8a0e343, 0x20fbfdfcc875e820, 0x1cf2bea80c2206e7}}},
+{{{0xc2ddf1deb36202ac, 0x92a5fe09d2e27aa5, 0x7d1648f6fc09f1d3, 0x74c2cc0513bc4959}},
+ {{0x982d6e1a02c0412f, 0x90fa4c83db58e8fe, 0x01c2f5bcdcb18bc0, 0x686e0c90216abc66}},
+ {{0x1fadbadba54395a7, 0xb41a02a0ae0da66a, 0xbf19f598bba37c07, 0x6a12b8acde48430d}}},
+{{{0xf8daea1f39d495d9, 0x592c190e525f1dfc, 0xdb8cbd04c9991d1b, 0x11f7fda3d88f0cb7}},
+ {{0x793bdd801aaeeb5f, 0x00a2a0aac1518871, 0xe8a373a31f2136b4, 0x48aab888fc91ef19}},
+ {{0x041f7e925830f40e, 0x002d6ca979661c06, 0x86dc9ff92b046a2e, 0x760360928b0493d1}}},
+{{{0x21bb41c6120cf9c6, 0xeab2aa12decda59b, 0xc1a72d020aa48b34, 0x215d4d27e87d3b68}},
+ {{0xb43108e5695a0b05, 0x6cb00ee8ad37a38b, 0x5edad6eea3537381, 0x3f2602d4b6dc3224}},
+ {{0xc8b247b65bcaf19c, 0x49779dc3b1b2c652, 0x89a180bbd5ece2e2, 0x13f098a3cec8e039}}},
+{{{0x9adc0ff9ce5ec54b, 0x039c2a6b8c2f130d, 0x028007c7f0f89515, 0x78968314ac04b36b}},
+ {{0xf3aa57a22796bb14, 0x883abab79b07da21, 0xe54be21831a0391c, 0x5ee7fb38d83205f9}},
+ {{0x538dfdcb41446a8e, 0xa5acfda9434937f9, 0x46af908d263c8c78, 0x61d0633c9bca0d09}}},
+{{{0x63744935ffdb2566, 0xc5bd6b89780b68bb, 0x6f1b3280553eec03, 0x6e965fd847aed7f5}},
+ {{0xada328bcf8fc73df, 0xee84695da6f037fc, 0x637fb4db38c2a909, 0x5b23ac2df8067bdc}},
+ {{0x9ad2b953ee80527b, 0xe88f19aafade6d8d, 0x0e711704150e82cf, 0x79b9bbb9dd95dedc}}},
+{{{0xebb355406a3126c2, 0xd26383a868c8c393, 0x6c0c6429e5b97a82, 0x5065f158c9fd2147}},
+ {{0xd1997dae8e9f7374, 0xa032a2f8cfbb0816, 0xcd6cba126d445f0a, 0x1ba811460accb834}},
+ {{0x708169fb0c429954, 0xe14600acd76ecf67, 0x2eaab98a70e645ba, 0x3981f39e58a4faf2}}},
+{{{0x18fb8a7559230a93, 0x1d168f6960e6f45d, 0x3a85a94514a93cb5, 0x38dc083705acd0fd}},
+ {{0xc845dfa56de66fde, 0xe152a5002c40483a, 0xe9d2e163c7b4f632, 0x30f4452edcbc1b65}},
+ {{0x856d2782c5759740, 0xfa134569f99cbecc, 0x8844fc73c0ea4e71, 0x632d9a1a593f2469}}},
+{{{0xf6bb6b15b807cba6, 0x1823c7dfbc54f0d7, 0xbb1d97036e29670b, 0x0b24f48847ed4a57}},
+ {{0xbf09fd11ed0c84a7, 0x63f071810d9f693a, 0x21908c2d57cf8779, 0x3a5a7df28af64ba2}},
+ {{0xdcdad4be511beac7, 0xa4538075ed26ccf2, 0xe19cff9f005f9a65, 0x34fcf74475481f63}}},
+{{{0xc197e04c789767ca, 0xb8714dcb38d9467d, 0x55de888283f95fa8, 0x3d3bdc164dfa63f7}},
+ {{0xa5bb1dab78cfaa98, 0x5ceda267190b72f2, 0x9309c9110a92608e, 0x0119a3042fb374b0}},
+ {{0x67a2d89ce8c2177d, 0x669da5f66895d0c1, 0xf56598e5b282a2b0, 0x56c088f1ede20a73}}},
+{{{0x336d3d1110a86e17, 0xd7f388320b75b2fa, 0xf915337625072988, 0x09674c6b99108b87}},
+ {{0x581b5fac24f38f02, 0xa90be9febae30cbd, 0x9a2169028acf92f0, 0x038b7ea48359038f}},
+ {{0x9f4ef82199316ff8, 0x2f49d282eaa78d4f, 0x0971a5ab5aef3174, 0x6e5e31025969eb65}}},
+{{{0xb16c62f587e593fb, 0x4999eddeca5d3e71, 0xb491c1e014cc3e6d, 0x08f5114789a8dba8}},
+ {{0x3304fb0e63066222, 0xfb35068987acba3f, 0xbd1924778c1061a3, 0x3058ad43d1838620}},
+ {{0x323c0ffde57663d0, 0x05c3df38a22ea610, 0xbdc78abdac994f9a, 0x26549fa4efe3dc99}}},
+{{{0x738b38d787ce8f89, 0xb62658e24179a88d, 0x30738c9cf151316d, 0x49128c7f727275c9}},
+ {{0x04dbbc17f75396b9, 0x69e6a2d7d2f86746, 0xc6409d99f53eabc6, 0x606175f6332e25d2}},
+ {{0x4021370ef540e7dd, 0x0910d6f5a1f1d0a5, 0x4634aacd5b06b807, 0x6a39e6356944f235}}},
+{{{0x96cd5640df90f3e7, 0x6c3a760edbfa25ea, 0x24f3ef0959e33cc4, 0x42889e7e530d2e58}},
+ {{0x1da1965774049e9d, 0xfbcd6ea198fe352b, 0xb1cbcd50cc5236a6, 0x1f5ec83d3f9846e2}},
+ {{0x8efb23c3328ccb75, 0xaf42a207dd876ee9, 0x20fbdadc5dfae796, 0x241e246b06bf9f51}}},
+{{{0x29e68e57ad6e98f6, 0x4c9260c80b462065, 0x3f00862ea51ebb4b, 0x5bc2c77fb38d9097}},
+ {{0x7eaafc9a6280bbb8, 0x22a70f12f403d809, 0x31ce40bb1bfc8d20, 0x2bc65635e8bd53ee}},
+ {{0xe8d5dc9fa96bad93, 0xe58fb17dde1947dc, 0x681532ea65185fa3, 0x1fdd6c3b034a7830}}},
+{{{0x0a64e28c55dc18fe, 0xe3df9e993399ebdd, 0x79ac432370e2e652, 0x35ff7fc33ae4cc0e}},
+ {{0x9c13a6a52dd8f7a9, 0x2dbb1f8c3efdcabf, 0x961e32405e08f7b5, 0x48c8a121bbe6c9e5}},
+ {{0xfc415a7c59646445, 0xd224b2d7c128b615, 0x6035c9c905fbb912, 0x42d7a91274429fab}}},
+{{{0x4e6213e3eaf72ed3, 0x6794981a43acd4e7, 0xff547cde6eb508cb, 0x6fed19dd10fcb532}},
+ {{0xa9a48947933da5bc, 0x4a58920ec2e979ec, 0x96d8800013e5ac4c, 0x453692d74b48b147}},
+ {{0xdd775d99a8559c6f, 0xf42a2140df003e24, 0x5223e229da928a66, 0x063f46ba6d38f22c}}},
+{{{0xd2d242895f536694, 0xca33a2c542939b2c, 0x986fada6c7ddb95c, 0x5a152c042f712d5d}},
+ {{0x39843cb737346921, 0xa747fb0738c89447, 0xcb8d8031a245307e, 0x67810f8e6d82f068}},
+ {{0x3eeb8fbcd2287db4, 0x72c7d3a301a03e93, 0x5473e88cbd98265a, 0x7324aa515921b403}}},
+{{{0x857942f46c3cbe8e, 0xa1d364b14730c046, 0x1c8ed914d23c41bf, 0x0838e161eef6d5d2}},
+ {{0xad23f6dae82354cb, 0x6962502ab6571a6d, 0x9b651636e38e37d1, 0x5cac5005d1a3312f}},
+ {{0x8cc154cce9e39904, 0x5b3a040b84de6846, 0xc4d8a61cb1be5d6e, 0x40fb897bd8861f02}}},
+{{{0x84c5aa9062de37a1, 0x421da5000d1d96e1, 0x788286306a9242d9, 0x3c5e464a690d10da}},
+ {{0xe57ed8475ab10761, 0x71435e206fd13746, 0x342f824ecd025632, 0x4b16281ea8791e7b}},
+ {{0xd1c101d50b813381, 0xdee60f1176ee6828, 0x0cb68893383f6409, 0x6183c565f6ff484a}}},
+{{{0x741d5a461e6bf9d6, 0x2305b3fc7777a581, 0xd45574a26474d3d9, 0x1926e1dc6401e0ff}},
+ {{0xdb468549af3f666e, 0xd77fcf04f14a0ea5, 0x3df23ff7a4ba0c47, 0x3a10dfe132ce3c85}},
+ {{0xe07f4e8aea17cea0, 0x2fd515463a1fc1fd, 0x175322fd31f2c0f1, 0x1fa1d01d861e5d15}}},
+{{{0xcc8055947d599832, 0x1e4656da37f15520, 0x99f6f7744e059320, 0x773563bc6a75cf33}},
+ {{0x38dcac00d1df94ab, 0x2e712bddd1080de9, 0x7f13e93efdd5e262, 0x73fced18ee9a01e5}},
+ {{0x06b1e90863139cb3, 0xa493da67c5a03ecd, 0x8d77cec8ad638932, 0x1f426b701b864f44}}},
+{{{0xefc9264c41911c01, 0xf1a3b7b817a22c25, 0x5875da6bf30f1447, 0x4e1af5271d31b090}},
+ {{0xf17e35c891a12552, 0xb76b8153575e9c76, 0xfa83406f0d9b723e, 0x0b76bb1b3fa7e438}},
+ {{0x08b8c1f97f92939b, 0xbe6771cbd444ab6e, 0x22e5646399bb8017, 0x7b6dd61eb772a955}}},
+{{{0xb7adc1e850f33d92, 0x7998fa4f608cd5cf, 0xad962dbd8dfc5bdb, 0x703e9bceaf1d2f4f}},
+ {{0x5730abf9ab01d2c7, 0x16fb76dc40143b18, 0x866cbe65a0cbb281, 0x53fa9b659bff6afe}},
+ {{0x6c14c8e994885455, 0x843a5d6665aed4e5, 0x181bb73ebcd65af1, 0x398d93e5c4c61f50}}},
+{{{0x1c4bd16733e248f3, 0xbd9e128715bf0a5f, 0xd43f8cf0a10b0376, 0x53b09b5ddf191b13}},
+ {{0xc3877c60d2e7e3f2, 0x3b34aaa030828bb1, 0x283e26e7739ef138, 0x699c9c9002c30577}},
+ {{0xf306a7235946f1cc, 0x921718b5cce5d97d, 0x28cdd24781b4e975, 0x51caf30c6fcdd907}}},
+{{{0xa60ba7427674e00a, 0x630e8570a17a7bf3, 0x3758563dcf3324cc, 0x5504aa292383fdaa}},
+ {{0x737af99a18ac54c7, 0x903378dcc51cb30f, 0x2b89bc334ce10cc7, 0x12ae29c189f8e99a}},
+ {{0xa99ec0cb1f0d01cf, 0x0dd1efcc3a34f7ae, 0x55ca7521d09c4e22, 0x5fd14fe958eba5ea}}},
+{{{0xb5dc2ddf2845ab2c, 0x069491b10a7fe993, 0x4daaf3d64002e346, 0x093ff26e586474d1}},
+ {{0x3c42fe5ebf93cb8e, 0xbedfa85136d4565f, 0xe0f0859e884220e8, 0x7dd73f960725d128}},
+ {{0xb10d24fe68059829, 0x75730672dbaf23e5, 0x1367253ab457ac29, 0x2f59bcbc86b470a4}}},
+{{{0x83847d429917135f, 0xad1b911f567d03d7, 0x7e7748d9be77aad1, 0x5458b42e2e51af4a}},
+ {{0x7041d560b691c301, 0x85201b3fadd7e71e, 0x16c2e16311335585, 0x2aa55e3d010828b1}},
+ {{0xed5192e60c07444f, 0x42c54e2d74421d10, 0x352b4c82fdb5c864, 0x13e9004a8a768664}}},
+{{{0xcbb5b5556c032bff, 0xdf7191b729297a3a, 0xc1ff7326aded81bb, 0x71ade8bb68be03f5}},
+ {{0x1e6284c5806b467c, 0xc5f6997be75d607b, 0x8b67d958b378d262, 0x3d88d66a81cd8b70}},
+ {{0x8b767a93204ed789, 0x762fcacb9fa0ae2a, 0x771febcc6dce4887, 0x343062158ff05fb3}}},
+{{{0xe05da1a7e1f5bf49, 0x26457d6dd4736092, 0x77dcb07773cc32f6, 0x0a5d94969cdd5fcd}},
+ {{0xfce219072a7b31b4, 0x4d7adc75aa578016, 0x0ec276a687479324, 0x6d6d9d5d1fda4beb}},
+ {{0x22b1a58ae9b08183, 0xfd95d071c15c388b, 0xa9812376850a0517, 0x33384cbabb7f335e}}},
+{{{0x3c6fa2680ca2c7b5, 0x1b5082046fb64fda, 0xeb53349c5431d6de, 0x5278b38f6b879c89}},
+ {{0x33bc627a26218b8d, 0xea80b21fc7a80c61, 0x9458b12b173e9ee6, 0x076247be0e2f3059}},
+ {{0x52e105f61416375a, 0xec97af3685abeba4, 0x26e6b50623a67c36, 0x5cf0e856f3d4fb01}}},
+{{{0xf6c968731ae8cab4, 0x5e20741ecb4f92c5, 0x2da53be58ccdbc3e, 0x2dddfea269970df7}},
+ {{0xbeaece313db342a8, 0xcba3635b842db7ee, 0xe88c6620817f13ef, 0x1b9438aa4e76d5c6}},
+ {{0x8a50777e166f031a, 0x067b39f10fb7a328, 0x1925c9a6010fbd76, 0x6df9b575cc740905}}},
+{{{0x42c1192927f6bdcf, 0x8f91917a403d61ca, 0xdc1c5a668b9e1f61, 0x1596047804ec0f8d}},
+ {{0xecdfc35b48cade41, 0x6a88471fb2328270, 0x740a4a2440a01b6a, 0x471e5796003b5f29}},
+ {{0xda96bbb3aced37ac, 0x7a2423b5e9208cea, 0x24cc5c3038aebae2, 0x50c356afdc5dae2f}}},
+{{{0x09dcbf4341c30318, 0xeeba061183181dce, 0xc179c0cedc1e29a1, 0x1dbf7b89073f35b0}},
+ {{0xcfed9cdf1b31b964, 0xf486a9858ca51af3, 0x14897265ea8c1f84, 0x784a53dd932acc00}},
+ {{0x2d99f9df14fc4920, 0x76ccb60cc4499fe5, 0xa4132cbbe5cf0003, 0x3f93d82354f000ea}}},
+{{{0x8183e7689e04ce85, 0x678fb71e04465341, 0xad92058f6688edac, 0x5da350d3532b099a}},
+ {{0xeaac12d179e14978, 0xff923ff3bbebff5e, 0x4af663e40663ce27, 0x0fd381a811a5f5ff}},
+ {{0xf256aceca436df54, 0x108b6168ae69d6e8, 0x20d986cb6b5d036c, 0x655957b9fee2af50}}},
+{{{0xaea8b07fa902030f, 0xf88c766af463d143, 0x15b083663c787a60, 0x08eab1148267a4a8}},
+ {{0xbdc1409bd002d0ac, 0x66660245b5ccd9a6, 0x82317dc4fade85ec, 0x02fe934b6ad7df0d}},
+ {{0xef5cf100cfb7ea74, 0x22897633a1cb42ac, 0xd4ce0c54cef285e2, 0x30408c048a146a55}}},
+{{{0x739d8845832fcedb, 0xfa38d6c9ae6bf863, 0x32bc0dcab74ffef7, 0x73937e8814bce45e}},
+ {{0xbb2e00c9193b877f, 0xece3a890e0dc506b, 0xecf3b7c036de649f, 0x5f46040898de9e1a}},
+ {{0xb9037116297bf48d, 0xa9d13b22d4f06834, 0xe19715574696bdc6, 0x2cf8a4e891d5e835}}},
+{{{0x6d93fd8707110f67, 0xdd4c09d37c38b549, 0x7cb16a4cc2736a86, 0x2049bd6e58252a09}},
+ {{0x2cb5487e17d06ba2, 0x24d2381c3950196b, 0xd7659c8185978a30, 0x7a6f7f2891d6a4f6}},
+ {{0x7d09fd8d6a9aef49, 0xf0ee60be5b3db90b, 0x4c21b52c519ebfd4, 0x6011aadfc545941d}}},
+{{{0x5f67926dcf95f83c, 0x7c7e856171289071, 0xd6a1e7f3998f7a5b, 0x6fc5cc1b0b62f9e0}},
+ {{0x63ded0c802cbf890, 0xfbd098ca0dff6aaa, 0x624d0afdb9b6ed99, 0x69ce18b779340b1e}},
+ {{0xd1ef5528b29879cb, 0xdd1aae3cd47e9092, 0x127e0442189f2352, 0x15596b3ae57101f1}}},
+{{{0x462739d23f9179a2, 0xff83123197d6ddcf, 0x1307deb553f2148a, 0x0d2237687b5f4dda}},
+ {{0x09ff31167e5124ca, 0x0be4158bd9c745df, 0x292b7d227ef556e5, 0x3aa4e241afb6d138}},
+ {{0x2cc138bf2a3305f5, 0x48583f8fa2e926c3, 0x083ab1a25549d2eb, 0x32fcaa6e4687a36c}}},
+{{{0x7bc56e8dc57d9af5, 0x3e0bd2ed9df0bdf2, 0xaac014de22efe4a3, 0x4627e9cefebd6a5c}},
+ {{0x3207a4732787ccdf, 0x17e31908f213e3f8, 0xd5b2ecd7f60d964e, 0x746f6336c2600be9}},
+ {{0x3f4af345ab6c971c, 0xe288eb729943731f, 0x33596a8a0344186d, 0x7b4917007ed66293}}},
+{{{0x2d85fb5cab84b064, 0x497810d289f3bc14, 0x476adc447b15ce0c, 0x122ba376f844fd7b}},
+ {{0x54341b28dd53a2dd, 0xaa17905bdf42fc3f, 0x0ff592d94dd2f8f4, 0x1d03620fe08cd37d}},
+ {{0xc20232cda2b4e554, 0x9ed0fd42115d187f, 0x2eabb4be7dd479d9, 0x02c70bf52b68ec4c}}},
+{{{0xa287ec4b5d0b2fbb, 0x415c5790074882ca, 0xe044a61ec1d0815c, 0x26334f0a409ef5e0}},
+ {{0xace532bf458d72e1, 0x5be768e07cb73cb5, 0x56cf7d94ee8bbde7, 0x6b0697e3feb43a03}},
+ {{0xb6c8f04adf62a3c0, 0x3ef000ef076da45d, 0x9c9cb95849f0d2a9, 0x1cc37f43441b2fae}}},
+{{{0x508f565a5cc7324f, 0xd061c4c0e506a922, 0xfb18abdb5c45ac19, 0x6c6809c10380314a}},
+ {{0xd76656f1c9ceaeb9, 0x1c5b15f818e5656a, 0x26e72832844c2334, 0x3a346f772f196838}},
+ {{0xd2d55112e2da6ac8, 0xe9bd0331b1e851ed, 0x960746dd8ec67262, 0x05911b9f6ef7c5d0}}},
+{{{0xe9dcd756b637ff2d, 0xec4c348fc987f0c4, 0xced59285f3fbc7b7, 0x3305354793e1ea87}},
+ {{0x01c18980c5fe9f94, 0xcd656769716fd5c8, 0x816045c3d195a086, 0x6e2b7f3266cc7982}},
+ {{0xcc802468f7c3568f, 0x9de9ba8219974cb3, 0xabb7229cb5b81360, 0x44e2017a6fbeba62}}},
+{{{0xc4c2a74354dab774, 0x8e5d4c3c4eaf031a, 0xb76c23d242838f17, 0x749a098f68dce4ea}},
+ {{0x87f82cf3b6ca6ecd, 0x580f893e18f4a0c2, 0x058930072604e557, 0x6cab6ac256d19c1d}},
+ {{0xdcdfe0a02cc1de60, 0x032665ff51c5575b, 0x2c0c32f1073abeeb, 0x6a882014cd7b8606}}},
+{{{0xa52a92fea4747fb5, 0xdc12a4491fa5ab89, 0xd82da94bb847a4ce, 0x4d77edce9512cc4e}},
+ {{0xd111d17caf4feb6e, 0x050bba42b33aa4a3, 0x17514c3ceeb46c30, 0x54bedb8b1bc27d75}},
+ {{0x77c8e14577e2189c, 0xa3e46f6aff99c445, 0x3144dfc86d335343, 0x3a96559e7c4216a9}}},
+{{{0x12550d37f42ad2ee, 0x8b78e00498a1fbf5, 0x5d53078233894cb2, 0x02c84e4e3e498d0c}},
+ {{0x4493896880baaa52, 0x4c98afc4f285940e, 0xef4aa79ba45448b6, 0x5278c510a57aae7f}},
+ {{0xa54dd074294c0b94, 0xf55d46b8df18ffb6, 0xf06fecc58dae8366, 0x588657668190d165}}},
+{{{0xd47712311aef7117, 0x50343101229e92c7, 0x7a95e1849d159b97, 0x2449959b8b5d29c9}},
+ {{0xbf5834f03de25cc3, 0xb887c8aed6815496, 0x5105221a9481e892, 0x6760ed19f7723f93}},
+ {{0x669ba3b7ac35e160, 0x2eccf73fba842056, 0x1aec1f17c0804f07, 0x0d96bc031856f4e7}}},
+{{{0x3318be7775c52d82, 0x4cb764b554d0aab9, 0xabcf3d27cc773d91, 0x3bf4d1848123288a}},
+ {{0xb1d534b0cc7505e1, 0x32cd003416c35288, 0xcb36a5800762c29d, 0x5bfe69b9237a0bf8}},
+ {{0x183eab7e78a151ab, 0xbbe990c999093763, 0xff717d6e4ac7e335, 0x4c5cddb325f39f88}}},
+{{{0xc0f6b74d6190a6eb, 0x20ea81a42db8f4e4, 0xa8bd6f7d97315760, 0x33b1d60262ac7c21}},
+ {{0x57750967e7a9f902, 0x2c37fdfc4f5b467e, 0xb261663a3177ba46, 0x3a375e78dc2d532b}},
+ {{0x8141e72f2d4dddea, 0xe6eafe9862c607c8, 0x23c28458573cafd0, 0x46b9476f4ff97346}}},
+{{{0x0c1ffea44f901e5c, 0x2b0b6fb72184b782, 0xe587ff910114db88, 0x37130f364785a142}},
+ {{0x1215505c0d58359f, 0x2a2013c7fc28c46b, 0x24a0a1af89ea664e, 0x4400b638a1130e1f}},
+ {{0x3a01b76496ed19c3, 0x31e00ab0ed327230, 0x520a885783ca15b1, 0x06aab9875accbec7}}},
+{{{0xc1339983f5df0ebb, 0xc0f3758f512c4cac, 0x2cf1130a0bb398e1, 0x6b3cecf9aa270c62}},
+ {{0x5349acf3512eeaef, 0x20c141d31cc1cb49, 0x24180c07a99a688d, 0x555ef9d1c64b2d17}},
+ {{0x36a770ba3b73bd08, 0x624aef08a3afbf0c, 0x5737ff98b40946f2, 0x675f4de13381749d}}},
+{{{0x0e2c52036b1782fc, 0x64816c816cad83b4, 0xd0dcbdd96964073e, 0x13d99df70164c520}},
+ {{0xa12ff6d93bdab31d, 0x0725d80f9d652dfe, 0x019c4ff39abe9487, 0x60f450b882cd3c43}},
+ {{0x014b5ec321e5c0ca, 0x4fcb69c9d719bfa2, 0x4e5f1c18750023a0, 0x1c06de9e55edac80}}},
+{{{0x990f7ad6a33ec4e2, 0x6608f938be2ee08e, 0x9ca143c563284515, 0x4cf38a1fec2db60d}},
+ {{0xffd52b40ff6d69aa, 0x34530b18dc4049bb, 0x5e4a5c2fa34d9897, 0x78096f8e7d32ba2d}},
+ {{0xa0aaaa650dfa5ce7, 0xf9c49e2a48b5478c, 0x4f09cc7d7003725b, 0x373cad3a26091abe}}},
+{{{0xb294634d82c9f57c, 0x1fcbfde124934536, 0x9e9c4db3418cdb5a, 0x0040f3d9454419fc}},
+ {{0xf1bea8fb89ddbbad, 0x3bcb2cbc61aeaecb, 0x8f58a7bb1f9b8d9d, 0x21547eda5112a686}},
+ {{0xdefde939fd5986d3, 0xf4272c89510a380c, 0xb72ba407bb3119b9, 0x63550a334a254df4}}},
+{{{0x6507d6edb569cf37, 0x178429b00ca52ee1, 0xea7c0090eb6bd65d, 0x3eea62c7daf78f51}},
+ {{0x9bba584572547b49, 0xf305c6fae2c408e0, 0x60e8fa69c734f18d, 0x39a92bafaa7d767a}},
+ {{0x9d24c713e693274e, 0x5f63857768dbd375, 0x70525560eb8ab39a, 0x68436a0665c9c4cd}}},
+{{{0xbc0235e8202f3f27, 0xc75c00e264f975b0, 0x91a4e9d5a38c2416, 0x17b6e7f68ab789f9}},
+ {{0x1e56d317e820107c, 0xc5266844840ae965, 0xc1e0a1c6320ffc7a, 0x5373669c91611472}},
+ {{0x5d2814ab9a0e5257, 0x908f2084c9cab3fc, 0xafcaf5885b2d1eca, 0x1cb4b5a678f87d11}}},
+{{{0xb664c06b394afc6c, 0x0c88de2498da5fb1, 0x4f8d03164bcad834, 0x330bca78de7434a2}},
+ {{0x6b74aa62a2a007e7, 0xf311e0b0f071c7b1, 0x5707e438000be223, 0x2dc0fd2d82ef6eac}},
+ {{0x982eff841119744e, 0xf9695e962b074724, 0xc58ac14fbfc953fb, 0x3c31be1b369f1cf5}}},
+{{{0xb0f4864d08948aee, 0x07dc19ee91ba1c6f, 0x7975cdaea6aca158, 0x330b61134262d4bb}},
+ {{0xc168bc93f9cb4272, 0xaeb8711fc7cedb98, 0x7f0e52aa34ac8d7a, 0x41cec1097e7d55bb}},
+ {{0xf79619d7a26d808a, 0xbb1fd49e1d9e156d, 0x73d7c36cdba1df27, 0x26b44cd91f28777d}}},
+{{{0x300a9035393aa6d8, 0x2b501131a12bb1cd, 0x7b1ff677f093c222, 0x4309c1f8cab82bad}},
+ {{0xaf44842db0285f37, 0x8753189047efc8df, 0x9574e091f820979a, 0x0e378d6069615579}},
+ {{0xd9fa917183075a55, 0x4bdb5ad26b009fdc, 0x7829ad2cd63def0e, 0x078fc54975fd3877}}},
+{{{0x87dfbd1428878f2d, 0x134636dd1e9421a1, 0x4f17c951257341a3, 0x5df98d4bad296cb8}},
+ {{0xe2004b5bb833a98a, 0x44775dec2d4c3330, 0x3aa244067eace913, 0x272630e3d58e00a9}},
+ {{0xf3678fd0ecc90b54, 0xf001459b12043599, 0x26725fbc3758b89b, 0x4325e4aa73a719ae}}},
+{{{0x657dc6ef433c3493, 0x65375e9f80dbf8c3, 0x47fd2d465b372dae, 0x4966ab79796e7947}},
+ {{0xed24629acf69f59d, 0x2a4a1ccedd5abbf4, 0x3535ca1f56b2d67b, 0x5d8c68d043b1b42d}},
+ {{0xee332d4de3b42b0a, 0xd84e5a2b16a4601c, 0x78243877078ba3e4, 0x77ed1eb4184ee437}}},
+{{{0xbfd4e13f201839a0, 0xaeefffe23e3df161, 0xb65b04f06b5d1fe3, 0x52e085fb2b62fbc0}},
+ {{0x185d43f89e92ed1a, 0xb04a1eeafe4719c6, 0x499fbe88a6f03f4f, 0x5d8b0d2f3c859bdd}},
+ {{0x124079eaa54cf2ba, 0xd72465eb001b26e7, 0x6843bcfdc97af7fd, 0x0524b42b55eacd02}}},
+{{{0xfd0d5dbee45447b0, 0x6cec351a092005ee, 0x99a47844567579cb, 0x59d242a216e7fa45}},
+ {{0xbc18dcad9b829eac, 0x23ae7d28b5f579d0, 0xc346122a69384233, 0x1a6110b2e7d4ac89}},
+ {{0x4f833f6ae66997ac, 0x6849762a361839a4, 0x6985dec1970ab525, 0x53045e89dcb1f546}}},
+{{{0xcb8bb346d75353db, 0xfcfcb24bae511e22, 0xcba48d40d50ae6ef, 0x26e3bae5f4f7cb5d}},
+ {{0x84da3cde8d45fe12, 0xbd42c218e444e2d2, 0xa85196781f7e3598, 0x7642c93f5616e2b2}},
+ {{0x2323daa74595f8e4, 0xde688c8b857abeb4, 0x3fc48e961c59326e, 0x0b2e73ca15c9b8ba}}},
+{{{0xd6bb4428c17f5026, 0x9eb27223fb5a9ca7, 0xe37ba5031919c644, 0x21ce380db59a6602}},
+ {{0x0e3fbfaf79c03a55, 0x3077af054cbb5acf, 0xd5c55245db3de39f, 0x015e68c1476a4af7}},
+ {{0xc1d5285220066a38, 0x95603e523570aef3, 0x832659a7226b8a4d, 0x5dd689091f8eedc9}}},
+{{{0xcbac84debfd3c856, 0x1624c348b35ff244, 0xb7f88dca5d9cad07, 0x3b0e574da2c2ebe8}},
+ {{0x1d022591a5313084, 0xca2d4aaed6270872, 0x86a12b852f0bfd20, 0x56e6c439ad7da748}},
+ {{0xc704ff4942bdbae6, 0x5e21ade2b2de1f79, 0xe95db3f35652fad8, 0x0822b5378f08ebc1}}},
+{{{0x51f048478f387475, 0xb25dbcf49cbecb3c, 0x9aab1244d99f2055, 0x2c709e6c1c10a5d6}},
+ {{0xe1b7f29362730383, 0x4b5279ffebca8a2c, 0xdafc778abfd41314, 0x7deb10149c72610f}},
+ {{0xcb62af6a8766ee7a, 0x66cbec045553cd0e, 0x588001380f0be4b5, 0x08e68e9ff62ce2ea}}},
+{{{0x34ad500a4bc130ad, 0x8d38db493d0bd49c, 0xa25c3d98500a89be, 0x2f1f3f87eeba3b09}},
+ {{0x2f2d09d50ab8f2f9, 0xacb9218dc55923df, 0x4a8f342673766cb9, 0x4cb13bd738f719f5}},
+ {{0xf7848c75e515b64a, 0xa59501badb4a9038, 0xc20d313f3f751b50, 0x19a1e353c0ae2ee8}}},
+{{{0x7d1c7560bafa05c3, 0xb3e1a0a0c6e55e61, 0xe3529718c0d66473, 0x41546b11c20c3486}},
+ {{0xb42172cdd596bdbd, 0x93e0454398eefc40, 0x9fb15347b44109b5, 0x736bd3990266ae34}},
+ {{0x85532d509334b3b4, 0x46fd114b60816573, 0xcc5f5f30425c8375, 0x412295a2b87fab5c}}},
+{{{0x19c99b88f57ed6e9, 0x5393cb266df8c825, 0x5cee3213b30ad273, 0x14e153ebb52d2e34}},
+ {{0x2e655261e293eac6, 0x845a92032133acdb, 0x460975cb7900996b, 0x0760bb8d195add80}},
+ {{0x413e1a17cde6818a, 0x57156da9ed69a084, 0x2cbf268f46caccb1, 0x6b34be9bc33ac5f2}}},
+{{{0xf3df2f643a78c0b2, 0x4c3e971ef22e027c, 0xec7d1c5e49c1b5a3, 0x2012c18f0922dd2d}},
+ {{0x11fc69656571f2d3, 0xc6c9e845530e737a, 0xe33ae7a2d4fe5035, 0x01b9c7b62e6dd30b}},
+ {{0x880b55e55ac89d29, 0x1483241f45a0a763, 0x3d36efdfc2e76c1f, 0x08af5b784e4bade8}}},
+{{{0x283499dc881f2533, 0x9d0525da779323b6, 0x897addfb673441f4, 0x32b79d71163a168d}},
+ {{0xe27314d289cc2c4b, 0x4be4bd11a287178d, 0x18d528d6fa3364ce, 0x6423c1d5afd9826e}},
+ {{0xcc85f8d9edfcb36a, 0x22bcc28f3746e5f9, 0xe49de338f9e5d3cd, 0x480a5efbc13e2dcc}}},
+{{{0x0b51e70b01622071, 0x06b505cf8b1dafc5, 0x2c6bb061ef5aabcd, 0x47aa27600cb7bf31}},
+ {{0xb6614ce442ce221f, 0x6e199dcc4c053928, 0x663fb4a4dc1cbe03, 0x24b31d47691c8e06}},
+ {{0x2a541eedc015f8c3, 0x11a4fe7e7c693f7c, 0xf0af66134ea278d6, 0x545b585d14dda094}}},
+{{{0x67bf275ea0d43a0f, 0xade68e34089beebe, 0x4289134cd479e72e, 0x0f62f9c332ba5454}},
+ {{0x6204e4d0e3b321e1, 0x3baa637a28ff1e95, 0x0b0ccffd5b99bd9e, 0x4d22dc3e64c8d071}},
+ {{0xfcb46589d63b5f39, 0x5cae6a3f57cbcf61, 0xfebac2d2953afa05, 0x1c0fa01a36371436}}},
+{{{0xe7547449bc7cd692, 0x0f9abeaae6f73ddf, 0x4af01ca700837e29, 0x63ab1b5d3f1bc183}},
+ {{0xc11ee5e854c53fae, 0x6a0b06c12b4f3ff4, 0x33540f80e0b67a72, 0x15f18fc3cd07e3ef}},
+ {{0x32750763b028f48c, 0x06020740556a065f, 0xd53bd812c3495b58, 0x08706c9b865f508d}}},
+{{{0xf37ca2ab3d343dff, 0x1a8c6a2d80abc617, 0x8e49e035d4ccffca, 0x48b46beebaa1d1b9}},
+ {{0xcc991b4138b41246, 0x243b9c526f9ac26b, 0xb9ef494db7cbabbd, 0x5fba433dd082ed00}},
+ {{0x9c49e355c9941ad0, 0xb9734ade74498f84, 0x41c3fed066663e5c, 0x0ecfedf8e8e710b3}}},
+{{{0x76430f9f9cd470d9, 0xb62acc9ba42f6008, 0x1898297c59adad5e, 0x7789dd2db78c5080}},
+ {{0x744f7463e9403762, 0xf79a8dee8dfcc9c9, 0x163a649655e4cde3, 0x3b61788db284f435}},
+ {{0xb22228190d6ef6b2, 0xa94a66b246ce4bfa, 0x46c1a77a4f0b6cc7, 0x4236ccffeb7338cf}}},
+{{{0x8497404d0d55e274, 0x6c6663d9c4ad2b53, 0xec2fb0d9ada95734, 0x2617e120cdb8f73c}},
+ {{0x3bd82dbfda777df6, 0x71b177cc0b98369e, 0x1d0e8463850c3699, 0x5a71945b48e2d1f1}},
+ {{0x6f203dd5405b4b42, 0x327ec60410b24509, 0x9c347230ac2a8846, 0x77de29fc11ffeb6a}}},
+{{{0xb0ac57c983b778a8, 0x53cdcca9d7fe912c, 0x61c2b854ff1f59dc, 0x3a1a2cf0f0de7dac}},
+ {{0x835e138fecced2ca, 0x8c9eaf13ea963b9a, 0xc95fbfc0b2160ea6, 0x575e66f3ad877892}},
+ {{0x99803a27c88fcb3a, 0x345a6789275ec0b0, 0x459789d0ff6c2be5, 0x62f882651e70a8b2}}},
+{{{0x085ae2c759ff1be4, 0x149145c93b0e40b7, 0xc467e7fa7ff27379, 0x4eeecf0ad5c73a95}},
+ {{0x6d822986698a19e0, 0xdc9821e174d78a71, 0x41a85f31f6cb1f47, 0x352721c2bcda9c51}},
+ {{0x48329952213fc985, 0x1087cf0d368a1746, 0x8e5261b166c15aa5, 0x2d5b2d842ed24c21}}},
+{{{0x02cfebd9ebd3ded1, 0xd45b217739021974, 0x7576f813fe30a1b7, 0x5691b6f9a34ef6c2}},
+ {{0x5eb7d13d196ac533, 0x377234ecdb80be2b, 0xe144cffc7cf5ae24, 0x5226bcf9c441acec}},
+ {{0x79ee6c7223e5b547, 0x6f5f50768330d679, 0xed73e1e96d8adce9, 0x27c3da1e1d8ccc03}}},
+{{{0x7eb9efb23fe24c74, 0x3e50f49f1651be01, 0x3ea732dc21858dea, 0x17377bd75bb810f9}},
+ {{0x28302e71630ef9f6, 0xc2d4a2032b64cee0, 0x090820304b6292be, 0x5fca747aa82adf18}},
+ {{0x232a03c35c258ea5, 0x86f23a2c6bcb0cf1, 0x3dad8d0d2e442166, 0x04a8933cab76862b}}},
+{{{0xd2c604b622943dff, 0xbc8cbece44cfb3a0, 0x5d254ff397808678, 0x0fa3614f3b1ca6bf}},
+ {{0x69082b0e8c936a50, 0xf9c9a035c1dac5b6, 0x6fb73e54c4dfb634, 0x4005419b1d2bc140}},
+ {{0xa003febdb9be82f0, 0x2089c1af3a44ac90, 0xf8499f911954fa8e, 0x1fba218aef40ab42}}},
+{{{0xab549448fac8f53e, 0x81f6e89a7ba63741, 0x74fd6c7d6c2b5e01, 0x392e3acaa8c86e42}},
+ {{0x4f3e57043e7b0194, 0xa81d3eee08daaf7f, 0xc839c6ab99dcdef1, 0x6c535d13ff7761d5}},
+ {{0x4cbd34e93e8a35af, 0x2e0781445887e816, 0x19319c76f29ab0ab, 0x25e17fe4d50ac13b}}},
+{{{0x0a289bd71e04f676, 0x208e1c52d6420f95, 0x5186d8b034691fab, 0x255751442a9fb351}},
+ {{0x915f7ff576f121a7, 0xc34a32272fcd87e3, 0xccba2fde4d1be526, 0x6bba828f8969899b}},
+ {{0xe2d1bc6690fe3901, 0x4cb54a18a0997ad5, 0x971d6914af8460d4, 0x559d504f7f6b7be4}}},
+{{{0xa7738378b3eb54d5, 0x1d69d366a5553c7c, 0x0a26cf62f92800ba, 0x01ab12d5807e3217}},
+ {{0x9c4891e7f6d266fd, 0x0744a19b0307781b, 0x88388f1d6061e23b, 0x123ea6a3354bd50e}},
+ {{0x118d189041e32d96, 0xb9ede3c2d8315848, 0x1eab4271d83245d9, 0x4a3961e2c918a154}}},
+{{{0x71dc3be0f8e6bba0, 0xd6cef8347effe30a, 0xa992425fe13a476a, 0x2cd6bce3fb1db763}},
+ {{0x0327d644f3233f1e, 0x499a260e34fcf016, 0x83b5a716f2dab979, 0x68aceead9bd4111f}},
+ {{0x38b4c90ef3d7c210, 0x308e6e24b7ad040c, 0x3860d9f1b7e73e23, 0x595760d5b508f597}}},
+{{{0x6129bfe104aa6397, 0x8f960008a4a7fccb, 0x3f8bc0897d909458, 0x709fa43edcb291a9}},
+ {{0x882acbebfd022790, 0x89af3305c4115760, 0x65f492e37d3473f4, 0x2cb2c5df54515a2b}},
+ {{0xeb0a5d8c63fd2aca, 0xd22bc1662e694eff, 0x2723f36ef8cbb03a, 0x70f029ecf0c8131f}}},
+{{{0x461307b32eed3e33, 0xae042f33a45581e7, 0xc94449d3195f0366, 0x0b7d5d8a6c314858}},
+ {{0x2a6aafaa5e10b0b9, 0x78f0a370ef041aa9, 0x773efb77aa3ad61f, 0x44eca5a2a74bd9e1}},
+ {{0x25d448327b95d543, 0x70d38300a3340f1d, 0xde1c531c60e1c52b, 0x272224512c7de9e4}}},
+{{{0x1abc92af49c5342e, 0xffeed811b2e6fad0, 0xefa28c8dfcc84e29, 0x11b5df18a44cc543}},
+ {{0xbf7bbb8a42a975fc, 0x8c5c397796ada358, 0xe27fc76fcdedaa48, 0x19735fd7f6bc20a6}},
+ {{0xe3ab90d042c84266, 0xeb848e0f7f19547e, 0x2503a1d065a497b9, 0x0fef911191df895f}}},
+{{{0xb1507ca1ab1c6eb9, 0xbd448f3e16b687b3, 0x3455fb7f2c7a91ab, 0x7579229e2f2adec1}},
+ {{0x6ab5dcb85b1c16b7, 0x94c0fce83c7b27a5, 0xa4b11c1a735517be, 0x499238d0ba0eafaa}},
+ {{0xecf46e527aba8b57, 0x15a08c478bd1647b, 0x7af1c6a65f706fef, 0x6345fa78f03a30d5}}},
+{{{0xdf02f95f1015e7a1, 0x790ec41da9b40263, 0x4d3a0ea133ea1107, 0x54f70be7e33af8c9}},
+ {{0x93d3cbe9bdd8f0a4, 0xdb152c1bfd177302, 0x7dbddc6d7f17a875, 0x3e1a71cc8f426efe}},
+ {{0xc83ca3e390babd62, 0x80ede3670291c833, 0xc88038ccd37900c4, 0x2c5fc0231ec31fa1}}},
+{{{0xfeba911717038b4f, 0xe5123721c9deef81, 0x1c97e4e75d0d8834, 0x68afae7a23dc3bc6}},
+ {{0xc422e4d102456e65, 0x87414ac1cad47b91, 0x1592e2bba2b6ffdd, 0x75d9d2bff5c2100f}},
+ {{0x5bd9b4763626e81c, 0x89966936bca02edd, 0x0a41193d61f077b3, 0x3097a24200ce5471}}},
+{{{0x57427734c7f8b84c, 0xf141a13e01b270e9, 0x02d1adfeb4e564a6, 0x4bb23d92ce83bd48}},
+ {{0xa162e7246695c486, 0x131d633435a89607, 0x30521561a0d12a37, 0x56704bada6afb363}},
+ {{0xaf6c4aa752f912b9, 0x5e665f6cd86770c8, 0x4c35ac83a3c8cd58, 0x2b7a29c010a58a7e}}},
+{{{0xc4007f77d0c1cec3, 0x8d1020b6bac492f8, 0x32ec29d57e69daaf, 0x599408759d95fce0}},
+ {{0x33810a23bf00086e, 0xafce925ee736ff7c, 0x3d60e670e24922d4, 0x11ce9e714f96061b}},
+ {{0x219ef713d815bac1, 0xf141465d485be25c, 0x6d5447cc4e513c51, 0x174926be5ef44393}}},
+{{{0xb5deb2f9fc5bd5bb, 0x92daa72ae1d810e1, 0xafc4cfdcb72a1c59, 0x497d78813fc22a24}},
+ {{0x3ef5d41593ea022e, 0x5cbcc1a20ed0eed6, 0x8fd24ecf07382c8c, 0x6fa42ead06d8e1ad}},
+ {{0xe276824a1f73371f, 0x7f7cf01c4f5b6736, 0x7e201fe304fa46e7, 0x785a36a357808c96}}},
+{{{0x825fbdfd63014d2b, 0xc852369c6ca7578b, 0x5b2fcd285c0b5df0, 0x12ab214c58048c8f}},
+ {{0x070442985d517bc3, 0x6acd56c7ae653678, 0x00a27983985a7763, 0x5167effae512662b}},
+ {{0xbd4ea9e10f53c4b6, 0x1673dc5f8ac91a14, 0xa8f81a4e2acc1aba, 0x33a92a7924332a25}}},
+{{{0x9dd1f49927996c02, 0x0cb3b058e04d1752, 0x1f7e88967fd02c3e, 0x2f964268cb8b3eb1}},
+ {{0x7ba95ba0218f2ada, 0xcff42287330fb9ca, 0xdada496d56c6d907, 0x5380c296f4beee54}},
+ {{0x9d4f270466898d0a, 0x3d0987990aff3f7a, 0xd09ef36267daba45, 0x7761455e7b1c669c}}} \ No newline at end of file
diff --git a/ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data b/ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data
new file mode 100644
index 00000000..a31f6f2f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base_niels_smalltables.data
@@ -0,0 +1,768 @@
+{{{0x9d103905d740913e, 0xfd399f05d140beb3, 0xa5c18434688f8a09, 0x44fd2f9298f81267}},
+ {{0x2fbc93c6f58c3b85, 0xcf932dc6fb8c0e19, 0x270b4898643d42c2, 0x07cf9d3a33d4ba65}},
+ {{0xdbbd15674b6fbb59, 0x41e13f00eea2a5ea, 0xcdd49d1cc957c6fa, 0x4f0ebe1faf16ecca}}},
+{{{0x8a99a56042b4d5a8, 0x8f2b810c4e60acf6, 0xe09e236bb16e37aa, 0x6bb595a669c92555}},
+ {{0x9224e7fc933c71d7, 0x9f469d967a0ff5b5, 0x5aa69a65e1d60702, 0x590c063fa87d2e2e}},
+ {{0x6e347eaadad36802, 0xbaf3599383ee4805, 0x3bcabe10e6076826, 0x49314f0a165ed1b8}}},
+{{{0x56611fe8a4fcd265, 0x3bd353fde5c1ba7d, 0x8131f31a214bd6bd, 0x2ab91587555bda62}},
+ {{0xaf25b0a84cee9730, 0x025a8430e8864b8a, 0xc11b50029f016732, 0x7a164e1b9a80f8f4}},
+ {{0x9bf211f4f1674834, 0xb84e6b17f62df895, 0xd7de6f075b722a4e, 0x549a04b963bb2a21}}},
+{{{0x95fe050a056818bf, 0x327e89715660faa9, 0xc3e8e3cd06a05073, 0x27933f4c7445a49a}},
+ {{0x287351b98efc099f, 0x6765c6f47dfd2538, 0xca348d3dfb0a9265, 0x680e910321e58727}},
+ {{0xbf1e45ece51426b0, 0xe32bc63d6dba0f94, 0xe42974d58cf852c0, 0x44f079b1b0e64c18}}},
+{{{0x7f9182c3a447d6ba, 0xd50014d14b2729b7, 0xe33cf11cb864a087, 0x154a7e73eb1b55f3}},
+ {{0xa212bc4408a5bb33, 0x8d5048c3c75eed02, 0xdd1beb0c5abfec44, 0x2945ccf146e206eb}},
+ {{0xc832a179e7d003b3, 0x5f729d0a00124d7e, 0x62c1d4a10e6d8ff3, 0x68b8ac5938b27a98}}},
+{{{0x499806b67b7d8ca4, 0x575be28427d22739, 0xbb085ce7204553b9, 0x38b64c41ae417884}},
+ {{0x3a0ceeeb77157131, 0x9b27158900c8af88, 0x8065b668da59a736, 0x51e57bb6a2cc38bd}},
+ {{0x8f9dad91689de3a4, 0x175f2428f8fb9137, 0x050ab5329fcfb988, 0x7865dfa21354c09f}}},
+{{{0xba6f2c9aaa3221b1, 0x6ca021533bba23a7, 0x9dea764f92192c3a, 0x1d6edd5d2e5317e0}},
+ {{0x6b1a5cd0944ea3bf, 0x7470353ab39dc0d2, 0x71b2528228542e49, 0x461bea69283c927e}},
+ {{0x217a8aacab0fda36, 0xa528c6543d3549c8, 0x37d05b8b13ab7568, 0x233cef623a2cbc37}}},
+{{{0xe2a75dedf39234d9, 0x963d7680e1b558f9, 0x2c2741ac6e3c23fb, 0x3a9024a1320e01c3}},
+ {{0x59b7596604dd3e8f, 0x6cb30377e288702c, 0xb1339c665ed9c323, 0x0915e76061bce52f}},
+ {{0xdf7de835a834a37e, 0x8be19cda689857ea, 0x2c1185367167b326, 0x589eb3d9dbefd5c2}}},
+{{{0xed5b635449aa515e, 0xa865c49f0bc6823a, 0x850c1fe95b42d1c4, 0x30d76d6f03d315b9}},
+ {{0x2eccdd0e632f9c1d, 0x51d0b69676893115, 0x52dfb76ba8637a58, 0x6dd37d49a00eef39}},
+ {{0x6c4444172106e4c7, 0xfb53d680928d7f69, 0xb4739ea4694d3f26, 0x10c697112e864bb0}}},
+{{{0x6493c4277dbe5fde, 0x265d4fad19ad7ea2, 0x0e00dfc846304590, 0x25e61cabed66fe09}},
+ {{0x0ca62aa08358c805, 0x6a3d4ae37a204247, 0x7464d3a63b11eddc, 0x03bf9baf550806ef}},
+ {{0x3f13e128cc586604, 0x6f5873ecb459747e, 0xa0b63dedcc1268f5, 0x566d78634586e22c}}},
+{{{0x1637a49f9cc10834, 0xbc8e56d5a89bc451, 0x1cb5ec0f7f7fd2db, 0x33975bca5ecc35d9}},
+ {{0xa1054285c65a2fd0, 0x6c64112af31667c3, 0x680ae240731aee58, 0x14fba5f34793b22a}},
+ {{0x3cd746166985f7d4, 0x593e5e84c9c80057, 0x2fc3f2b67b61131e, 0x14829cea83fc526c}}},
+{{{0xff437b8497dd95c2, 0x6c744e30aa4eb5a7, 0x9e0c5d613c85e88b, 0x2fd9c71e5f758173}},
+ {{0x21e70b2f4e71ecb8, 0xe656ddb940a477e3, 0xbf6556cece1d4f80, 0x05fc3bc4535d7b7e}},
+ {{0x24b8b3ae52afdedd, 0x3495638ced3b30cf, 0x33a4bc83a9be8195, 0x373767475c651f04}}},
+{{{0x2fba99fd40d1add9, 0xb307166f96f4d027, 0x4363f05215f03bae, 0x1fbea56c3b18f999}},
+ {{0x634095cb14246590, 0xef12144016c15535, 0x9e38140c8910bc60, 0x6bf5905730907c8c}},
+ {{0x0fa778f1e1415b8a, 0x06409ff7bac3a77e, 0x6f52d7b89aa29a50, 0x02521cf67a635a56}}},
+{{{0x513fee0b0a9d5294, 0x8f98e75c0fdf5a66, 0xd4618688bfe107ce, 0x3fa00a7e71382ced}},
+ {{0xb1146720772f5ee4, 0xe8f894b196079ace, 0x4af8224d00ac824a, 0x001753d9f7cd6cc4}},
+ {{0x3c69232d963ddb34, 0x1dde87dab4973858, 0xaad7d1f9a091f285, 0x12b5fe2fa048edb6}}},
+{{{0x71f0fbc496fce34d, 0x73b9826badf35bed, 0xd2047261ff28c561, 0x749b76f96fb1206f}},
+ {{0xdf2b7c26ad6f1e92, 0x4b66d323504b8913, 0x8c409dc0751c8bc3, 0x6f7e93c20796c7b8}},
+ {{0x1f5af604aea6ae05, 0xc12351f1bee49c99, 0x61a808b5eeff6b66, 0x0fcec10f01e02151}}},
+{{{0x644d58a649fe1e44, 0x21fcaea231ad777e, 0x02441c5a887fd0d2, 0x4901aa7183c511f3}},
+ {{0x3df2d29dc4244e45, 0x2b020e7493d8de0a, 0x6cc8067e820c214d, 0x413779166feab90a}},
+ {{0x08b1b7548c1af8f0, 0xce0f7a7c246299b4, 0xf760b0f91e06d939, 0x41bb887b726d1213}}},
+{{{0x40e87d44744346be, 0x1d48dad415b52b25, 0x7c3a8a18a13b603e, 0x4eb728c12fcdbdf7}},
+ {{0x7e234c597c6691ae, 0x64889d3d0a85b4c8, 0xdae2c90c354afae7, 0x0a871e070c6a9e1d}},
+ {{0x3301b5994bbc8989, 0x736bae3a5bdd4260, 0x0d61ade219d59e3c, 0x3ee7300f2685d464}}},
+{{{0xf5d255e49e7dd6b7, 0x8016115c610b1eac, 0x3c99975d92e187ca, 0x13815762979125c2}},
+ {{0x43fa7947841e7518, 0xe5c6fa59639c46d7, 0xa1065e1de3052b74, 0x7d47c6a2cfb89030}},
+ {{0x3fdad0148ef0d6e0, 0x9d3e749a91546f3c, 0x71ec621026bb8157, 0x148cf58d34c9ec80}}},
+{{{0x46a492f67934f027, 0x469984bef6840aa9, 0x5ca1bc2a89611854, 0x3ff2fa1ebd5dbbd4}},
+ {{0xe2572f7d9ae4756d, 0x56c345bb88f3487f, 0x9fd10b6d6960a88d, 0x278febad4eaea1b9}},
+ {{0xb1aa681f8c933966, 0x8c21949c20290c98, 0x39115291219d3c52, 0x4104dd02fe9c677b}}},
+{{{0x72b2bf5e1124422a, 0xa1fa0c3398a33ab5, 0x94cb6101fa52b666, 0x2c863b00afaf53d5}},
+ {{0x81214e06db096ab8, 0x21a8b6c90ce44f35, 0x6524c12a409e2af5, 0x0165b5a48efca481}},
+ {{0xf190a474a0846a76, 0x12eff984cd2f7cc0, 0x695e290658aa2b8f, 0x591b67d9bffec8b8}}},
+{{{0x312f0d1c80b49bfa, 0x5979515eabf3ec8a, 0x727033c09ef01c88, 0x3de02ec7ca8f7bcb}},
+ {{0x99b9b3719f18b55d, 0xe465e5faa18c641e, 0x61081136c29f05ed, 0x489b4f867030128b}},
+ {{0xd232102d3aeb92ef, 0xe16253b46116a861, 0x3d7eabe7190baa24, 0x49f5fbba496cbebf}}},
+{{{0x30949a108a5bcfd4, 0xdc40dd70bc6473eb, 0x92c294c1307c0d1c, 0x5604a86dcbfa6e74}},
+ {{0x155d628c1e9c572e, 0x8a4d86acc5884741, 0x91a352f6515763eb, 0x06a1a6c28867515b}},
+ {{0x7288d1d47c1764b6, 0x72541140e0418b51, 0x9f031a6018acf6d1, 0x20989e89fe2742c6}}},
+{{{0x499777fd3a2dcc7f, 0x32857c2ca54fd892, 0xa279d864d207e3a0, 0x0403ed1d0ca67e29}},
+ {{0x1674278b85eaec2e, 0x5621dc077acb2bdf, 0x640a4c1661cbf45a, 0x730b9950f70595d3}},
+ {{0xc94b2d35874ec552, 0xc5e6c8cf98246f8d, 0xf7cb46fa16c035ce, 0x5bd7454308303dcc}}},
+{{{0x7f9ad19528b24cc2, 0x7f6b54656335c181, 0x66b8b66e4fc07236, 0x133a78007380ad83}},
+ {{0x85c4932115e7792a, 0xc64c89a2bdcdddc9, 0x9d1e3da8ada3d762, 0x5bb7db123067f82c}},
+ {{0x0961f467c6ca62be, 0x04ec21d6211952ee, 0x182360779bd54770, 0x740dca6d58f0e0d2}}},
+{{{0xdf48ee0752cfce4e, 0xc3fffaf306ec08b7, 0x05710b2ab95459c4, 0x161d25fa963ea38d}},
+ {{0x231a8c570478433c, 0xb7b5270ec281439d, 0xdbaa99eae3d9079f, 0x2c03f5256c2b03d9}},
+ {{0x790f18757b53a47d, 0x307b0130cf0c5879, 0x31903d77257ef7f9, 0x699468bdbd96bbaf}}},
+{{{0xbd1f2f46f4dafecf, 0x7cef0114a47fd6f7, 0xd31ffdda4a47b37f, 0x525219a473905785}},
+ {{0xd8dd3de66aa91948, 0x485064c22fc0d2cc, 0x9b48246634fdea2f, 0x293e1c4e6c4a2e3a}},
+ {{0x376e134b925112e1, 0x703778b5dca15da0, 0xb04589af461c3111, 0x5b605c447f032823}}},
+{{{0xb965805920c47c89, 0xe7f0100c923b8fcc, 0x0001256502e2ef77, 0x24a76dcea8aeb3ee}},
+ {{0x3be9fec6f0e7f04c, 0x866a579e75e34962, 0x5542ef161e1de61a, 0x2f12fef4cc5abdd5}},
+ {{0x0a4522b2dfc0c740, 0x10d06e7f40c9a407, 0xc6cf144178cff668, 0x5e607b2518a43790}}},
+{{{0x58b31d8f6cdf1818, 0x35cfa74fc36258a2, 0xe1b3ff4f66e61d6e, 0x5067acab6ccdd5f7}},
+ {{0xa02c431ca596cf14, 0xe3c42d40aed3e400, 0xd24526802e0f26db, 0x201f33139e457068}},
+ {{0xfd527f6b08039d51, 0x18b14964017c0006, 0xd5220eb02e25a4a8, 0x397cba8862460375}}},
+{{{0x30c13093f05959b2, 0xe23aa18de9a97976, 0x222fd491721d5e26, 0x2339d320766e6c3a}},
+ {{0x7815c3fbc81379e7, 0xa6619420dde12af1, 0xffa9c0f885a8fdd5, 0x771b4022c1e1c252}},
+ {{0xd87dd986513a2fa7, 0xf5ac9b71f9d4cf08, 0xd06bc31b1ea283b3, 0x331a189219971a76}}},
+{{{0xf5166f45fb4f80c6, 0x9c36c7de61c775cf, 0xe3d4e81b9041d91c, 0x31167c6b83bdfe21}},
+ {{0x26512f3a9d7572af, 0x5bcbe28868074a9e, 0x84edc1c11180f7c4, 0x1ac9619ff649a67b}},
+ {{0xf22b3842524b1068, 0x5068343bee9ce987, 0xfc9d71844a6250c8, 0x612436341f08b111}}},
+{{{0xd99d41db874e898d, 0x09fea5f16c07dc20, 0x793d2c67d00f9bbc, 0x46ebe2309e5eff40}},
+ {{0x8b6349e31a2d2638, 0x9ddfb7009bd3fd35, 0x7f8bf1b8a3a06ba4, 0x1522aa3178d90445}},
+ {{0x2c382f5369614938, 0xdafe409ab72d6d10, 0xe8c83391b646f227, 0x45fe70f50524306c}}},
+{{{0xda4875a6960c0b8c, 0x5b68d076ef0e2f20, 0x07fb51cf3d0b8fd4, 0x428d1623a0e392d4}},
+ {{0x62f24920c8951491, 0x05f007c83f630ca2, 0x6fbb45d2f5c9d4b8, 0x16619f6db57a2245}},
+ {{0x084f4a4401a308fd, 0xa82219c376a5caac, 0xdeb8de4643d1bc7d, 0x1d81592d60bd38c6}}},
+{{{0x61368756a60dac5f, 0x17e02f6aebabdc57, 0x7f193f2d4cce0f7d, 0x20234a7789ecdcf0}},
+ {{0x8765b69f7b85c5e8, 0x6ff0678bd168bab2, 0x3a70e77c1d330f9b, 0x3a5f6d51b0af8e7c}},
+ {{0x76d20db67178b252, 0x071c34f9d51ed160, 0xf62a4a20b3e41170, 0x7cd682353cffe366}}},
+{{{0x0be1a45bd887fab6, 0x2a846a32ba403b6e, 0xd9921012e96e6000, 0x2838c8863bdc0943}},
+ {{0xa665cd6068acf4f3, 0x42d92d183cd7e3d3, 0x5759389d336025d9, 0x3ef0253b2b2cd8ff}},
+ {{0xd16bb0cf4a465030, 0xfa496b4115c577ab, 0x82cfae8af4ab419d, 0x21dcb8a606a82812}}},
+{{{0x5c6004468c9d9fc8, 0x2540096ed42aa3cb, 0x125b4d4c12ee2f9c, 0x0bc3d08194a31dab}},
+ {{0x9a8d00fabe7731ba, 0x8203607e629e1889, 0xb2cc023743f3d97f, 0x5d840dbf6c6f678b}},
+ {{0x706e380d309fe18b, 0x6eb02da6b9e165c7, 0x57bbba997dae20ab, 0x3a4276232ac196dd}}},
+{{{0x4b42432c8a7084fa, 0x898a19e3dfb9e545, 0xbe9f00219c58e45d, 0x1ff177cea16debd1}},
+ {{0x3bf8c172db447ecb, 0x5fcfc41fc6282dbd, 0x80acffc075aa15fe, 0x0770c9e824e1a9f9}},
+ {{0xcf61d99a45b5b5fd, 0x860984e91b3a7924, 0xe7300919303e3e89, 0x39f264fd41500b1e}}},
+{{{0xa7ad3417dbe7e29c, 0xbd94376a2b9c139c, 0xa0e91b8e93597ba9, 0x1712d73468889840}},
+ {{0xd19b4aabfe097be1, 0xa46dfce1dfe01929, 0xc3c908942ca6f1ff, 0x65c621272c35f14e}},
+ {{0xe72b89f8ce3193dd, 0x4d103356a125c0bb, 0x0419a93d2e1cfe83, 0x22f9800ab19ce272}}},
+{{{0x605a368a3e9ef8cb, 0xe3e9c022a5504715, 0x553d48b05f24248f, 0x13f416cd647626e5}},
+ {{0x42029fdd9a6efdac, 0xb912cebe34a54941, 0x640f64b987bdf37b, 0x4171a4d38598cab4}},
+ {{0xfa2758aa99c94c8c, 0x23006f6fb000b807, 0xfbd291ddadda5392, 0x508214fa574bd1ab}}},
+{{{0xc20269153ed6fe4b, 0xa65a6739511d77c4, 0xcbde26462c14af94, 0x22f960ec6faba74b}},
+ {{0x461a15bb53d003d6, 0xb2102888bcf3c965, 0x27c576756c683a5a, 0x3a7758a4c86cb447}},
+ {{0x548111f693ae5076, 0x1dae21df1dfd54a6, 0x12248c90f3115e65, 0x5d9fd15f8de7f494}}},
+{{{0x031408d36d63727f, 0x6a379aefd7c7b533, 0xa9e18fc5ccaee24b, 0x332f35914f8fbed3}},
+ {{0x3f244d2aeed7521e, 0x8e3a9028432e9615, 0xe164ba772e9c16d4, 0x3bc187fa47eb98d8}},
+ {{0x6d470115ea86c20c, 0x998ab7cb6c46d125, 0xd77832b53a660188, 0x450d81ce906fba03}}},
+{{{0x6e7bb6a1a6205275, 0xaa4f21d7413c8e83, 0x6f56d155e88f5cb2, 0x2de25d4ba6345be1}},
+ {{0xd074d8961cae743f, 0xf86d18f5ee1c63ed, 0x97bdc55be7f4ed29, 0x4cbad279663ab108}},
+ {{0x80d19024a0d71fcd, 0xc525c20afb288af8, 0xb1a3974b5f3a6419, 0x7d7fbcefe2007233}}},
+{{{0xfaef1e6a266b2801, 0x866c68c4d5739f16, 0xf68a2fbc1b03762c, 0x5975435e87b75a8d}},
+ {{0xcd7c5dc5f3c29094, 0xc781a29a2a9105ab, 0x80c61d36421c3058, 0x4f9cd196dcd8d4d7}},
+ {{0x199297d86a7b3768, 0xd0d058241ad17a63, 0xba029cad5c1c0c17, 0x7ccdd084387a0307}}},
+{{{0xdca6422c6d260417, 0xae153d50948240bd, 0xa9c0c1b4fb68c677, 0x428bd0ed61d0cf53}},
+ {{0x9b0c84186760cc93, 0xcdae007a1ab32a99, 0xa88dec86620bda18, 0x3593ca848190ca44}},
+ {{0x9213189a5e849aa7, 0xd4d8c33565d8facd, 0x8c52545b53fdbbd1, 0x27398308da2d63e6}}},
+{{{0x42c38d28435ed413, 0xbd50f3603278ccc9, 0xbb07ab1a79da03ef, 0x269597aebe8c3355}},
+ {{0xb9a10e4c0a702453, 0x0fa25866d57d1bde, 0xffb9d9b5cd27daf7, 0x572c2945492c33fd}},
+ {{0xc77fc745d6cd30be, 0xe4dfe8d3e3baaefb, 0xa22c8830aa5dda0c, 0x7f985498c05bca80}}},
+{{{0x3849ce889f0be117, 0x8005ad1b7b54a288, 0x3da3c39f23fc921c, 0x76c2ec470a31f304}},
+ {{0xd35615520fbf6363, 0x08045a45cf4dfba6, 0xeec24fbc873fa0c2, 0x30f2653cd69b12e7}},
+ {{0x8a08c938aac10c85, 0x46179b60db276bcb, 0xa920c01e0e6fac70, 0x2f1273f1596473da}}},
+{{{0x4739fc7c8ae01e11, 0xfd5274904a6aab9f, 0x41d98a8287728f2e, 0x5d9e572ad85b69f2}},
+ {{0x30488bd755a70bc0, 0x06d6b5a4f1d442e7, 0xead1a69ebc596162, 0x38ac1997edc5f784}},
+ {{0x0666b517a751b13b, 0x747d06867e9b858c, 0xacacc011454dde49, 0x22dfcd9cbfe9e69c}}},
+{{{0x8ddbd2e0c30d0cd9, 0xad8e665facbb4333, 0x8f6b258c322a961f, 0x6b2916c05448c1c7}},
+ {{0x56ec59b4103be0a1, 0x2ee3baecd259f969, 0x797cb29413f5cd32, 0x0fe9877824cde472}},
+ {{0x7edb34d10aba913b, 0x4ea3cd822e6dac0e, 0x66083dff6578f815, 0x4c303f307ff00a17}}},
+{{{0xd30a3bd617b28c85, 0xc5d377b739773bea, 0xc6c6e78c1e6a5cbf, 0x0d61b8f78b2ab7c4}},
+ {{0x29fc03580dd94500, 0xecd27aa46fbbec93, 0x130a155fc2e2a7f8, 0x416b151ab706a1d5}},
+ {{0x56a8d7efe9c136b0, 0xbd07e5cd58e44b20, 0xafe62fda1b57e0ab, 0x191a2af74277e8d2}}},
+{{{0xce16f74bc53c1431, 0x2b9725ce2072edde, 0xb8b9c36fb5b23ee7, 0x7e2e0e450b5cc908}},
+ {{0x9fe62b434f460efb, 0xded303d4a63607d6, 0xf052210eb7a0da24, 0x237e7dbe00545b93}},
+ {{0x013575ed6701b430, 0x231094e69f0bfd10, 0x75320f1583e47f22, 0x71afa699b11155e3}}},
+{{{0x65ce6f9b3953b61d, 0xc65839eaafa141e6, 0x0f435ffda9f759fe, 0x021142e9c2b1c28e}},
+ {{0xea423c1c473b50d6, 0x51e87a1f3b38ef10, 0x9b84bf5fb2c9be95, 0x00731fbc78f89a1c}},
+ {{0xe430c71848f81880, 0xbf960c225ecec119, 0xb6dae0836bba15e3, 0x4c4d6f3347e15808}}},
+{{{0x18f7eccfc17d1fc9, 0x6c75f5a651403c14, 0xdbde712bf7ee0cdf, 0x193fddaaa7e47a22}},
+ {{0x2f0cddfc988f1970, 0x6b916227b0b9f51b, 0x6ec7b6c4779176be, 0x38bf9500a88f9fa8}},
+ {{0x1fd2c93c37e8876f, 0xa2f61e5a18d1462c, 0x5080f58239241276, 0x6a6fb99ebf0d4969}}},
+{{{0x6a46c1bb560855eb, 0x2416bb38f893f09d, 0xd71d11378f71acc1, 0x75f76914a31896ea}},
+ {{0xeeb122b5b6e423c6, 0x939d7010f286ff8e, 0x90a92a831dcf5d8c, 0x136fda9f42c5eb10}},
+ {{0xf94cdfb1a305bdd1, 0x0f364b9d9ff82c08, 0x2a87d8a5c3bb588a, 0x022183510be8dcba}}},
+{{{0x4af766385ead2d14, 0xa08ed880ca7c5830, 0x0d13a6e610211e3d, 0x6a071ce17b806c03}},
+ {{0x9d5a710143307a7f, 0xb063de9ec47da45f, 0x22bbfe52be927ad3, 0x1387c441fd40426c}},
+ {{0xb5d3c3d187978af8, 0x722b5a3d7f0e4413, 0x0d7b4848bb477ca0, 0x3171b26aaf1edc92}}},
+{{{0xa92f319097564ca8, 0xff7bb84c2275e119, 0x4f55fe37a4875150, 0x221fd4873cf0835a}},
+ {{0xa60db7d8b28a47d1, 0xa6bf14d61770a4f1, 0xd4a1f89353ddbd58, 0x6c514a63344243e9}},
+ {{0x2322204f3a156341, 0xfb73e0e9ba0a032d, 0xfce0dd4c410f030e, 0x48daa596fb924aaa}}},
+{{{0x6eca8e665ca59cc7, 0xa847254b2e38aca0, 0x31afc708d21e17ce, 0x676dd6fccad84af7}},
+ {{0x14f61d5dc84c9793, 0x9941f9e3ef418206, 0xcdf5b88f346277ac, 0x58c837fa0e8a79a9}},
+ {{0x0cf9688596fc9058, 0x1ddcbbf37b56a01b, 0xdcc2e77d4935d66a, 0x1c4f73f2c6a57f0a}}},
+{{{0x0e7a4fbd305fa0bb, 0x829d4ce054c663ad, 0xf421c3832fe33848, 0x795ac80d1bf64c42}},
+ {{0xb36e706efc7c3484, 0x73dfc9b4c3c1cf61, 0xeb1d79c9781cc7e5, 0x70459adb7daf675c}},
+ {{0x1b91db4991b42bb3, 0x572696234b02dcca, 0x9fdf9ee51f8c78dc, 0x5fe162848ce21fd3}}},
+{{{0x4e59214fe194961a, 0x49be7dc70d71cd4f, 0x9300cfd23b50f22d, 0x4789d446fc917232}},
+ {{0x2879852d5d7cb208, 0xb8dedd70687df2e7, 0xdc0bffab21687891, 0x2b44c043677daa35}},
+ {{0x1a1c87ab074eb78e, 0xfac6d18e99daf467, 0x3eacbbcd484f9067, 0x60c52eef2bb9a4e4}}},
+{{{0x0b5d89bc3bfd8bf1, 0xb06b9237c9f3551a, 0x0e4c16b0d53028f5, 0x10bc9c312ccfcaab}},
+ {{0x702bc5c27cae6d11, 0x44c7699b54a48cab, 0xefbc4056ba492eb2, 0x70d77248d9b6676d}},
+ {{0xaa8ae84b3ec2a05b, 0x98699ef4ed1781e0, 0x794513e4708e85d1, 0x63755bd3a976f413}}},
+{{{0xb55fa03e2ad10853, 0x356f75909ee63569, 0x9ff9f1fdbe69b890, 0x0d8cc1c48bc16f84}},
+ {{0x3dc7101897f1acb7, 0x5dda7d5ec165bbd8, 0x508e5b9c0fa1020f, 0x2763751737c52a56}},
+ {{0x029402d36eb419a9, 0xf0b44e7e77b460a5, 0xcfa86230d43c4956, 0x70c2dd8a7ad166e7}}},
+{{{0x656194509f6fec0e, 0xee2e7ea946c6518d, 0x9733c1f367e09b5c, 0x2e0fac6363948495}},
+ {{0x91d4967db8ed7e13, 0x74252f0ad776817a, 0xe40982e00d852564, 0x32b8613816a53ce5}},
+ {{0x79e7f7bee448cd64, 0x6ac83a67087886d0, 0xf89fd4d9a0e4db2e, 0x4179215c735a4f41}}},
+{{{0x8c7094e7d7dced2a, 0x97fb8ac347d39c70, 0xe13be033a906d902, 0x700344a30cd99d76}},
+ {{0xe4ae33b9286bcd34, 0xb7ef7eb6559dd6dc, 0x278b141fb3d38e1f, 0x31fa85662241c286}},
+ {{0xaf826c422e3622f4, 0xc12029879833502d, 0x9bc1b7e12b389123, 0x24bb2312a9952489}}},
+{{{0xb1a8ed1732de67c3, 0x3cb49418461b4948, 0x8ebd434376cfbcd2, 0x0fee3e871e188008}},
+ {{0x41f80c2af5f85c6b, 0x687284c304fa6794, 0x8945df99a3ba1bad, 0x0d1d2af9ffeb5d16}},
+ {{0xa9da8aa132621edf, 0x30b822a159226579, 0x4004197ba79ac193, 0x16acd79718531d76}}},
+{{{0x72df72af2d9b1d3d, 0x63462a36a432245a, 0x3ecea07916b39637, 0x123e0ef6b9302309}},
+ {{0xc959c6c57887b6ad, 0x94e19ead5f90feba, 0x16e24e62a342f504, 0x164ed34b18161700}},
+ {{0x487ed94c192fe69a, 0x61ae2cea3a911513, 0x877bf6d3b9a4de27, 0x78da0fc61073f3eb}}},
+{{{0x5bf15d28e52bc66a, 0x2c47e31870f01a8e, 0x2419afbc06c28bdd, 0x2d25deeb256b173a}},
+ {{0xa29f80f1680c3a94, 0x71f77e151ae9e7e6, 0x1100f15848017973, 0x054aa4b316b38ddd}},
+ {{0xdfc8468d19267cb8, 0x0b28789c66e54daf, 0x2aeb1d2a666eec17, 0x134610a6ab7da760}}},
+{{{0x51138ec78df6b0fe, 0x5397da89e575f51b, 0x09207a1d717af1b9, 0x2102fdba2b20d650}},
+ {{0xcd2a65e777d1f515, 0x548991878faa60f1, 0xb1b73bbcdabc06e5, 0x654878cba97cc9fb}},
+ {{0x969ee405055ce6a1, 0x36bca7681251ad29, 0x3a1af517aa7da415, 0x0ad725db29ecb2ba}}},
+{{{0xdc4267b1834e2457, 0xb67544b570ce1bc5, 0x1af07a0bf7d15ed7, 0x4aefcffb71a03650}},
+ {{0xfec7bc0c9b056f85, 0x537d5268e7f5ffd7, 0x77afc6624312aefa, 0x4f675f5302399fd9}},
+ {{0xc32d36360415171e, 0xcd2bef118998483b, 0x870a6eadd0945110, 0x0bccbb72a2a86561}}},
+{{{0x185e962feab1a9c8, 0x86e7e63565147dcd, 0xb092e031bb5b6df2, 0x4024f0ab59d6b73e}},
+ {{0x186d5e4c50fe1296, 0xe0397b82fee89f7e, 0x3bc7f6c5507031b0, 0x6678fd69108f37c2}},
+ {{0x1586fa31636863c2, 0x07f68c48572d33f2, 0x4f73cc9f789eaefc, 0x2d42e2108ead4701}}},
+{{{0x97f5131594dfd29b, 0x6155985d313f4c6a, 0xeba13f0708455010, 0x676b2608b8d2d322}},
+ {{0x21717b0d0f537593, 0x914e690b131e064c, 0x1bb687ae752ae09f, 0x420bf3a79b423c6e}},
+ {{0x8138ba651c5b2b47, 0x8671b6ec311b1b80, 0x7bff0cb1bc3135b0, 0x745d2ffa9c0cf1e0}}},
+{{{0xbf525a1e2bc9c8bd, 0xea5b260826479d81, 0xd511c70edf0155db, 0x1ae23ceb960cf5d0}},
+ {{0x6036df5721d34e6a, 0xb1db8827997bb3d0, 0xd3c209c3c8756afa, 0x06e15be54c1dc839}},
+ {{0x5b725d871932994a, 0x32351cb5ceb1dab0, 0x7dc41549dab7ca05, 0x58ded861278ec1f7}}},
+{{{0xd8173793f266c55c, 0xc8c976c5cc454e49, 0x5ce382f8bc26c3a8, 0x2ff39de85485f6f9}},
+ {{0x2dfb5ba8b6c2c9a8, 0x48eeef8ef52c598c, 0x33809107f12d1573, 0x08ba696b531d5bd8}},
+ {{0x77ed3eeec3efc57a, 0x04e05517d4ff4811, 0xea3d7a3ff1a671cb, 0x120633b4947cfe54}}},
+{{{0x0b94987891610042, 0x4ee7b13cecebfae8, 0x70be739594f0a4c0, 0x35d30a99b4d59185}},
+ {{0x82bd31474912100a, 0xde237b6d7e6fbe06, 0xe11e761911ea79c6, 0x07433be3cb393bde}},
+ {{0xff7944c05ce997f4, 0x575d3de4b05c51a3, 0x583381fd5a76847c, 0x2d873ede7af6da9f}}},
+{{{0x157a316443373409, 0xfab8b7eef4aa81d9, 0xb093fee6f5a64806, 0x2e773654707fa7b6}},
+ {{0xaa6202e14e5df981, 0xa20d59175015e1f5, 0x18a275d3bae21d6c, 0x0543618a01600253}},
+ {{0x0deabdf4974c23c1, 0xaa6f0a259dce4693, 0x04202cb8a29aba2c, 0x4b1443362d07960d}}},
+{{{0xccc4b7c7b66e1f7a, 0x44157e25f50c2f7e, 0x3ef06dfc713eaf1c, 0x582f446752da63f7}},
+ {{0x967c54e91c529ccb, 0x30f6269264c635fb, 0x2747aff478121965, 0x17038418eaf66f5c}},
+ {{0xc6317bd320324ce4, 0xa81042e8a4488bc4, 0xb21ef18b4e5a1364, 0x0c2a1c4bcda28dc9}}},
+{{{0xd24dc7d06f1f0447, 0xb2269e3edb87c059, 0xd15b0272fbb2d28f, 0x7c558bd1c6f64877}},
+ {{0xedc4814869bd6945, 0x0d6d907dbe1c8d22, 0xc63bd212d55cc5ab, 0x5a6a9b30a314dc83}},
+ {{0xd0ec1524d396463d, 0x12bb628ac35a24f0, 0xa50c3a791cbc5fa4, 0x0404a5ca0afbafc3}}},
+{{{0x8c1f40070aa743d6, 0xccbad0cb5b265ee8, 0x574b046b668fd2de, 0x46395bfdcadd9633}},
+ {{0x62bc9e1b2a416fd1, 0xb5c6f728e350598b, 0x04343fd83d5d6967, 0x39527516e7f8ee98}},
+ {{0x117fdb2d1a5d9a9c, 0x9c7745bcd1005c2a, 0xefd4bef154d56fea, 0x76579a29e822d016}}},
+{{{0x45b68e7e49c02a17, 0x23cd51a2bca9a37f, 0x3ed65f11ec224c1b, 0x43a384dc9e05bdb1}},
+ {{0x333cb51352b434f2, 0xd832284993de80e1, 0xb5512887750d35ce, 0x02c514bb2a2777c1}},
+ {{0x684bd5da8bf1b645, 0xfb8bd37ef6b54b53, 0x313916d7a9b0d253, 0x1160920961548059}}},
+{{{0xb44d166929dacfaa, 0xda529f4c8413598f, 0xe9ef63ca453d5559, 0x351e125bc5698e0b}},
+ {{0x7a385616369b4dcd, 0x75c02ca7655c3563, 0x7dc21bf9d4f18021, 0x2f637d7491e6e042}},
+ {{0xd4b49b461af67bbe, 0xd603037ac8ab8961, 0x71dee19ff9a699fb, 0x7f182d06e7ce2a9a}}},
+{{{0x7a7c8e64ab0168ec, 0xcb5a4a5515edc543, 0x095519d347cd0eda, 0x67d4ac8c343e93b0}},
+ {{0x09454b728e217522, 0xaa58e8f4d484b8d8, 0xd358254d7f46903c, 0x44acc043241c5217}},
+ {{0x1c7d6bbb4f7a5777, 0x8b35fed4918313e1, 0x4adca1c6c96b4684, 0x556d1c8312ad71bd}}},
+{{{0x17ef40e30c8d3982, 0x31f7073e15a3fa34, 0x4f21f3cb0773646e, 0x746c6c6d1d824eff}},
+ {{0x81f06756b11be821, 0x0faff82310a3f3dd, 0xf8b2d0556a99465d, 0x097abe38cc8c7f05}},
+ {{0x0c49c9877ea52da4, 0x4c4369559bdc1d43, 0x022c3809f7ccebd2, 0x577e14a34bee84bd}}},
+{{{0xf0e268ac61a73b0a, 0xf2fafa103791a5f5, 0xc1e13e826b6d00e9, 0x60fa7ee96fd78f42}},
+ {{0x94fecebebd4dd72b, 0xf46a4fda060f2211, 0x124a5977c0c8d1ff, 0x705304b8fb009295}},
+ {{0xb63d1d354d296ec6, 0xf3c3053e5fad31d8, 0x670b958cb4bd42ec, 0x21398e0ca16353fd}}},
+{{{0x89f5058a382b33f3, 0x5ae2ba0bad48c0b4, 0x8f93b503a53db36e, 0x5aa3ed9d95a232e6}},
+ {{0x2798aaf9b4b75601, 0x5eac72135c8dad72, 0xd2ceaa6161b7a023, 0x1bbfb284e98f7d4e}},
+ {{0x656777e9c7d96561, 0xcb2b125472c78036, 0x65053299d9506eee, 0x4a07e14e5e8957cc}}},
+{{{0x4ee412cb980df999, 0xa315d76f3c6ec771, 0xbba5edde925c77fd, 0x3f0bac391d313402}},
+ {{0x240b58cdc477a49b, 0xfd38dade6447f017, 0x19928d32a7c86aad, 0x50af7aed84afa081}},
+ {{0x6e4fde0115f65be5, 0x29982621216109b2, 0x780205810badd6d9, 0x1921a316baebd006}}},
+{{{0x89422f7edfb870fc, 0x2c296beb4f76b3bd, 0x0738f1d436c24df7, 0x6458df41e273aeb0}},
+ {{0xd75aad9ad9f3c18b, 0x566a0eef60b1c19c, 0x3e9a0bac255c0ed9, 0x7b049deca062c7f5}},
+ {{0xdccbe37a35444483, 0x758879330fedbe93, 0x786004c312c5dd87, 0x6093dccbc2950e64}}},
+{{{0x1ff39a8585e0706d, 0x36d0a5d8b3e73933, 0x43b9f2e1718f453b, 0x57d1ea084827a97c}},
+ {{0x6bdeeebe6084034b, 0x3199c2b6780fb854, 0x973376abb62d0695, 0x6e3180c98b647d90}},
+ {{0xee7ab6e7a128b071, 0xa4c1596d93a88baa, 0xf7b4de82b2216130, 0x363e999ddd97bd18}}},
+{{{0x96a843c135ee1fc4, 0x976eb35508e4c8cf, 0xb42f6801b58cd330, 0x48ee9b78693a052b}},
+ {{0x2f1848dce24baec6, 0x769b7255babcaf60, 0x90cb3c6e3cefe931, 0x231f979bc6f9b355}},
+ {{0x5c31de4bcc2af3c6, 0xb04bb030fe208d1f, 0xb78d7009c14fb466, 0x079bfa9b08792413}}},
+{{{0xe3903a51da300df4, 0x843964233da95ab0, 0xed3cf12d0b356480, 0x038c77f684817194}},
+ {{0xf3c9ed80a2d54245, 0x0aa08b7877f63952, 0xd76dac63d1085475, 0x1ef4fb159470636b}},
+ {{0x854e5ee65b167bec, 0x59590a4296d0cdc2, 0x72b2df3498102199, 0x575ee92a4a0bff56}}},
+{{{0xd4c080908a182fcf, 0x30e170c299489dbd, 0x05babd5752f733de, 0x43d4e7112cd3fd00}},
+ {{0x5d46bc450aa4d801, 0xc3af1227a533b9d8, 0x389e3b262b8906c2, 0x200a1e7e382f581b}},
+ {{0x518db967eaf93ac5, 0x71bc989b056652c0, 0xfe2b85d9567197f5, 0x050eca52651e4e38}}},
+{{{0xc3431ade453f0c9c, 0xe9f5045eff703b9b, 0xfcd97ac9ed847b3d, 0x4b0ee6c21c58f4c6}},
+ {{0x97ac397660e668ea, 0x9b19bbfe153ab497, 0x4cb179b534eca79f, 0x6151c09fa131ae57}},
+ {{0x3af55c0dfdf05d96, 0xdd262ee02ab4ee7a, 0x11b2bb8712171709, 0x1fef24fa800f030b}}},
+{{{0x37d653fb1aa73196, 0x0f9495303fd76418, 0xad200b09fb3a17b2, 0x544d49292fc8613e}},
+ {{0x22d2aff530976b86, 0x8d90b806c2d24604, 0xdca1896c4de5bae5, 0x28005fe6c8340c17}},
+ {{0x6aefba9f34528688, 0x5c1bff9425107da1, 0xf75bbbcd66d94b36, 0x72e472930f316dfa}}},
+{{{0x2695208c9781084f, 0xb1502a0b23450ee1, 0xfd9daea603efde02, 0x5a9d2e8c2733a34c}},
+ {{0x07f3f635d32a7627, 0x7aaa4d865f6566f0, 0x3c85e79728d04450, 0x1fee7f000fe06438}},
+ {{0x765305da03dbf7e5, 0xa4daf2491434cdbd, 0x7b4ad5cdd24a88ec, 0x00f94051ee040543}}},
+{{{0x8d356b23c3d330b2, 0xf21c8b9bb0471b06, 0xb36c316c6e42b83c, 0x07d79c7e8beab10d}},
+ {{0xd7ef93bb07af9753, 0x583ed0cf3db766a7, 0xce6998bf6e0b1ec5, 0x47b7ffd25dd40452}},
+ {{0x87fbfb9cbc08dd12, 0x8a066b3ae1eec29b, 0x0d57242bdb1fc1bf, 0x1c3520a35ea64bb6}}},
+{{{0x80d253a6bccba34a, 0x3e61c3a13838219b, 0x90c3b6019882e396, 0x1c3d05775d0ee66f}},
+ {{0xcda86f40216bc059, 0x1fbb231d12bcd87e, 0xb4956a9e17c70990, 0x38750c3b66d12e55}},
+ {{0x692ef1409422e51a, 0xcbc0c73c2b5df671, 0x21014fe7744ce029, 0x0621e2c7d330487c}}},
+{{{0xaf9860cc8259838d, 0x90ea48c1c69f9adc, 0x6526483765581e30, 0x0007d6097bd3a5bc}},
+ {{0xb7ae1796b0dbf0f3, 0x54dfafb9e17ce196, 0x25923071e9aaa3b4, 0x5d8e589ca1002e9d}},
+ {{0xc0bf1d950842a94b, 0xb2d3c363588f2e3e, 0x0a961438bb51e2ef, 0x1583d7783c1cbf86}}},
+{{{0xeceea2ef5da27ae1, 0x597c3a1455670174, 0xc9a62a126609167a, 0x252a5f2e81ed8f70}},
+ {{0x90034704cc9d28c7, 0x1d1b679ef72cc58f, 0x16e12b5fbe5b8726, 0x4958064e83c5580a}},
+ {{0x0d2894265066e80d, 0xfcc3f785307c8c6b, 0x1b53da780c1112fd, 0x079c170bd843b388}}},
+{{{0x0506ece464fa6fff, 0xbee3431e6205e523, 0x3579422451b8ea42, 0x6dec05e34ac9fb00}},
+ {{0xcdd6cd50c0d5d056, 0x9af7686dbb03573b, 0x3ca6723ff3c3ef48, 0x6768c0d7317b8acc}},
+ {{0x94b625e5f155c1b3, 0x417bf3a7997b7b91, 0xc22cbddc6d6b2600, 0x51445e14ddcd52f4}}},
+{{{0x57502b4b3b144951, 0x8e67ff6b444bbcb3, 0xb8bd6927166385db, 0x13186f31e39295c8}},
+ {{0x893147ab2bbea455, 0x8c53a24f92079129, 0x4b49f948be30f7a7, 0x12e990086e4fd43d}},
+ {{0xf10c96b37fdfbb2e, 0x9f9a935e121ceaf9, 0xdf1136c43a5b983f, 0x77b2e3f05d3e99af}}},
+{{{0x296fa9c59c2ec4de, 0xbc8b61bf4f84f3cb, 0x1c7706d917a8f908, 0x63b795fc7ad3255d}},
+ {{0xd598639c12ddb0a4, 0xa5d19f30c024866b, 0xd17c2f0358fce460, 0x07a195152e095e8a}},
+ {{0xa8368f02389e5fc8, 0x90433b02cf8de43b, 0xafa1fd5dc5412643, 0x3e8fe83d032f0137}}},
+{{{0x2f8b15b90570a294, 0x94f2427067084549, 0xde1c5ae161bbfd84, 0x75ba3b797fac4007}},
+ {{0x08704c8de8efd13c, 0xdfc51a8e33e03731, 0xa59d5da51260cde3, 0x22d60899a6258c86}},
+ {{0x6239dbc070cdd196, 0x60fe8a8b6c7d8a9a, 0xb38847bceb401260, 0x0904d07b87779e5e}}},
+{{{0xb4ce1fd4ddba919c, 0xcf31db3ec74c8daa, 0x2c63cc63ad86cc51, 0x43e2143fbc1dde07}},
+ {{0xf4322d6648f940b9, 0x06952f0cbd2d0c39, 0x167697ada081f931, 0x6240aacebaf72a6c}},
+ {{0xf834749c5ba295a0, 0xd6947c5bca37d25a, 0x66f13ba7e7c9316a, 0x56bdaf238db40cac}}},
+{{{0x362ab9e3f53533eb, 0x338568d56eb93d40, 0x9e0e14521d5a5572, 0x1d24a86d83741318}},
+ {{0x1310d36cc19d3bb2, 0x062a6bb7622386b9, 0x7c9b8591d7a14f5c, 0x03aa31507e1e5754}},
+ {{0xf4ec7648ffd4ce1f, 0xe045eaf054ac8c1c, 0x88d225821d09357c, 0x43b261dc9aeb4859}}},
+{{{0xe55b1e1988bb79bb, 0xa09ed07dc17a359d, 0xb02c2ee2603dea33, 0x326055cf5b276bc2}},
+ {{0x19513d8b6c951364, 0x94fe7126000bf47b, 0x028d10ddd54f9567, 0x02b4d5e242940964}},
+ {{0xb4a155cb28d18df2, 0xeacc4646186ce508, 0xc49cf4936c824389, 0x27a6c809ae5d3410}}},
+{{{0x8ba6ebcd1f0db188, 0x37d3d73a675a5be8, 0xf22edfa315f5585a, 0x2cb67174ff60a17e}},
+ {{0xcd2c270ac43d6954, 0xdd4a3e576a66cab2, 0x79fa592469d7036c, 0x221503603d8c2599}},
+ {{0x59eecdf9390be1d0, 0xa9422044728ce3f1, 0x82891c667a94f0f4, 0x7b1df4b73890f436}}},
+{{{0xe492f2e0b3b2a224, 0x7c6c9e062b551160, 0x15eb8fe20d7f7b0e, 0x61fcef2658fc5992}},
+ {{0x5f2e221807f8f58c, 0xe3555c9fd49409d4, 0xb2aaa88d1fb6a630, 0x68698245d352e03d}},
+ {{0xdbb15d852a18187a, 0xf3e4aad386ddacd7, 0x44bae2810ff6c482, 0x46cf4c473daf01cf}}},
+{{{0x426525ed9ec4e5f9, 0x0e5eda0116903303, 0x72b1a7f2cbe5cadc, 0x29387bcd14eb5f40}},
+ {{0x213c6ea7f1498140, 0x7c1e7ef8392b4854, 0x2488c38c5629ceba, 0x1065aae50d8cc5bb}},
+ {{0x1c2c4525df200d57, 0x5c3b2dd6bfca674a, 0x0a07e7b1e1834030, 0x69a198e64f1ce716}}},
+{{{0x9062b2e0d91a78bc, 0x47c9889cc8509667, 0x9df54a66405070b8, 0x7369e6a92493a1bf}},
+ {{0xe1014434dcc5caed, 0x47ed5d963c84fb33, 0x70019576ed86a0e7, 0x25b2697bd267f9e4}},
+ {{0x9d673ffb13986864, 0x3ca5fbd9415dc7b8, 0xe04ecc3bdf273b5e, 0x1420683db54e4cd2}}},
+{{{0xb478bd1e249dd197, 0x620c35005e58c102, 0xfb02d32fccbaac5c, 0x60b63bebf508a72d}},
+ {{0x34eebb6fc1cc5ad0, 0x6a1b0ce99646ac8b, 0xd3b0da49a66bde53, 0x31e83b4161d081c1}},
+ {{0x97e8c7129e062b4f, 0x49e48f4f29320ad8, 0x5bece14b6f18683f, 0x55cf1eb62d550317}}},
+{{{0x5879101065c23d58, 0x8b9d086d5094819c, 0xe2402fa912c55fa7, 0x669a6564570891d4}},
+ {{0x3076b5e37df58c52, 0xd73ab9dde799cc36, 0xbd831ce34913ee20, 0x1a56fbaa62ba0133}},
+ {{0x943e6b505c9dc9ec, 0x302557bba77c371a, 0x9873ae5641347651, 0x13c4836799c58a5c}}},
+{{{0x423a5d465ab3e1b9, 0xfc13c187c7f13f61, 0x19f83664ecb5b9b6, 0x66f80c93a637b607}},
+ {{0xc4dcfb6a5d8bd080, 0xdeebc4ec571a4842, 0xd4b2e883b8e55365, 0x50bdc87dc8e5b827}},
+ {{0x606d37836edfe111, 0x32353e15f011abd9, 0x64b03ac325b73b96, 0x1dd56444725fd5ae}}},
+{{{0x8fa47ff83362127d, 0xbc9f6ac471cd7c15, 0x6e71454349220c8b, 0x0e645912219f732e}},
+ {{0xc297e60008bac89a, 0x7d4cea11eae1c3e0, 0xf3e38be19fe7977c, 0x3a3a450f63a305cd}},
+ {{0x078f2f31d8394627, 0x389d3183de94a510, 0xd1e36c6d17996f80, 0x318c8d9393a9a87b}}},
+{{{0xf2745d032afffe19, 0x0c9f3c497f24db66, 0xbc98d3e3ba8598ef, 0x224c7c679a1d5314}},
+ {{0x5d669e29ab1dd398, 0xfc921658342d9e3b, 0x55851dfdf35973cd, 0x509a41c325950af6}},
+ {{0xbdc06edca6f925e9, 0x793ef3f4641b1f33, 0x82ec12809d833e89, 0x05bff02328a11389}}},
+{{{0x3632137023cae00b, 0x544acf0ad1accf59, 0x96741049d21a1c88, 0x780b8cc3fa2a44a7}},
+ {{0x6881a0dd0dc512e4, 0x4fe70dc844a5fafe, 0x1f748e6b8f4a5240, 0x576277cdee01a3ea}},
+ {{0x1ef38abc234f305f, 0x9a577fbd1405de08, 0x5e82a51434e62a0d, 0x5ff418726271b7a1}}},
+{{{0x398e080c1789db9d, 0xa7602025f3e778f5, 0xfa98894c06bd035d, 0x106a03dc25a966be}},
+ {{0xe5db47e813b69540, 0xf35d2a3b432610e1, 0xac1f26e938781276, 0x29d4db8ca0a0cb69}},
+ {{0xd9ad0aaf333353d0, 0x38669da5acd309e5, 0x3c57658ac888f7f0, 0x4ab38a51052cbefa}}},
+{{{0xda7c2b256768d593, 0x98c1c0574422ca13, 0xf1a80bd5ca0ace1d, 0x29cdd1adc088a690}},
+ {{0xd6cfd1ef5fddc09c, 0xe82b3efdf7575dce, 0x25d56b5d201634c2, 0x3041c6bb04ed2b9b}},
+ {{0x0ff2f2f9d956e148, 0xade797759f356b2e, 0x1a4698bb5f6c025c, 0x104bbd6814049a7b}}},
+{{{0x51f0fd3168f1ed67, 0x2c811dcdd86f3bc2, 0x44dc5c4304d2f2de, 0x5be8cc57092a7149}},
+ {{0xa95d9a5fd67ff163, 0xe92be69d4cc75681, 0xb7f8024cde20f257, 0x204f2a20fb072df5}},
+ {{0xc8143b3d30ebb079, 0x7589155abd652e30, 0x653c3c318f6d5c31, 0x2570fb17c279161f}}},
+{{{0x3efa367f2cb61575, 0xf5f96f761cd6026c, 0xe8c7142a65b52562, 0x3dcb65ea53030acd}},
+ {{0x192ea9550bb8245a, 0xc8e6fba88f9050d1, 0x7986ea2d88a4c935, 0x241c5f91de018668}},
+ {{0x28d8172940de6caa, 0x8fbf2cf022d9733a, 0x16d7fcdd235b01d1, 0x08420edd5fcdf0e5}}},
+{{{0xcdff20ab8362fa4a, 0x57e118d4e21a3e6e, 0xe3179617fc39e62b, 0x0d9a53efbc1769fd}},
+ {{0x0358c34e04f410ce, 0xb6135b5a276e0685, 0x5d9670c7ebb91521, 0x04d654f321db889c}},
+ {{0x5e7dc116ddbdb5d5, 0x2954deb68da5dd2d, 0x1cb608173334a292, 0x4a7a4f2618991ad7}}},
+{{{0xf4a718025fb15f95, 0x3df65f346b5c1b8f, 0xcdfcf08500e01112, 0x11b50c4cddd31848}},
+ {{0x24c3b291af372a4b, 0x93da8270718147f2, 0xdd84856486899ef2, 0x4a96314223e0ee33}},
+ {{0xa6e8274408a4ffd6, 0x738e177e9c1576d9, 0x773348b63d02b3f2, 0x4f4bce4dce6bcc51}}},
+{{{0xa71fce5ae2242584, 0x26ea725692f58a9e, 0xd21a09d71cea3cf4, 0x73fcdd14b71c01e6}},
+ {{0x30e2616ec49d0b6f, 0xe456718fcaec2317, 0x48eb409bf26b4fa6, 0x3042cee561595f37}},
+ {{0x427e7079449bac41, 0x855ae36dbce2310a, 0x4cae76215f841a7c, 0x389e740c9a9ce1d6}}},
+{{{0x64fcb3ae34dcb9ce, 0x97500323e348d0ad, 0x45b3f07d62c6381b, 0x61545379465a6788}},
+ {{0xc9bd78f6570eac28, 0xe55b0b3227919ce1, 0x65fc3eaba19b91ed, 0x25c425e5d6263690}},
+ {{0x3f3e06a6f1d7de6e, 0x3ef976278e062308, 0x8c14f6264e8a6c77, 0x6539a08915484759}}},
+{{{0xe9d21f74c3d2f773, 0xc150544125c46845, 0x624e5ce8f9b99e33, 0x11c5e4aac5cd186c}},
+ {{0xddc4dbd414bb4a19, 0x19b2bc3c98424f8e, 0x48a89fd736ca7169, 0x0f65320ef019bd90}},
+ {{0xd486d1b1cafde0c6, 0x4f3fe6e3163b5181, 0x59a8af0dfaf2939a, 0x4cabc7bdec33072a}}},
+{{{0x239e9624089c0a2e, 0xc748c4c03afe4738, 0x17dbed2a764fa12a, 0x639b93f0321c8582}},
+ {{0xc08f788f3f78d289, 0xfe30a72ca1404d9f, 0xf2778bfccf65cc9d, 0x7ee498165acb2021}},
+ {{0x7bd508e39111a1c3, 0x2b2b90d480907489, 0xe7d2aec2ae72fd19, 0x0edf493c85b602a6}}},
+{{{0xaecc8158599b5a68, 0xea574f0febade20e, 0x4fe41d7422b67f07, 0x403b92e3019d4fb4}},
+ {{0x6767c4d284764113, 0xa090403ff7f5f835, 0x1c8fcffacae6bede, 0x04c00c54d1dfa369}},
+ {{0x4dc22f818b465cf8, 0x71a0f35a1480eff8, 0xaee8bfad04c7d657, 0x355bb12ab26176f4}}},
+{{{0xa71e64cc7493bbf4, 0xe5bd84d9eca3b0c3, 0x0a6bc50cfa05e785, 0x0f9b8132182ec312}},
+ {{0xa301dac75a8c7318, 0xed90039db3ceaa11, 0x6f077cbf3bae3f2d, 0x7518eaf8e052ad8e}},
+ {{0xa48859c41b7f6c32, 0x0f2d60bcf4383298, 0x1815a929c9b1d1d9, 0x47c3871bbb1755c4}}},
+{{{0x5144539771ec4f48, 0xf805b17dc98c5d6e, 0xf762c11a47c3c66b, 0x00b89b85764699dc}},
+ {{0xfbe65d50c85066b0, 0x62ecc4b0b3a299b0, 0xe53754ea441ae8e0, 0x08fea02ce8d48d5f}},
+ {{0x824ddd7668deead0, 0xc86445204b685d23, 0xb514cfcd5d89d665, 0x473829a74f75d537}}},
+{{{0x82d2da754679c418, 0xe63bd7d8b2618df0, 0x355eef24ac47eb0a, 0x2078684c4833c6b4}},
+ {{0x23d9533aad3902c9, 0x64c2ddceef03588f, 0x15257390cfe12fb4, 0x6c668b4d44e4d390}},
+ {{0x3b48cf217a78820c, 0xf76a0ab281273e97, 0xa96c65a78c8eed7b, 0x7411a6054f8a433f}}},
+{{{0x4d659d32b99dc86d, 0x044cdc75603af115, 0xb34c712cdcc2e488, 0x7c136574fb8134ff}},
+ {{0x579ae53d18b175b4, 0x68713159f392a102, 0x8455ecba1eef35f5, 0x1ec9a872458c398f}},
+ {{0xb8e6a4d400a2509b, 0x9b81d7020bc882b4, 0x57e7cc9bf1957561, 0x3add88a5c7cd6460}}},
+{{{0xab895770b635dcf2, 0x02dfef6cf66c1fbc, 0x85530268beb6d187, 0x249929fccc879e74}},
+ {{0x85c298d459393046, 0x8f7e35985ff659ec, 0x1d2ca22af2f66e3a, 0x61ba1131a406a720}},
+ {{0xa3d0a0f116959029, 0x023b6b6cba7ebd89, 0x7bf15a3e26783307, 0x5620310cbbd8ece7}}},
+{{{0x528993434934d643, 0xb9dbf806a51222f5, 0x8f6d878fc3f41c22, 0x37676a2a4d9d9730}},
+ {{0x6646b5f477e285d6, 0x40e8ff676c8f6193, 0xa6ec7311abb594dd, 0x7ec846f3658cec4d}},
+ {{0x9b5e8f3f1da22ec7, 0x130f1d776c01cd13, 0x214c8fcfa2989fb8, 0x6daaf723399b9dd5}}},
+{{{0x5f3a7562eb3dbe47, 0xf7ea38548ebda0b8, 0x00c3e53145747299, 0x1304e9e71627d551}},
+ {{0x583b04bfacad8ea2, 0x29b743e8148be884, 0x2b1e583b0810c5db, 0x2b5449e58eb3bbaa}},
+ {{0x789814d26adc9cfe, 0x3c1bab3f8b48dd0b, 0xda0fe1fff979c60a, 0x4468de2d7c2dd693}}},
+{{{0x51bb355e9419469e, 0x33e6dc4c23ddc754, 0x93a5b6d6447f9962, 0x6cce7c6ffb44bd63}},
+ {{0x4b9ad8c6f86307ce, 0x21113531435d0c28, 0xd4a866c5657a772c, 0x5da6427e63247352}},
+ {{0x1a94c688deac22ca, 0xb9066ef7bbae1ff8, 0x88ad8c388d59580f, 0x58f29abfe79f2ca8}}},
+{{{0xe90ecfab8de73e68, 0x54036f9f377e76a5, 0xf0495b0bbe015982, 0x577629c4a7f41e36}},
+ {{0x4b5a64bf710ecdf6, 0xb14ce538462c293c, 0x3643d056d50b3ab9, 0x6af93724185b4870}},
+ {{0x3220024509c6a888, 0xd2e036134b558973, 0x83e236233c33289f, 0x701f25bb0caec18f}}},
+{{{0xc3a8b0f8e4616ced, 0xf700660e9e25a87d, 0x61e3061ff4bca59c, 0x2e0c92bfbdc40be9}},
+ {{0x9d18f6d97cbec113, 0x844a06e674bfdbe4, 0x20f5b522ac4e60d6, 0x720a5bc050955e51}},
+ {{0x0c3f09439b805a35, 0xe84e8b376242abfc, 0x691417f35c229346, 0x0e9b9cbb144ef0ec}}},
+{{{0xfbbad48ffb5720ad, 0xee81916bdbf90d0e, 0xd4813152635543bf, 0x221104eb3f337bd8}},
+ {{0x8dee9bd55db1beee, 0xc9c3ab370a723fb9, 0x44a8f1bf1c68d791, 0x366d44191cfd3cde}},
+ {{0x9e3c1743f2bc8c14, 0x2eda26fcb5856c3b, 0xccb82f0e68a7fb97, 0x4167a4e6bc593244}}},
+{{{0x643b9d2876f62700, 0x5d1d9d400e7668eb, 0x1b4b430321fc0684, 0x7938bb7e2255246a}},
+ {{0xc2be2665f8ce8fee, 0xe967ff14e880d62c, 0xf12e6e7e2f364eee, 0x34b33370cb7ed2f6}},
+ {{0xcdc591ee8681d6cc, 0xce02109ced85a753, 0xed7485c158808883, 0x1176fc6e2dfe65e4}}},
+{{{0xb4af6cd05b9c619b, 0x2ddfc9f4b2a58480, 0x3d4fa502ebe94dc4, 0x08fc3a4c677d5f34}},
+ {{0xdb90e28949770eb8, 0x98fbcc2aacf440a3, 0x21354ffeded7879b, 0x1f6a3e54f26906b6}},
+ {{0x60a4c199d30734ea, 0x40c085b631165cd6, 0xe2333e23f7598295, 0x4f2fad0116b900d1}}},
+{{{0x44beb24194ae4e54, 0x5f541c511857ef6c, 0xa61e6b2d368d0498, 0x445484a4972ef7ab}},
+ {{0x962cd91db73bb638, 0xe60577aafc129c08, 0x6f619b39f3b61689, 0x3451995f2944ee81}},
+ {{0x9152fcd09fea7d7c, 0x4a816c94b0935cf6, 0x258e9aaa47285c40, 0x10b89ca6042893b7}}},
+{{{0x3d5947499718289c, 0x12ebf8c524533f26, 0x0262bfcb14c3ef15, 0x20b878d577b7518e}},
+ {{0x753941be5a45f06e, 0xd07caeed6d9c5f65, 0x11776b9c72ff51b6, 0x17d2d1d9ef0d4da9}},
+ {{0x27f2af18073f3e6a, 0xfd3fe519d7521069, 0x22e3b72c3ca60022, 0x72214f63cc65c6a7}}},
+{{{0xb4e37f405307a693, 0xaba714d72f336795, 0xd6fbd0a773761099, 0x5fdf48c58171cbc9}},
+ {{0x1d9db7b9f43b29c9, 0xd605824a4f518f75, 0xf2c072bd312f9dc4, 0x1f24ac855a1545b0}},
+ {{0x24d608328e9505aa, 0x4748c1d10c1420ee, 0xc7ffe45c06fb25a2, 0x00ba739e2ae395e6}}},
+{{{0x592e98de5c8790d6, 0xe5bfb7d345c2a2df, 0x115a3b60f9b49922, 0x03283a3e67ad78f3}},
+ {{0xae4426f5ea88bb26, 0x360679d984973bfb, 0x5c9f030c26694e50, 0x72297de7d518d226}},
+ {{0x48241dc7be0cb939, 0x32f19b4d8b633080, 0xd3dfc90d02289308, 0x05e1296846271945}}},
+{{{0xba82eeb32d9c495a, 0xceefc8fcf12bb97c, 0xb02dabae93b5d1e0, 0x39c00c9c13698d9b}},
+ {{0xadbfbbc8242c4550, 0xbcc80cecd03081d9, 0x843566a6f5c8df92, 0x78cf25d38258ce4c}},
+ {{0x15ae6b8e31489d68, 0xaa851cab9c2bf087, 0xc9a75a97f04efa05, 0x006b52076b3ff832}}},
+{{{0x29e0cfe19d95781c, 0xb681df18966310e2, 0x57df39d370516b39, 0x4d57e3443bc76122}},
+ {{0xf5cb7e16b9ce082d, 0x3407f14c417abc29, 0xd4b36bce2bf4a7ab, 0x7de2e9561a9f75ce}},
+ {{0xde70d4f4b6a55ecb, 0x4801527f5d85db99, 0xdbc9c440d3ee9a81, 0x6b2a90af1a6029ed}}},
+{{{0x6923f4fc9ae61e97, 0x5735281de03f5fd1, 0xa764ae43e6edd12d, 0x5fd8f4e9d12d3e4a}},
+ {{0x77ebf3245bb2d80a, 0xd8301b472fb9079b, 0xc647e6f24cee7333, 0x465812c8276c2109}},
+ {{0x4d43beb22a1062d9, 0x7065fb753831dc16, 0x180d4a7bde2968d7, 0x05b32c2b1cb16790}}},
+{{{0xc8c05eccd24da8fd, 0xa1cf1aac05dfef83, 0xdbbeeff27df9cd61, 0x3b5556a37b471e99}},
+ {{0xf7fca42c7ad58195, 0x3214286e4333f3cc, 0xb6c29d0d340b979d, 0x31771a48567307e1}},
+ {{0x32b0c524e14dd482, 0xedb351541a2ba4b6, 0xa3d16048282b5af3, 0x4fc079d27a7336eb}}},
+{{{0x51c938b089bf2f7f, 0x2497bd6502dfe9a7, 0xffffc09c7880e453, 0x124567cecaf98e92}},
+ {{0xdc348b440c86c50d, 0x1337cbc9cc94e651, 0x6422f74d643e3cb9, 0x241170c2bae3cd08}},
+ {{0x3ff9ab860ac473b4, 0xf0911dee0113e435, 0x4ae75060ebc6c4af, 0x3f8612966c87000d}}},
+{{{0x559a0cc9782a0dde, 0x551dcdb2ea718385, 0x7f62865b31ef238c, 0x504aa7767973613d}},
+ {{0x9c18fcfa36048d13, 0x29159db373899ddd, 0xdc9f350b9f92d0aa, 0x26f57eee878a19d4}},
+ {{0x0cab2cd55687efb1, 0x5180d162247af17b, 0x85c15a344f5a2467, 0x4041943d9dba3069}}},
+{{{0xc3c0eeba43ebcc96, 0x8d749c9c26ea9caf, 0xd9fa95ee1c77ccc6, 0x1420a1d97684340f}},
+ {{0x4b217743a26caadd, 0x47a6b424648ab7ce, 0xcb1d4f7a03fbc9e3, 0x12d931429800d019}},
+ {{0x00c67799d337594f, 0x5e3c5140b23aa47b, 0x44182854e35ff395, 0x1b4f92314359a012}}},
+{{{0x3e5c109d89150951, 0x39cefa912de9696a, 0x20eae43f975f3020, 0x239b572a7f132dae}},
+ {{0x33cf3030a49866b1, 0x251f73d2215f4859, 0xab82aa4051def4f6, 0x5ff191d56f9a23f6}},
+ {{0x819ed433ac2d9068, 0x2883ab795fc98523, 0xef4572805593eb3d, 0x020c526a758f36cb}}},
+{{{0x779834f89ed8dbbc, 0xc8f2aaf9dc7ca46c, 0xa9524cdca3e1b074, 0x02aacc4615313877}},
+ {{0xe931ef59f042cc89, 0x2c589c9d8e124bb6, 0xadc8e18aaec75997, 0x452cfe0a5602c50c}},
+ {{0x86a0f7a0647877df, 0xbbc464270e607c9f, 0xab17ea25f1fb11c9, 0x4cfb7d7b304b877b}}},
+{{{0x72b43d6cb89b75fe, 0x54c694d99c6adc80, 0xb8c3aa373ee34c9f, 0x14b4622b39075364}},
+ {{0xe28699c29789ef12, 0x2b6ecd71df57190d, 0xc343c857ecc970d0, 0x5b1d4cbc434d3ac5}},
+ {{0xb6fb2615cc0a9f26, 0x3a4f0e2bb88dcce5, 0x1301498b3369a705, 0x2f98f71258592dd1}}},
+{{{0x0c94a74cb50f9e56, 0x5b1ff4a98e8e1320, 0x9a2acc2182300f67, 0x3a6ae249d806aaf9}},
+ {{0x2e12ae444f54a701, 0xfcfe3ef0a9cbd7de, 0xcebf890d75835de0, 0x1d8062e9e7614554}},
+ {{0x657ada85a9907c5a, 0x1a0ea8b591b90f62, 0x8d0e1dfbdf34b4e9, 0x298b8ce8aef25ff3}}},
+{{{0x2a927953eff70cb2, 0x4b89c92a79157076, 0x9418457a30a7cf6a, 0x34b8a8404d5ce485}},
+ {{0x837a72ea0a2165de, 0x3fab07b40bcf79f6, 0x521636c77738ae70, 0x6ba6271803a7d7dc}},
+ {{0xc26eecb583693335, 0xd5a813df63b5fefd, 0xa293aa9aa4b22573, 0x71d62bdd465e1c6a}}},
+{{{0x6533cc28d378df80, 0xf6db43790a0fa4b4, 0xe3645ff9f701da5a, 0x74d5f317f3172ba4}},
+ {{0xcd2db5dab1f75ef5, 0xd77f95cf16b065f5, 0x14571fea3f49f085, 0x1c333621262b2b3d}},
+ {{0xa86fe55467d9ca81, 0x398b7c752b298c37, 0xda6d0892e3ac623b, 0x4aebcc4547e9d98c}}},
+{{{0x12f0071b276d01c9, 0xe7b8bac586c48c70, 0x5308129b71d6fba9, 0x5d88fbf95a3db792}},
+ {{0x0b408d9e7354b610, 0x806b32535ba85b6e, 0xdbe63a034a58a207, 0x173bd9ddc9a1df2c}},
+ {{0x2b500f1efe5872df, 0x58d6582ed43918c1, 0xe6ed278ec9673ae0, 0x06e1cd13b19ea319}}},
+{{{0x40d0ad516f166f23, 0x118e32931fab6abe, 0x3fe35e14a04d088e, 0x3080603526e16266}},
+ {{0x472baf629e5b0353, 0x3baa0b90278d0447, 0x0c785f469643bf27, 0x7f3a6a1a8d837b13}},
+ {{0xf7e644395d3d800b, 0x95a8d555c901edf6, 0x68cd7830592c6339, 0x30d0fded2e51307e}}},
+{{{0xe0594d1af21233b3, 0x1bdbe78ef0cc4d9c, 0x6965187f8f499a77, 0x0a9214202c099868}},
+ {{0x9cb4971e68b84750, 0xa09572296664bbcf, 0x5c8de72672fa412b, 0x4615084351c589d9}},
+ {{0xbc9019c0aeb9a02e, 0x55c7110d16034cae, 0x0e6df501659932ec, 0x3bca0d2895ca5dfe}}},
+{{{0x40f031bc3c5d62a4, 0x19fc8b3ecff07a60, 0x98183da2130fb545, 0x5631deddae8f13cd}},
+ {{0x9c688eb69ecc01bf, 0xf0bc83ada644896f, 0xca2d955f5f7a9fe2, 0x4ea8b4038df28241}},
+ {{0x2aed460af1cad202, 0x46305305a48cee83, 0x9121774549f11a5f, 0x24ce0930542ca463}}},
+{{{0x1fe890f5fd06c106, 0xb5c468355d8810f2, 0x827808fe6e8caf3e, 0x41d4e3c28a06d74b}},
+ {{0x3fcfa155fdf30b85, 0xd2f7168e36372ea4, 0xb2e064de6492f844, 0x549928a7324f4280}},
+ {{0xf26e32a763ee1a2e, 0xae91e4b7d25ffdea, 0xbc3bd33bd17f4d69, 0x491b66dec0dcff6a}}},
+{{{0x98f5b13dc7ea32a7, 0xe3d5f8cc7e16db98, 0xac0abf52cbf8d947, 0x08f338d0c85ee4ac}},
+ {{0x75f04a8ed0da64a1, 0xed222caf67e2284b, 0x8234a3791f7b7ba4, 0x4cf6b8b0b7018b67}},
+ {{0xc383a821991a73bd, 0xab27bc01df320c7a, 0xc13d331b84777063, 0x530d4a82eb078a99}}},
+{{{0x004c3630e1f94825, 0x7e2d78268cab535a, 0xc7482323cc84ff8b, 0x65ea753f101770b9}},
+ {{0x6d6973456c9abf9e, 0x257fb2fc4900a880, 0x2bacf412c8cfb850, 0x0db3e7e00cbfbd5b}},
+ {{0x3d66fc3ee2096363, 0x81d62c7f61b5cb6b, 0x0fbe044213443b1a, 0x02a4ec1921e1a1db}}},
+{{{0x5ce6259a3b24b8a2, 0xb8577acc45afa0b8, 0xcccbe6e88ba07037, 0x3d143c51127809bf}},
+ {{0xf5c86162f1cf795f, 0x118c861926ee57f2, 0x172124851c063578, 0x36d12b5dec067fcf}},
+ {{0x126d279179154557, 0xd5e48f5cfc783a0a, 0x36bdb6e8df179bac, 0x2ef517885ba82859}}},
+{{{0x4637974e8c58aedc, 0xb9ef22fbabf041a4, 0xe185d956e980718a, 0x2f1b78fab143a8a6}},
+ {{0x96eebffb305b2f51, 0xd3f938ad889596b8, 0xf0f52dc746d5dd25, 0x57968290bb3a0095}},
+ {{0xf71ab8430a20e101, 0xf393658d24f0ec47, 0xcf7509a86ee2eed1, 0x7dc43e35dc2aa3e1}}},
+{{{0x85966665887dd9c3, 0xc90f9b314bb05355, 0xc6e08df8ef2079b1, 0x7ef72016758cc12f}},
+ {{0x5a782a5c273e9718, 0x3576c6995e4efd94, 0x0f2ed8051f237d3e, 0x044fb81d82d50a99}},
+ {{0xc1df18c5a907e3d9, 0x57b3371dce4c6359, 0xca704534b201bb49, 0x7f79823f9c30dd2e}}},
+{{{0x8334d239a3b513e8, 0xc13670d4b91fa8d8, 0x12b54136f590bd33, 0x0a4e0373d784d9b4}},
+ {{0x6a9c1ff068f587ba, 0x0827894e0050c8de, 0x3cbf99557ded5be7, 0x64a9b0431c06d6f0}},
+ {{0x2eb3d6a15b7d2919, 0xb0b4f6a0d53a8235, 0x7156ce4389a45d47, 0x071a7d0ace18346c}}},
+{{{0xd3072daac887ba0b, 0x01262905bfa562ee, 0xcf543002c0ef768b, 0x2c3bcc7146ea7e9c}},
+ {{0xcc0c355220e14431, 0x0d65950709b15141, 0x9af5621b209d5f36, 0x7c69bcf7617755d3}},
+ {{0x07f0d7eb04e8295f, 0x10db18252f50f37d, 0xe951a9a3171798d7, 0x6f5a9a7322aca51d}}},
+{{{0x8ba1000c2f41c6c5, 0xc49f79c10cfefb9b, 0x4efa47703cc51c9f, 0x494e21a2e147afca}},
+ {{0xe729d4eba3d944be, 0x8d9e09408078af9e, 0x4525567a47869c03, 0x02ab9680ee8d3b24}},
+ {{0xefa48a85dde50d9a, 0x219a224e0fb9a249, 0xfa091f1dd91ef6d9, 0x6b5d76cbea46bb34}}},
+{{{0x8857556cec0cd994, 0x6472dc6f5cd01dba, 0xaf0169148f42b477, 0x0ae333f685277354}},
+ {{0xe0f941171e782522, 0xf1e6ae74036936d3, 0x408b3ea2d0fcc746, 0x16fb869c03dd313e}},
+ {{0x288e199733b60962, 0x24fc72b4d8abe133, 0x4811f7ed0991d03e, 0x3f81e38b8f70d075}}},
+{{{0x7f910fcc7ed9affe, 0x545cb8a12465874b, 0xa8397ed24b0c4704, 0x50510fc104f50993}},
+ {{0x0adb7f355f17c824, 0x74b923c3d74299a4, 0xd57c3e8bcbf8eaf7, 0x0ad3e2d34cdedc3d}},
+ {{0x6f0c0fc5336e249d, 0x745ede19c331cfd9, 0xf2d6fd0009eefe1c, 0x127c158bf0fa1ebe}}},
+{{{0xf6197c422e9879a2, 0xa44addd452ca3647, 0x9b413fc14b4eaccb, 0x354ef87d07ef4f68}},
+ {{0xdea28fc4ae51b974, 0x1d9973d3744dfe96, 0x6240680b873848a8, 0x4ed82479d167df95}},
+ {{0xfee3b52260c5d975, 0x50352efceb41b0b8, 0x8808ac30a9f6653c, 0x302d92d20539236d}}},
+{{{0x7813c1a2bca4283d, 0xed62f091a1863dd9, 0xaec7bcb8c268fa86, 0x10e5d3b76f1cae4c}},
+ {{0x2dbc6fb6e4e0f177, 0x04e1bf29a4bd6a93, 0x5e1966d4787af6e8, 0x0edc5f5eb426d060}},
+ {{0x5453bfd653da8e67, 0xe9dc1eec24a9f641, 0xbf87263b03578a23, 0x45b46c51361cba72}}},
+{{{0xa9402abf314f7fa1, 0xe257f1dc8e8cf450, 0x1dbbd54b23a8be84, 0x2177bfa36dcb713b}},
+ {{0xce9d4ddd8a7fe3e4, 0xab13645676620e30, 0x4b594f7bb30e9958, 0x5c1c0aef321229df}},
+ {{0x37081bbcfa79db8f, 0x6048811ec25f59b3, 0x087a76659c832487, 0x4ae619387d8ab5bb}}},
+{{{0x8ddbf6aa5344a32e, 0x7d88eab4b41b4078, 0x5eb0eb974a130d60, 0x1a00d91b17bf3e03}},
+ {{0x61117e44985bfb83, 0xfce0462a71963136, 0x83ac3448d425904b, 0x75685abe5ba43d64}},
+ {{0x6e960933eb61f2b2, 0x543d0fa8c9ff4952, 0xdf7275107af66569, 0x135529b623b0e6aa}}},
+{{{0x18f0dbd7add1d518, 0x979f7888cfc11f11, 0x8732e1f07114759b, 0x79b5b81a65ca3a01}},
+ {{0xf5c716bce22e83fe, 0xb42beb19e80985c1, 0xec9da63714254aae, 0x5972ea051590a613}},
+ {{0x0fd4ac20dc8f7811, 0x9a9ad294ac4d4fa8, 0xc01b2d64b3360434, 0x4f7e9c95905f3bdb}}},
+{{{0x62674bbc5781302e, 0xd8520f3989addc0f, 0x8c2999ae53fbd9c6, 0x31993ad92e638e4c}},
+ {{0x71c8443d355299fe, 0x8bcd3b1cdbebead7, 0x8092499ef1a49466, 0x1942eec4a144adc8}},
+ {{0x7dac5319ae234992, 0x2c1b3d910cea3e92, 0x553ce494253c1122, 0x2a0a65314ef9ca75}}},
+{{{0x2db7937ff7f927c2, 0xdb741f0617d0a635, 0x5982f3a21155af76, 0x4cf6e218647c2ded}},
+ {{0xcf361acd3c1c793a, 0x2f9ebcac5a35bc3b, 0x60e860e9a8cda6ab, 0x055dc39b6dea1a13}},
+ {{0xb119227cc28d5bb6, 0x07e24ebc774dffab, 0xa83c78cee4a32c89, 0x121a307710aa24b6}}},
+{{{0xe4db5d5e9f034a97, 0xe153fc093034bc2d, 0x460546919551d3b1, 0x333fc76c7a40e52d}},
+ {{0xd659713ec77483c9, 0x88bfe077b82b96af, 0x289e28231097bcd3, 0x527bb94a6ced3a9b}},
+ {{0x563d992a995b482e, 0x3405d07c6e383801, 0x485035de2f64d8e5, 0x6b89069b20a7a9f7}}},
+{{{0x812aa0416270220d, 0x995a89faf9245b4e, 0xffadc4ce5072ef05, 0x23bc2103aa73eb73}},
+ {{0x4082fa8cb5c7db77, 0x068686f8c734c155, 0x29e6c8d9f6e7a57e, 0x0473d308a7639bcf}},
+ {{0xcaee792603589e05, 0x2b4b421246dcc492, 0x02a1ef74e601a94f, 0x102f73bfde04341a}}},
+{{{0xeb18b9ab7f5745c6, 0x023a8aee5787c690, 0xb72712da2df7afa9, 0x36597d25ea5c013d}},
+ {{0xa2b4dae0b5511c9a, 0x7ac860292bffff06, 0x981f375df5504234, 0x3f6bd725da4ea12d}},
+ {{0x734d8d7b106058ac, 0xd940579e6fc6905f, 0x6466f8f99202932d, 0x7b7ecc19da60d6d0}}},
+{{{0x78c2373c695c690d, 0xdd252e660642906e, 0x951d44444ae12bd2, 0x4235ad7601743956}},
+ {{0x6dae4a51a77cfa9b, 0x82263654e7a38650, 0x09bbffcd8f2d82db, 0x03bedc661bf5caba}},
+ {{0x6258cb0d078975f5, 0x492942549189f298, 0xa0cab423e2e36ee4, 0x0e7ce2b0cdf066a1}}},
+{{{0xc494643ac48c85a3, 0xfd361df43c6139ad, 0x09db17dd3ae94d48, 0x666e0a5d8fb4674a}},
+ {{0xfea6fedfd94b70f9, 0xf130c051c1fcba2d, 0x4882d47e7f2fab89, 0x615256138aeceeb5}},
+ {{0x2abbf64e4870cb0d, 0xcd65bcf0aa458b6b, 0x9abe4eba75e8985d, 0x7f0bc810d514dee4}}},
+{{{0xb9006ba426f4136f, 0x8d67369e57e03035, 0xcbc8dfd94f463c28, 0x0d1f8dbcf8eedbf5}},
+ {{0x83ac9dad737213a0, 0x9ff6f8ba2ef72e98, 0x311e2edd43ec6957, 0x1d3a907ddec5ab75}},
+ {{0xba1693313ed081dc, 0x29329fad851b3480, 0x0128013c030321cb, 0x00011b44a31bfde3}}},
+{{{0x3fdfa06c3fc66c0c, 0x5d40e38e4dd60dd2, 0x7ae38b38268e4d71, 0x3ac48d916e8357e1}},
+ {{0x16561f696a0aa75c, 0xc1bf725c5852bd6a, 0x11a8dd7f9a7966ad, 0x63d988a2d2851026}},
+ {{0x00120753afbd232e, 0xe92bceb8fdd8f683, 0xf81669b384e72b91, 0x33fad52b2368a066}}},
+{{{0x540649c6c5e41e16, 0x0af86430333f7735, 0xb2acfcd2f305e746, 0x16c0f429a256dca7}},
+ {{0x8d2cc8d0c422cfe8, 0x072b4f7b05a13acb, 0xa3feb6e6ecf6a56f, 0x3cc355ccb90a71e2}},
+ {{0xe9b69443903e9131, 0xb8a494cb7a5637ce, 0xc87cd1a4baba9244, 0x631eaf426bae7568}}},
+{{{0xb3e90410da66fe9f, 0x85dd4b526c16e5a6, 0xbc3d97611ef9bf83, 0x5599648b1ea919b5}},
+ {{0x47d975b9a3700de8, 0x7280c5fbe2f80552, 0x53658f2732e45de1, 0x431f2c7f665f80b5}},
+ {{0xd6026344858f7b19, 0x14ab352fa1ea514a, 0x8900441a2090a9d7, 0x7b04715f91253b26}}},
+{{{0x83edbd28acf6ae43, 0x86357c8b7d5c7ab4, 0xc0404769b7eb2c44, 0x59b37bf5c2f6583f}},
+ {{0xb376c280c4e6bac6, 0x970ed3dd6d1d9b0b, 0xb09a9558450bf944, 0x48d0acfa57cde223}},
+ {{0xb60f26e47dabe671, 0xf1d1a197622f3a37, 0x4208ce7ee9960394, 0x16234191336d3bdb}}},
+{{{0xb9e499def6267ff6, 0x7772ca7b742c0843, 0x23a0153fe9a4f2b1, 0x2cdfdfecd5d05006}},
+ {{0xdd499cd61ff38640, 0x29cd9bc3063625a0, 0x51e2d8023dd73dc3, 0x4a25707a203b9231}},
+ {{0x2ab7668a53f6ed6a, 0x304242581dd170a1, 0x4000144c3ae20161, 0x5721896d248e49fc}}},
+{{{0x0b6e5517fd181bae, 0x9022629f2bb963b4, 0x5509bce932064625, 0x578edd74f63c13da}},
+ {{0x285d5091a1d0da4e, 0x4baa6fa7b5fe3e08, 0x63e5177ce19393b3, 0x03c935afc4b030fd}},
+ {{0x997276c6492b0c3d, 0x47ccc2c4dfe205fc, 0xdcd29b84dd623a3c, 0x3ec2ab590288c7a2}}},
+{{{0xa1a0d27be4d87bb9, 0xa98b4deb61391aed, 0x99a0ddd073cb9b83, 0x2dd5c25a200fcace}},
+ {{0xa7213a09ae32d1cb, 0x0f2b87df40f5c2d5, 0x0baea4c6e81eab29, 0x0e1bf66c6adbac5e}},
+ {{0xe2abd5e9792c887e, 0x1a020018cb926d5d, 0xbfba69cdbaae5f1e, 0x730548b35ae88f5f}}},
+{{{0xc43551a3cba8b8ee, 0x65a26f1db2115f16, 0x760f4f52ab8c3850, 0x3043443b411db8ca}},
+ {{0x805b094ba1d6e334, 0xbf3ef17709353f19, 0x423f06cb0622702b, 0x585a2277d87845dd}},
+ {{0xa18a5f8233d48962, 0x6698c4b5ec78257f, 0xa78e6fa5373e41ff, 0x7656278950ef981f}}},
+{{{0x38c3cf59d51fc8c0, 0x9bedd2fd0506b6f2, 0x26bf109fab570e8f, 0x3f4160a8c1b846a6}},
+ {{0xe17073a3ea86cf9d, 0x3a8cfbb707155fdc, 0x4853e7fc31838a8e, 0x28bbf484b613f616}},
+ {{0xf2612f5c6f136c7c, 0xafead107f6dd11be, 0x527e9ad213de6f33, 0x1e79cb358188f75d}}},
+{{{0x013436c3eef7e3f1, 0x828b6a7ffe9e10f8, 0x7ff908e5bcf9defc, 0x65d7951b3a3b3831}},
+ {{0x77e953d8f5e08181, 0x84a50c44299dded9, 0xdc6c2d0c864525e5, 0x478ab52d39d1f2f4}},
+ {{0x66a6a4d39252d159, 0xe5dde1bc871ac807, 0xb82c6b40a6c1c96f, 0x16d87a411a212214}}},
+{{{0xb3bd7e5a42066215, 0x879be3cd0c5a24c1, 0x57c05db1d6f994b7, 0x28f87c8165f38ca6}},
+ {{0xfba4d5e2d54e0583, 0xe21fafd72ebd99fa, 0x497ac2736ee9778f, 0x1f990b577a5a6dde}},
+ {{0xa3344ead1be8f7d6, 0x7d1e50ebacea798f, 0x77c6569e520de052, 0x45882fe1534d6d3e}}},
+{{{0x6669345d757983d6, 0x62b6ed1117aa11a6, 0x7ddd1857985e128f, 0x688fe5b8f626f6dd}},
+ {{0xd8ac9929943c6fe4, 0xb5f9f161a38392a2, 0x2699db13bec89af3, 0x7dcf843ce405f074}},
+ {{0x6c90d6484a4732c0, 0xd52143fdca563299, 0xb3be28c3915dc6e1, 0x6739687e7327191b}}},
+{{{0xef782014385675a6, 0xa2649f30aafda9e8, 0x4cd1eb505cdfa8cb, 0x46115aba1d4dc0b3}},
+ {{0xa66dcc9dc80c1ac0, 0x97a05cf41b38a436, 0xa7ebf3be95dbd7c6, 0x7da0b8f68d7e7dab}},
+ {{0xd40f1953c3b5da76, 0x1dac6f7321119e9b, 0x03cc6021feb25960, 0x5a5f887e83674b4b}}},
+{{{0x8f6301cf70a13d11, 0xcfceb815350dd0c4, 0xf70297d4a4bca47e, 0x3669b656e44d1434}},
+ {{0x9e9628d3a0a643b9, 0xb5c3cb00e6c32064, 0x9b5302897c2dec32, 0x43e37ae2d5d1c70c}},
+ {{0x387e3f06eda6e133, 0x67301d5199a13ac0, 0xbd5ad8f836263811, 0x6a21e6cd4fd5e9be}}},
+{{{0xf1c6170a3046e65f, 0x58712a2a00d23524, 0x69dbbd3c8c82b755, 0x586bf9f1a195ff57}},
+ {{0xef4129126699b2e3, 0x71d30847708d1301, 0x325432d01182b0bd, 0x45371b07001e8b36}},
+ {{0xa6db088d5ef8790b, 0x5278f0dc610937e5, 0xac0349d261a16eb8, 0x0eafb03790e52179}}},
+{{{0x960555c13748042f, 0x219a41e6820baa11, 0x1c81f73873486d0c, 0x309acc675a02c661}},
+ {{0x5140805e0f75ae1d, 0xec02fbe32662cc30, 0x2cebdf1eea92396d, 0x44ae3344c5435bb3}},
+ {{0x9cf289b9bba543ee, 0xf3760e9d5ac97142, 0x1d82e5c64f9360aa, 0x62d5221b7f94678f}}},
+{{{0x524c299c18d0936d, 0xc86bb56c8a0c1a0c, 0xa375052edb4a8631, 0x5c0efde4bc754562}},
+ {{0x7585d4263af77a3c, 0xdfae7b11fee9144d, 0xa506708059f7193d, 0x14f29a5383922037}},
+ {{0xdf717edc25b2d7f5, 0x21f970db99b53040, 0xda9234b7c3ed4c62, 0x5e72365c7bee093e}}},
+{{{0x575bfc074571217f, 0x3779675d0694d95b, 0x9a0a37bbf4191e33, 0x77f1104c47b4eabc}},
+ {{0x7d9339062f08b33e, 0x5b9659e5df9f32be, 0xacff3dad1f9ebdfd, 0x70b20555cb7349b7}},
+ {{0xbe5113c555112c4c, 0x6688423a9a881fcd, 0x446677855e503b47, 0x0e34398f4a06404a}}},
+{{{0xb67d22d93ecebde8, 0x09b3e84127822f07, 0x743fa61fb05b6d8d, 0x5e5405368a362372}},
+ {{0x18930b093e4b1928, 0x7de3e10e73f3f640, 0xf43217da73395d6f, 0x6f8aded6ca379c3e}},
+ {{0xe340123dfdb7b29a, 0x487b97e1a21ab291, 0xf9967d02fde6949e, 0x780de72ec8d3de97}}},
+{{{0x0ae28545089ae7bc, 0x388ddecf1c7f4d06, 0x38ac15510a4811b8, 0x0eb28bf671928ce4}},
+ {{0x671feaf300f42772, 0x8f72eb2a2a8c41aa, 0x29a17fd797373292, 0x1defc6ad32b587a6}},
+ {{0xaf5bbe1aef5195a7, 0x148c1277917b15ed, 0x2991f7fb7ae5da2e, 0x467d201bf8dd2867}}},
+{{{0x95fe919a74ef4fad, 0x3a827becf6a308a2, 0x964e01d309a47b01, 0x71c43c4f5ba3c797}},
+ {{0xbc1ef4bd567ae7a9, 0x3f624cb2d64498bd, 0xe41064d22c1f4ec8, 0x2ef9c5a5ba384001}},
+ {{0xb6fd6df6fa9e74cd, 0xf18278bce4af267a, 0x8255b3d0f1ef990e, 0x5a758ca390c5f293}}},
+{{{0xa2b72710d9462495, 0x3aa8c6d2d57d5003, 0xe3d400bfa0b487ca, 0x2dbae244b3eb72ec}},
+ {{0x8ce0918b1d61dc94, 0x8ded36469a813066, 0xd4e6a829afe8aad3, 0x0a738027f639d43f}},
+ {{0x980f4a2f57ffe1cc, 0x00670d0de1839843, 0x105c3f4a49fb15fd, 0x2698ca635126a69c}}},
+{{{0xe765318832b0ba78, 0x381831f7925cff8b, 0x08a81b91a0291fcc, 0x1fb43dcc49caeb07}},
+ {{0x2e3d702f5e3dd90e, 0x9e3f0918e4d25386, 0x5e773ef6024da96a, 0x3c004b0c4afa3332}},
+ {{0x9aa946ac06f4b82b, 0x1ca284a5a806c4f3, 0x3ed3265fc6cd4787, 0x6b43fd01cd1fd217}}},
+{{{0xc7a75d4b4697c544, 0x15fdf848df0fffbf, 0x2868b9ebaa46785a, 0x5a68d7105b52f714}},
+ {{0xb5c742583e760ef3, 0x75dc52b9ee0ab990, 0xbf1427c2072b923f, 0x73420b2d6ff0d9f0}},
+ {{0xaf2cf6cb9e851e06, 0x8f593913c62238c4, 0xda8ab89699fbf373, 0x3db5632fea34bc9e}}},
+{{{0xf46eee2bf75dd9d8, 0x0d17b1f6396759a5, 0x1bf2d131499e7273, 0x04321adf49d75f13}},
+ {{0x2e4990b1829825d5, 0xedeaeb873e9a8991, 0xeef03d394c704af8, 0x59197ea495df2b0e}},
+ {{0x04e16019e4e55aae, 0xe77b437a7e2f92e9, 0xc7ce2dc16f159aa4, 0x45eafdc1f4d70cc0}}},
+{{{0x698401858045d72b, 0x4c22faa2cf2f0651, 0x941a36656b222dc6, 0x5a5eebc80362dade}},
+ {{0xb60e4624cfccb1ed, 0x59dbc292bd5c0395, 0x31a09d1ddc0481c9, 0x3f73ceea5d56d940}},
+ {{0xb7a7bfd10a4e8dc6, 0xbe57007e44c9b339, 0x60c1207f1557aefa, 0x26058891266218db}}},
+{{{0x59f704a68360ff04, 0xc3d93fde7661e6f4, 0x831b2a7312873551, 0x54ad0c2e4e615d57}},
+ {{0x4c818e3cc676e542, 0x5e422c9303ceccad, 0xec07cccab4129f08, 0x0dedfa10b24443b8}},
+ {{0xee3b67d5b82b522a, 0x36f163469fa5c1eb, 0xa5b4d2f26ec19fd3, 0x62ecb2baa77a9408}}},
+{{{0xe5ed795261152b3d, 0x4962357d0eddd7d1, 0x7482c8d0b96b4c71, 0x2e59f919a966d8be}},
+ {{0x92072836afb62874, 0x5fcd5e8579e104a5, 0x5aad01adc630a14a, 0x61913d5075663f98}},
+ {{0x0dc62d361a3231da, 0xfa47583294200270, 0x02d801513f9594ce, 0x3ddbc2a131c05d5c}}},
+{{{0x9adc0ff9ce5ec54b, 0x039c2a6b8c2f130d, 0x028007c7f0f89515, 0x78968314ac04b36b}},
+ {{0xf3aa57a22796bb14, 0x883abab79b07da21, 0xe54be21831a0391c, 0x5ee7fb38d83205f9}},
+ {{0x538dfdcb41446a8e, 0xa5acfda9434937f9, 0x46af908d263c8c78, 0x61d0633c9bca0d09}}},
+{{{0x63744935ffdb2566, 0xc5bd6b89780b68bb, 0x6f1b3280553eec03, 0x6e965fd847aed7f5}},
+ {{0xada328bcf8fc73df, 0xee84695da6f037fc, 0x637fb4db38c2a909, 0x5b23ac2df8067bdc}},
+ {{0x9ad2b953ee80527b, 0xe88f19aafade6d8d, 0x0e711704150e82cf, 0x79b9bbb9dd95dedc}}},
+{{{0xebb355406a3126c2, 0xd26383a868c8c393, 0x6c0c6429e5b97a82, 0x5065f158c9fd2147}},
+ {{0xd1997dae8e9f7374, 0xa032a2f8cfbb0816, 0xcd6cba126d445f0a, 0x1ba811460accb834}},
+ {{0x708169fb0c429954, 0xe14600acd76ecf67, 0x2eaab98a70e645ba, 0x3981f39e58a4faf2}}},
+{{{0x18fb8a7559230a93, 0x1d168f6960e6f45d, 0x3a85a94514a93cb5, 0x38dc083705acd0fd}},
+ {{0xc845dfa56de66fde, 0xe152a5002c40483a, 0xe9d2e163c7b4f632, 0x30f4452edcbc1b65}},
+ {{0x856d2782c5759740, 0xfa134569f99cbecc, 0x8844fc73c0ea4e71, 0x632d9a1a593f2469}}},
+{{{0xf6bb6b15b807cba6, 0x1823c7dfbc54f0d7, 0xbb1d97036e29670b, 0x0b24f48847ed4a57}},
+ {{0xbf09fd11ed0c84a7, 0x63f071810d9f693a, 0x21908c2d57cf8779, 0x3a5a7df28af64ba2}},
+ {{0xdcdad4be511beac7, 0xa4538075ed26ccf2, 0xe19cff9f005f9a65, 0x34fcf74475481f63}}},
+{{{0xc197e04c789767ca, 0xb8714dcb38d9467d, 0x55de888283f95fa8, 0x3d3bdc164dfa63f7}},
+ {{0xa5bb1dab78cfaa98, 0x5ceda267190b72f2, 0x9309c9110a92608e, 0x0119a3042fb374b0}},
+ {{0x67a2d89ce8c2177d, 0x669da5f66895d0c1, 0xf56598e5b282a2b0, 0x56c088f1ede20a73}}},
+{{{0x336d3d1110a86e17, 0xd7f388320b75b2fa, 0xf915337625072988, 0x09674c6b99108b87}},
+ {{0x581b5fac24f38f02, 0xa90be9febae30cbd, 0x9a2169028acf92f0, 0x038b7ea48359038f}},
+ {{0x9f4ef82199316ff8, 0x2f49d282eaa78d4f, 0x0971a5ab5aef3174, 0x6e5e31025969eb65}}},
+{{{0xb16c62f587e593fb, 0x4999eddeca5d3e71, 0xb491c1e014cc3e6d, 0x08f5114789a8dba8}},
+ {{0x3304fb0e63066222, 0xfb35068987acba3f, 0xbd1924778c1061a3, 0x3058ad43d1838620}},
+ {{0x323c0ffde57663d0, 0x05c3df38a22ea610, 0xbdc78abdac994f9a, 0x26549fa4efe3dc99}}},
+{{{0x741d5a461e6bf9d6, 0x2305b3fc7777a581, 0xd45574a26474d3d9, 0x1926e1dc6401e0ff}},
+ {{0xdb468549af3f666e, 0xd77fcf04f14a0ea5, 0x3df23ff7a4ba0c47, 0x3a10dfe132ce3c85}},
+ {{0xe07f4e8aea17cea0, 0x2fd515463a1fc1fd, 0x175322fd31f2c0f1, 0x1fa1d01d861e5d15}}},
+{{{0xcc8055947d599832, 0x1e4656da37f15520, 0x99f6f7744e059320, 0x773563bc6a75cf33}},
+ {{0x38dcac00d1df94ab, 0x2e712bddd1080de9, 0x7f13e93efdd5e262, 0x73fced18ee9a01e5}},
+ {{0x06b1e90863139cb3, 0xa493da67c5a03ecd, 0x8d77cec8ad638932, 0x1f426b701b864f44}}},
+{{{0xefc9264c41911c01, 0xf1a3b7b817a22c25, 0x5875da6bf30f1447, 0x4e1af5271d31b090}},
+ {{0xf17e35c891a12552, 0xb76b8153575e9c76, 0xfa83406f0d9b723e, 0x0b76bb1b3fa7e438}},
+ {{0x08b8c1f97f92939b, 0xbe6771cbd444ab6e, 0x22e5646399bb8017, 0x7b6dd61eb772a955}}},
+{{{0xb7adc1e850f33d92, 0x7998fa4f608cd5cf, 0xad962dbd8dfc5bdb, 0x703e9bceaf1d2f4f}},
+ {{0x5730abf9ab01d2c7, 0x16fb76dc40143b18, 0x866cbe65a0cbb281, 0x53fa9b659bff6afe}},
+ {{0x6c14c8e994885455, 0x843a5d6665aed4e5, 0x181bb73ebcd65af1, 0x398d93e5c4c61f50}}},
+{{{0x1c4bd16733e248f3, 0xbd9e128715bf0a5f, 0xd43f8cf0a10b0376, 0x53b09b5ddf191b13}},
+ {{0xc3877c60d2e7e3f2, 0x3b34aaa030828bb1, 0x283e26e7739ef138, 0x699c9c9002c30577}},
+ {{0xf306a7235946f1cc, 0x921718b5cce5d97d, 0x28cdd24781b4e975, 0x51caf30c6fcdd907}}},
+{{{0xa60ba7427674e00a, 0x630e8570a17a7bf3, 0x3758563dcf3324cc, 0x5504aa292383fdaa}},
+ {{0x737af99a18ac54c7, 0x903378dcc51cb30f, 0x2b89bc334ce10cc7, 0x12ae29c189f8e99a}},
+ {{0xa99ec0cb1f0d01cf, 0x0dd1efcc3a34f7ae, 0x55ca7521d09c4e22, 0x5fd14fe958eba5ea}}},
+{{{0xb5dc2ddf2845ab2c, 0x069491b10a7fe993, 0x4daaf3d64002e346, 0x093ff26e586474d1}},
+ {{0x3c42fe5ebf93cb8e, 0xbedfa85136d4565f, 0xe0f0859e884220e8, 0x7dd73f960725d128}},
+ {{0xb10d24fe68059829, 0x75730672dbaf23e5, 0x1367253ab457ac29, 0x2f59bcbc86b470a4}}},
+{{{0x83847d429917135f, 0xad1b911f567d03d7, 0x7e7748d9be77aad1, 0x5458b42e2e51af4a}},
+ {{0x7041d560b691c301, 0x85201b3fadd7e71e, 0x16c2e16311335585, 0x2aa55e3d010828b1}},
+ {{0xed5192e60c07444f, 0x42c54e2d74421d10, 0x352b4c82fdb5c864, 0x13e9004a8a768664}}},
+{{{0x739d8845832fcedb, 0xfa38d6c9ae6bf863, 0x32bc0dcab74ffef7, 0x73937e8814bce45e}},
+ {{0xbb2e00c9193b877f, 0xece3a890e0dc506b, 0xecf3b7c036de649f, 0x5f46040898de9e1a}},
+ {{0xb9037116297bf48d, 0xa9d13b22d4f06834, 0xe19715574696bdc6, 0x2cf8a4e891d5e835}}},
+{{{0x6d93fd8707110f67, 0xdd4c09d37c38b549, 0x7cb16a4cc2736a86, 0x2049bd6e58252a09}},
+ {{0x2cb5487e17d06ba2, 0x24d2381c3950196b, 0xd7659c8185978a30, 0x7a6f7f2891d6a4f6}},
+ {{0x7d09fd8d6a9aef49, 0xf0ee60be5b3db90b, 0x4c21b52c519ebfd4, 0x6011aadfc545941d}}},
+{{{0x5f67926dcf95f83c, 0x7c7e856171289071, 0xd6a1e7f3998f7a5b, 0x6fc5cc1b0b62f9e0}},
+ {{0x63ded0c802cbf890, 0xfbd098ca0dff6aaa, 0x624d0afdb9b6ed99, 0x69ce18b779340b1e}},
+ {{0xd1ef5528b29879cb, 0xdd1aae3cd47e9092, 0x127e0442189f2352, 0x15596b3ae57101f1}}},
+{{{0x462739d23f9179a2, 0xff83123197d6ddcf, 0x1307deb553f2148a, 0x0d2237687b5f4dda}},
+ {{0x09ff31167e5124ca, 0x0be4158bd9c745df, 0x292b7d227ef556e5, 0x3aa4e241afb6d138}},
+ {{0x2cc138bf2a3305f5, 0x48583f8fa2e926c3, 0x083ab1a25549d2eb, 0x32fcaa6e4687a36c}}},
+{{{0x7bc56e8dc57d9af5, 0x3e0bd2ed9df0bdf2, 0xaac014de22efe4a3, 0x4627e9cefebd6a5c}},
+ {{0x3207a4732787ccdf, 0x17e31908f213e3f8, 0xd5b2ecd7f60d964e, 0x746f6336c2600be9}},
+ {{0x3f4af345ab6c971c, 0xe288eb729943731f, 0x33596a8a0344186d, 0x7b4917007ed66293}}},
+{{{0x2d85fb5cab84b064, 0x497810d289f3bc14, 0x476adc447b15ce0c, 0x122ba376f844fd7b}},
+ {{0x54341b28dd53a2dd, 0xaa17905bdf42fc3f, 0x0ff592d94dd2f8f4, 0x1d03620fe08cd37d}},
+ {{0xc20232cda2b4e554, 0x9ed0fd42115d187f, 0x2eabb4be7dd479d9, 0x02c70bf52b68ec4c}}},
+{{{0xa287ec4b5d0b2fbb, 0x415c5790074882ca, 0xe044a61ec1d0815c, 0x26334f0a409ef5e0}},
+ {{0xace532bf458d72e1, 0x5be768e07cb73cb5, 0x56cf7d94ee8bbde7, 0x6b0697e3feb43a03}},
+ {{0xb6c8f04adf62a3c0, 0x3ef000ef076da45d, 0x9c9cb95849f0d2a9, 0x1cc37f43441b2fae}}},
+{{{0x508f565a5cc7324f, 0xd061c4c0e506a922, 0xfb18abdb5c45ac19, 0x6c6809c10380314a}},
+ {{0xd76656f1c9ceaeb9, 0x1c5b15f818e5656a, 0x26e72832844c2334, 0x3a346f772f196838}},
+ {{0xd2d55112e2da6ac8, 0xe9bd0331b1e851ed, 0x960746dd8ec67262, 0x05911b9f6ef7c5d0}}},
+{{{0xc1339983f5df0ebb, 0xc0f3758f512c4cac, 0x2cf1130a0bb398e1, 0x6b3cecf9aa270c62}},
+ {{0x5349acf3512eeaef, 0x20c141d31cc1cb49, 0x24180c07a99a688d, 0x555ef9d1c64b2d17}},
+ {{0x36a770ba3b73bd08, 0x624aef08a3afbf0c, 0x5737ff98b40946f2, 0x675f4de13381749d}}},
+{{{0x0e2c52036b1782fc, 0x64816c816cad83b4, 0xd0dcbdd96964073e, 0x13d99df70164c520}},
+ {{0xa12ff6d93bdab31d, 0x0725d80f9d652dfe, 0x019c4ff39abe9487, 0x60f450b882cd3c43}},
+ {{0x014b5ec321e5c0ca, 0x4fcb69c9d719bfa2, 0x4e5f1c18750023a0, 0x1c06de9e55edac80}}},
+{{{0x990f7ad6a33ec4e2, 0x6608f938be2ee08e, 0x9ca143c563284515, 0x4cf38a1fec2db60d}},
+ {{0xffd52b40ff6d69aa, 0x34530b18dc4049bb, 0x5e4a5c2fa34d9897, 0x78096f8e7d32ba2d}},
+ {{0xa0aaaa650dfa5ce7, 0xf9c49e2a48b5478c, 0x4f09cc7d7003725b, 0x373cad3a26091abe}}},
+{{{0xb294634d82c9f57c, 0x1fcbfde124934536, 0x9e9c4db3418cdb5a, 0x0040f3d9454419fc}},
+ {{0xf1bea8fb89ddbbad, 0x3bcb2cbc61aeaecb, 0x8f58a7bb1f9b8d9d, 0x21547eda5112a686}},
+ {{0xdefde939fd5986d3, 0xf4272c89510a380c, 0xb72ba407bb3119b9, 0x63550a334a254df4}}},
+{{{0x6507d6edb569cf37, 0x178429b00ca52ee1, 0xea7c0090eb6bd65d, 0x3eea62c7daf78f51}},
+ {{0x9bba584572547b49, 0xf305c6fae2c408e0, 0x60e8fa69c734f18d, 0x39a92bafaa7d767a}},
+ {{0x9d24c713e693274e, 0x5f63857768dbd375, 0x70525560eb8ab39a, 0x68436a0665c9c4cd}}},
+{{{0xbc0235e8202f3f27, 0xc75c00e264f975b0, 0x91a4e9d5a38c2416, 0x17b6e7f68ab789f9}},
+ {{0x1e56d317e820107c, 0xc5266844840ae965, 0xc1e0a1c6320ffc7a, 0x5373669c91611472}},
+ {{0x5d2814ab9a0e5257, 0x908f2084c9cab3fc, 0xafcaf5885b2d1eca, 0x1cb4b5a678f87d11}}},
+{{{0xb664c06b394afc6c, 0x0c88de2498da5fb1, 0x4f8d03164bcad834, 0x330bca78de7434a2}},
+ {{0x6b74aa62a2a007e7, 0xf311e0b0f071c7b1, 0x5707e438000be223, 0x2dc0fd2d82ef6eac}},
+ {{0x982eff841119744e, 0xf9695e962b074724, 0xc58ac14fbfc953fb, 0x3c31be1b369f1cf5}}},
+{{{0xb0f4864d08948aee, 0x07dc19ee91ba1c6f, 0x7975cdaea6aca158, 0x330b61134262d4bb}},
+ {{0xc168bc93f9cb4272, 0xaeb8711fc7cedb98, 0x7f0e52aa34ac8d7a, 0x41cec1097e7d55bb}},
+ {{0xf79619d7a26d808a, 0xbb1fd49e1d9e156d, 0x73d7c36cdba1df27, 0x26b44cd91f28777d}}},
+{{{0x51f048478f387475, 0xb25dbcf49cbecb3c, 0x9aab1244d99f2055, 0x2c709e6c1c10a5d6}},
+ {{0xe1b7f29362730383, 0x4b5279ffebca8a2c, 0xdafc778abfd41314, 0x7deb10149c72610f}},
+ {{0xcb62af6a8766ee7a, 0x66cbec045553cd0e, 0x588001380f0be4b5, 0x08e68e9ff62ce2ea}}},
+{{{0x34ad500a4bc130ad, 0x8d38db493d0bd49c, 0xa25c3d98500a89be, 0x2f1f3f87eeba3b09}},
+ {{0x2f2d09d50ab8f2f9, 0xacb9218dc55923df, 0x4a8f342673766cb9, 0x4cb13bd738f719f5}},
+ {{0xf7848c75e515b64a, 0xa59501badb4a9038, 0xc20d313f3f751b50, 0x19a1e353c0ae2ee8}}},
+{{{0x7d1c7560bafa05c3, 0xb3e1a0a0c6e55e61, 0xe3529718c0d66473, 0x41546b11c20c3486}},
+ {{0xb42172cdd596bdbd, 0x93e0454398eefc40, 0x9fb15347b44109b5, 0x736bd3990266ae34}},
+ {{0x85532d509334b3b4, 0x46fd114b60816573, 0xcc5f5f30425c8375, 0x412295a2b87fab5c}}},
+{{{0x19c99b88f57ed6e9, 0x5393cb266df8c825, 0x5cee3213b30ad273, 0x14e153ebb52d2e34}},
+ {{0x2e655261e293eac6, 0x845a92032133acdb, 0x460975cb7900996b, 0x0760bb8d195add80}},
+ {{0x413e1a17cde6818a, 0x57156da9ed69a084, 0x2cbf268f46caccb1, 0x6b34be9bc33ac5f2}}},
+{{{0xf3df2f643a78c0b2, 0x4c3e971ef22e027c, 0xec7d1c5e49c1b5a3, 0x2012c18f0922dd2d}},
+ {{0x11fc69656571f2d3, 0xc6c9e845530e737a, 0xe33ae7a2d4fe5035, 0x01b9c7b62e6dd30b}},
+ {{0x880b55e55ac89d29, 0x1483241f45a0a763, 0x3d36efdfc2e76c1f, 0x08af5b784e4bade8}}},
+{{{0x283499dc881f2533, 0x9d0525da779323b6, 0x897addfb673441f4, 0x32b79d71163a168d}},
+ {{0xe27314d289cc2c4b, 0x4be4bd11a287178d, 0x18d528d6fa3364ce, 0x6423c1d5afd9826e}},
+ {{0xcc85f8d9edfcb36a, 0x22bcc28f3746e5f9, 0xe49de338f9e5d3cd, 0x480a5efbc13e2dcc}}},
+{{{0x0b51e70b01622071, 0x06b505cf8b1dafc5, 0x2c6bb061ef5aabcd, 0x47aa27600cb7bf31}},
+ {{0xb6614ce442ce221f, 0x6e199dcc4c053928, 0x663fb4a4dc1cbe03, 0x24b31d47691c8e06}},
+ {{0x2a541eedc015f8c3, 0x11a4fe7e7c693f7c, 0xf0af66134ea278d6, 0x545b585d14dda094}}},
+{{{0x67bf275ea0d43a0f, 0xade68e34089beebe, 0x4289134cd479e72e, 0x0f62f9c332ba5454}},
+ {{0x6204e4d0e3b321e1, 0x3baa637a28ff1e95, 0x0b0ccffd5b99bd9e, 0x4d22dc3e64c8d071}},
+ {{0xfcb46589d63b5f39, 0x5cae6a3f57cbcf61, 0xfebac2d2953afa05, 0x1c0fa01a36371436}}},
+{{{0xd2c604b622943dff, 0xbc8cbece44cfb3a0, 0x5d254ff397808678, 0x0fa3614f3b1ca6bf}},
+ {{0x69082b0e8c936a50, 0xf9c9a035c1dac5b6, 0x6fb73e54c4dfb634, 0x4005419b1d2bc140}},
+ {{0xa003febdb9be82f0, 0x2089c1af3a44ac90, 0xf8499f911954fa8e, 0x1fba218aef40ab42}}},
+{{{0xab549448fac8f53e, 0x81f6e89a7ba63741, 0x74fd6c7d6c2b5e01, 0x392e3acaa8c86e42}},
+ {{0x4f3e57043e7b0194, 0xa81d3eee08daaf7f, 0xc839c6ab99dcdef1, 0x6c535d13ff7761d5}},
+ {{0x4cbd34e93e8a35af, 0x2e0781445887e816, 0x19319c76f29ab0ab, 0x25e17fe4d50ac13b}}},
+{{{0x0a289bd71e04f676, 0x208e1c52d6420f95, 0x5186d8b034691fab, 0x255751442a9fb351}},
+ {{0x915f7ff576f121a7, 0xc34a32272fcd87e3, 0xccba2fde4d1be526, 0x6bba828f8969899b}},
+ {{0xe2d1bc6690fe3901, 0x4cb54a18a0997ad5, 0x971d6914af8460d4, 0x559d504f7f6b7be4}}},
+{{{0xa7738378b3eb54d5, 0x1d69d366a5553c7c, 0x0a26cf62f92800ba, 0x01ab12d5807e3217}},
+ {{0x9c4891e7f6d266fd, 0x0744a19b0307781b, 0x88388f1d6061e23b, 0x123ea6a3354bd50e}},
+ {{0x118d189041e32d96, 0xb9ede3c2d8315848, 0x1eab4271d83245d9, 0x4a3961e2c918a154}}},
+{{{0x71dc3be0f8e6bba0, 0xd6cef8347effe30a, 0xa992425fe13a476a, 0x2cd6bce3fb1db763}},
+ {{0x0327d644f3233f1e, 0x499a260e34fcf016, 0x83b5a716f2dab979, 0x68aceead9bd4111f}},
+ {{0x38b4c90ef3d7c210, 0x308e6e24b7ad040c, 0x3860d9f1b7e73e23, 0x595760d5b508f597}}},
+{{{0x6129bfe104aa6397, 0x8f960008a4a7fccb, 0x3f8bc0897d909458, 0x709fa43edcb291a9}},
+ {{0x882acbebfd022790, 0x89af3305c4115760, 0x65f492e37d3473f4, 0x2cb2c5df54515a2b}},
+ {{0xeb0a5d8c63fd2aca, 0xd22bc1662e694eff, 0x2723f36ef8cbb03a, 0x70f029ecf0c8131f}}},
+{{{0x461307b32eed3e33, 0xae042f33a45581e7, 0xc94449d3195f0366, 0x0b7d5d8a6c314858}},
+ {{0x2a6aafaa5e10b0b9, 0x78f0a370ef041aa9, 0x773efb77aa3ad61f, 0x44eca5a2a74bd9e1}},
+ {{0x25d448327b95d543, 0x70d38300a3340f1d, 0xde1c531c60e1c52b, 0x272224512c7de9e4}}},
+{{{0x1abc92af49c5342e, 0xffeed811b2e6fad0, 0xefa28c8dfcc84e29, 0x11b5df18a44cc543}},
+ {{0xbf7bbb8a42a975fc, 0x8c5c397796ada358, 0xe27fc76fcdedaa48, 0x19735fd7f6bc20a6}},
+ {{0xe3ab90d042c84266, 0xeb848e0f7f19547e, 0x2503a1d065a497b9, 0x0fef911191df895f}}} \ No newline at end of file
diff --git a/ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data b/ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data
new file mode 100644
index 00000000..32a5d474
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_base_slide_multiples.data
@@ -0,0 +1,96 @@
+{{{0x9d103905d740913e, 0xfd399f05d140beb3, 0xa5c18434688f8a09, 0x44fd2f9298f81267}},
+ {{0x2fbc93c6f58c3b85, 0xcf932dc6fb8c0e19, 0x270b4898643d42c2, 0x07cf9d3a33d4ba65}},
+ {{0xabc91205877aaa68, 0x26d9e823ccaac49e, 0x5a1b7dcbdd43598c, 0x6f117b689f0c65a8}}},
+{{{0x56611fe8a4fcd265, 0x3bd353fde5c1ba7d, 0x8131f31a214bd6bd, 0x2ab91587555bda62}},
+ {{0xaf25b0a84cee9730, 0x025a8430e8864b8a, 0xc11b50029f016732, 0x7a164e1b9a80f8f4}},
+ {{0x14ae933f0dd0d889, 0x589423221c35da62, 0xd170e5458cf2db4c, 0x5a2826af12b9b4c6}}},
+{{{0x7f9182c3a447d6ba, 0xd50014d14b2729b7, 0xe33cf11cb864a087, 0x154a7e73eb1b55f3}},
+ {{0xa212bc4408a5bb33, 0x8d5048c3c75eed02, 0xdd1beb0c5abfec44, 0x2945ccf146e206eb}},
+ {{0xbcbbdbf1812a8285, 0x270e0807d0bdd1fc, 0xb41b670b1bbda72d, 0x43aabe696b3bb69a}}},
+{{{0xba6f2c9aaa3221b1, 0x6ca021533bba23a7, 0x9dea764f92192c3a, 0x1d6edd5d2e5317e0}},
+ {{0x6b1a5cd0944ea3bf, 0x7470353ab39dc0d2, 0x71b2528228542e49, 0x461bea69283c927e}},
+ {{0xf1836dc801b8b3a2, 0xb3035f47053ea49a, 0x529c41ba5877adf3, 0x7a9fbb1c6a0f90a7}}},
+{{{0xf36e217e039d8064, 0x98a081b6f520419b, 0x96cbc608e75eb044, 0x49c05a51fadc9c8f}},
+ {{0x9b2e678aa6a8632f, 0xa6509e6f51bc46c5, 0xceb233c9c686f5b5, 0x34b9ed338add7f59}},
+ {{0x06b4e8bf9045af1b, 0xe2ff83e8a719d22f, 0xaaf6fc2993d4cf16, 0x73c172021b008b06}}},
+{{{0x315f5b0249864348, 0x3ed6b36977088381, 0xa3a075556a8deb95, 0x18ab598029d5c77f}},
+ {{0x2fbf00848a802ade, 0xe5d9fecf02302e27, 0x113e847117703406, 0x4275aae2546d8faf}},
+ {{0xd82b2cc5fd6089e9, 0x031eb4a13282e4a4, 0x44311199b51a8622, 0x3dc65522b53df948}}},
+{{{0x506f013b327fbf93, 0xaefcebc99b776f6b, 0x9d12b232aaad5968, 0x0267882d176024a7}},
+ {{0xbf70c222a2007f6d, 0xbf84b39ab5bcdedb, 0x537a0e12fb07ba07, 0x234fd7eec346f241}},
+ {{0x5360a119732ea378, 0x2437e6b1df8dd471, 0xa2ef37f891a7e533, 0x497ba6fdaa097863}}},
+{{{0x040bcd86468ccf0b, 0xd3829ba42a9910d6, 0x7508300807b25192, 0x43b5cd4218d05ebf}},
+ {{0x24cecc0313cfeaa0, 0x8648c28d189c246d, 0x2dbdbdfac1f2d4d0, 0x61e22917f12de72b}},
+ {{0x5d9a762f9bd0b516, 0xeb38af4e373fdeee, 0x032e5a7d93d64270, 0x511d61210ae4d842}}},
+{{{0x081386484420de87, 0x8a1cf016b592edb4, 0x39fa4e2729942d25, 0x71a7fe6fe2482810}},
+ {{0x92c676ef950e9d81, 0xa54620cdc0d7044f, 0xaa9b36646f8f1248, 0x6d325924ddb855e3}},
+ {{0x6c7182b8a5c8c854, 0x33fd1479fe5f2a03, 0x72cf591883778d0c, 0x4746c4b6559eeaa9}}},
+{{{0x348546c864741147, 0x7d35aedd0efcc849, 0xff939a760672a332, 0x219663497db5e6d6}},
+ {{0xd3777b3c6dc69a2b, 0xdefab2276f89f617, 0x45651cf7b53a16b5, 0x5c9a51de34fe9fb7}},
+ {{0xf510f1cf79f10e67, 0xffdddaa1e658515b, 0x09c3a71710142277, 0x4804503c608223bb}}},
+{{{0x3b6821d23a36d175, 0xbbb40aa7e99b9e32, 0x5d9e5ce420838a47, 0x771e098858de4c5e}},
+ {{0xc4249ed02ca37fc7, 0xa059a0e3a615acab, 0x88a96ed7c96e0e23, 0x553398a51650696d}},
+ {{0x9a12f5d278451edf, 0x3ada5d7985899ccb, 0x477f4a2d9fa59508, 0x5a5ed1d68ff5a611}}},
+{{{0xbae5e0c558527359, 0x392e5c19cadb9d7e, 0x28653c1eda1cabe9, 0x019b60135fefdc44}},
+ {{0x1195122afe150e83, 0xcf209a257e4b35d8, 0x7387f8291e711e20, 0x44acb897d8bf92f0}},
+ {{0x1e6068145e134b83, 0xc4f5e64f24304c16, 0x506e88a8fc1a3ed7, 0x150c49fde6ad2f92}}},
+{{{0xb849863c9cdca868, 0xc83f44dbb8714ad0, 0xfe3ee3560c36168d, 0x78a6d7791e05fbc1}},
+ {{0x8e7bf29509471138, 0x5d6fef394f75a651, 0x10af79c425a708ad, 0x6b2b5a075bb99922}},
+ {{0x58bf704b47a0b976, 0xa601b355741748d5, 0xaa2b1fb1d542f590, 0x725c7ffc4ad55d00}}},
+{{{0x91802bf71cd098c0, 0xfe416ca4ed5e6366, 0xdf585d714902994c, 0x4cd54625f855fae7}},
+ {{0xe4426715d1cf99b2, 0x7352d51102a20d34, 0x23d1157b8b12109f, 0x794cc9277cb1f3a3}},
+ {{0x4af6c426c2ac5053, 0xbc9aedad32f67258, 0x2ad032f10a311021, 0x7008357b6fcc8e85}}},
+{{{0xd01b9fbb82584a34, 0x47ab6463d2b4792b, 0xb631639c48536202, 0x13a92a3669d6d428}},
+ {{0x0b88672738773f01, 0xb8ccc8fa95fbccfb, 0x8d2dd5a3b9ad29b6, 0x06ef7e9851ad0f6a}},
+ {{0xca93771cc0577de5, 0x7540e41e5035dc5c, 0x24680f01d802e071, 0x3c296ddf8a2af86a}}},
+{{{0xfceb4d2ebb1f2541, 0xb89510c740adb91f, 0xfc71a37dd0a1ad05, 0x0a892c700747717b}},
+ {{0xaead15f9d914a713, 0xa92f7bf98c8ff912, 0xaff823179f53d730, 0x7a99d393490c77ba}},
+ {{0x8f52ed2436bda3e8, 0x77a8c84157e80794, 0xa5a96563262f9ce0, 0x286762d28302f7d2}}},
+{{{0x7c558e2bce2ef5bd, 0xe4986cb46747bc63, 0x154a179f3bbb89b8, 0x7686f2a3d6f1767a}},
+ {{0x4e7836093ce35b25, 0x82e1181db26baa97, 0x0cc192d3cbc7b83f, 0x32f1da046a9d9d3a}},
+ {{0xaa8d12a66d597c6a, 0x8f11930304d3852b, 0x3f91dc73c209b022, 0x561305f8a9ad28a6}}},
+{{{0x6722cc28e7b0c0d5, 0x709de9bbdb075c53, 0xcaf68da7d7010a61, 0x030a1aef2c57cc6c}},
+ {{0x100c978dec92aed1, 0xca43d5434d6d73e5, 0x83131b22d847ba48, 0x00aaec53e35d4d2c}},
+ {{0x7bb1f773003ad2aa, 0x0b3f29802b216608, 0x7821dc86520ed23e, 0x20be9c1c24065480}}},
+{{{0x20e0e44ae2025e60, 0xb03b3b2fcbdcb938, 0x105d639cf95a0d1c, 0x69764c545067e311}},
+ {{0xe15387d8249673a6, 0x5943bc2df546e493, 0x1c7f9a81c36f63b5, 0x750ab3361f0ac1de}},
+ {{0x1e8a3283a2f81037, 0x6f2eda23bd7fcbf1, 0xb72fd15bac2e2563, 0x54f96b3fb7075040}}},
+{{{0x177dafc616b11ecd, 0x89764b9cfa576479, 0xb7a8a110e6ece785, 0x78e6839fbe85dbf0}},
+ {{0x0fadf20429669279, 0x3adda2047d7d724a, 0x6f3d94828c5760f1, 0x3d7fe9c52bb7539e}},
+ {{0x70332df737b8856b, 0x75d05d43041a178a, 0x320ff74aa0e59e22, 0x70f268f350088242}}},
+{{{0x2324112070dcf355, 0x380cc97ee7fce117, 0xb31ddeed3552b698, 0x404e56c039b8c4b9}},
+ {{0x66864583b1805f47, 0xf535c5d160dd7c19, 0xe9874eb71e4cb006, 0x7c0d345cfad889d9}},
+ {{0x591f1f4b8c78338a, 0xa0366ab167e0b5e1, 0x5cbc4152b45f3d44, 0x20d754762aaec777}}},
+{{{0x9d74feb135b9f543, 0x84b37df1de8c956c, 0xe9322b0757138ba9, 0x38b8ada8790b4ce1}},
+ {{0x5e8fc36fc73bb758, 0xace543a5363cbb9a, 0xa9934a7d903bc922, 0x2b8f1e46f3ceec62}},
+ {{0xb5c04a9cdf51f95d, 0x2b3952aecb1fdeac, 0x1d106d8b328b66da, 0x049aeb32ceba1953}}},
+{{{0xd7767d3c63dcfe7e, 0x209c594897856e40, 0xb6676861e14f7c13, 0x51c665e0c8d625fc}},
+ {{0xaa507d0b75fc7931, 0x0fef924b7a6725d3, 0x1d82542b396b3930, 0x795ee17530f674fc}},
+ {{0x254a5b0a52ecbd81, 0x5d411f6ee034afe7, 0xe6a24d0dcaee4a31, 0x6cd19bf49dc54477}}},
+{{{0x7e87619052179ca3, 0x571d0a060b2c9f85, 0x80a2baa88499711e, 0x7520f3db40b2e638}},
+ {{0x1ffe612165afc386, 0x082a2a88b8d51b10, 0x76f6627e20990baa, 0x5e01b3a7429e43e7}},
+ {{0x3db50be3d39357a1, 0x967b6cdd599e94a5, 0x1a309a64df311e6e, 0x71092c9ccef3c986}}},
+{{{0x53d8523f0364918c, 0xa2b404f43fab6b1c, 0x080b4a9e6681e5a4, 0x0ea15b03d0257ba7}},
+ {{0x856bd8ac74051dcf, 0x03f6a40855b7aa1e, 0x3a4ae7cbc9743ceb, 0x4173a5bb7137abde}},
+ {{0x17c56e31f0f9218a, 0x5a696e2b1afc4708, 0xf7931668f4b2f176, 0x5fc565614a4e3a67}}},
+{{{0x136e570dc46d7ae5, 0x0fd0aacc54f8dc8f, 0x59549f03310dad86, 0x62711c414c454aa1}},
+ {{0x4892e1e67790988e, 0x01d5950f1c5cd722, 0xe3b0819ae5923eed, 0x3214c7409d46651b}},
+ {{0x1329827406651770, 0x3ba4a0668a279436, 0xd9b6b8ec185d223c, 0x5bea94073ecb833c}}},
+{{{0x641dbf0912c89be4, 0xacf38b317d6e579c, 0xabfe9e02f697b065, 0x3aacd5c148f61eec}},
+ {{0xb470ce63f343d2f8, 0x0067ba8f0543e8f1, 0x35da51a1a2117b6f, 0x4ad0785944f1bd2f}},
+ {{0x858e3b34c3318301, 0xdc99c04707316826, 0x34085b2ed39da88c, 0x3aff0cb1d902853d}}},
+{{{0x87c5c7eb3a20405e, 0x8ee311efedad56c9, 0x29252e48ad29d5f9, 0x110e7e86f4cd251d}},
+ {{0x9226430bf4c53505, 0x68e49c13261f2283, 0x09ef33788fd327c6, 0x2ccf9f732bd99e7f}},
+ {{0x57c0d89ed603f5e4, 0x12888628f0b0200c, 0x53172709a02e3bb7, 0x05c557e0b9693a37}}},
+{{{0xd8f9ce311fc97e6f, 0x7a3f263011f9fdae, 0xe15b7ea08bed25dd, 0x6e154c178fe9875a}},
+ {{0xf776bbb089c20eb0, 0x61f85bf6fa0fd85c, 0xb6b93f4e634421fb, 0x289fef0841861205}},
+ {{0xcf616336fed69abf, 0x9b16e4e78335c94f, 0x13789765753a7fe7, 0x6afbf642a95ca319}}},
+{{{0x7da8de0c62f5d2c1, 0x98fc3da4b00e7b9a, 0x7deb6ada0dad70e0, 0x0db4b851b95038c4}},
+ {{0x5de55070f913a8cc, 0x7d1d167b2b0cf561, 0xda2956b690ead489, 0x12c093cedb801ed9}},
+ {{0xfc147f9308b8190f, 0x06969da0a11ae310, 0xcee75572dac7d7fd, 0x33aa8799c6635ce6}}},
+{{{0xaf0ff51ebd085cf2, 0x78f51a8967d33f1f, 0x6ec2bfe15060033c, 0x233c6f29e8e21a86}},
+ {{0x8348f588fc156cb1, 0x6da2ba9b1a0a6d27, 0xe2262d5c87ca5ab6, 0x212cd0c1c8d589a6}},
+ {{0xd2f4d5107f18c781, 0x122ecdf2527e9d28, 0xa70a862a3d3d3341, 0x1db7778911914ce3}}},
+{{{0xddf352397c6bc26f, 0x7a97e2cc53d50113, 0x7c74f43abf79a330, 0x31ad97ad26e2adfc}},
+ {{0xb3394769dd701ab6, 0xe2b8ded419cf8da5, 0x15df4161fd2ac852, 0x7ae2ca8a017d24be}},
+ {{0xb7e817ed0920b962, 0x1e8518cc3f19da9d, 0xe491c14f25560a64, 0x1ed1fc53a6622c83}}} \ No newline at end of file
diff --git a/ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s b/ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s
new file mode 100644
index 00000000..265daf09
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_dbl_p1p1.s
@@ -0,0 +1,2975 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 e0
+
+# qhasm: int64 e1
+
+# qhasm: int64 e2
+
+# qhasm: int64 e3
+
+# qhasm: stack64 e0_stack
+
+# qhasm: stack64 e1_stack
+
+# qhasm: stack64 e2_stack
+
+# qhasm: stack64 e3_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: stack64 rx0_stack
+
+# qhasm: stack64 rx1_stack
+
+# qhasm: stack64 rx2_stack
+
+# qhasm: stack64 rx3_stack
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 ry4
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 squarer4
+
+# qhasm: int64 squarer5
+
+# qhasm: int64 squarer6
+
+# qhasm: int64 squarer7
+
+# qhasm: int64 squarer8
+
+# qhasm: int64 squarerax
+
+# qhasm: int64 squarerdx
+
+# qhasm: int64 squaret1
+
+# qhasm: int64 squaret2
+
+# qhasm: int64 squaret3
+
+# qhasm: int64 squarec
+
+# qhasm: int64 squarezero
+
+# qhasm: int64 squarei38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_dbl_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $192,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<pp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: a1 = squarerax
+# asm 1: mov <squarerax=int64#7,>a1=int64#5
+# asm 2: mov <squarerax=%rax,>a1=%r8
+mov %rax,%r8
+
+# qhasm: a2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>a2=int64#6
+# asm 2: mov <squarerdx=%rdx,>a2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<pp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 8)
+# asm 1: mulq 8(<pp=int64#2)
+# asm 2: mulq 8(<pp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: a3 = squarerax
+# asm 1: mov <squarerax=int64#7,>a3=int64#8
+# asm 2: mov <squarerax=%rax,>a3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 16)
+# asm 1: mulq 16(<pp=int64#2)
+# asm 2: mulq 16(<pp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<pp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? a2 += squarerax
+# asm 1: add <squarerax=int64#7,<a2=int64#6
+# asm 2: add <squarerax=%rax,<a2=%r9
+add %rax,%r9
+
+# qhasm: carry? a3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<a3=int64#8
+# asm 2: adc <squarerdx=%rdx,<a3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 8)
+# asm 1: mulq 8(<pp=int64#2)
+# asm 2: mulq 8(<pp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: carry? a3 += squarerax
+# asm 1: add <squarerax=int64#7,<a3=int64#8
+# asm 2: add <squarerax=%rax,<a3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? a1 += a1
+# asm 1: add <a1=int64#5,<a1=int64#5
+# asm 2: add <a1=%r8,<a1=%r8
+add %r8,%r8
+
+# qhasm: carry? a2 += a2 + carry
+# asm 1: adc <a2=int64#6,<a2=int64#6
+# asm 2: adc <a2=%r9,<a2=%r9
+adc %r9,%r9
+
+# qhasm: carry? a3 += a3 + carry
+# asm 1: adc <a3=int64#8,<a3=int64#8
+# asm 2: adc <a3=%r10,<a3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 0(<pp=%rsi),>squarerax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 0)
+# asm 1: mulq 0(<pp=int64#2)
+# asm 2: mulq 0(<pp=%rsi)
+mulq 0(%rsi)
+
+# qhasm: a0 = squarerax
+# asm 1: mov <squarerax=int64#7,>a0=int64#12
+# asm 2: mov <squarerax=%rax,>a0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 8(<pp=%rsi),>squarerax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 8)
+# asm 1: mulq 8(<pp=int64#2)
+# asm 2: mulq 8(<pp=%rsi)
+mulq 8(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 16(<pp=%rsi),>squarerax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 16)
+# asm 1: mulq 16(<pp=int64#2)
+# asm 2: mulq 16(<pp=%rsi)
+mulq 16(%rsi)
+
+# qhasm: carry? a1 += squaret1
+# asm 1: add <squaret1=int64#13,<a1=int64#5
+# asm 2: add <squaret1=%r15,<a1=%r8
+add %r15,%r8
+
+# qhasm: carry? a2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<a2=int64#6
+# asm 2: adc <squaret2=%rbx,<a2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? a3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<a3=int64#8
+# asm 2: adc <squaret3=%rbp,<a3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 24(<pp=%rsi),>squarerax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 24)
+# asm 1: mulq 24(<pp=int64#2)
+# asm 2: mulq 24(<pp=%rsi)
+mulq 24(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#9
+# asm 2: mov <squarerax=%rax,>squarer4=%r11
+mov %rax,%r11
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r12
+mov %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#10
+# asm 2: add <squarerax=%rax,<squarer5=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#11
+# asm 2: mov $0,>squarer6=%r13
+mov $0,%r13
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#11
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r13
+adc %rdx,%r13
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += squarer4
+# asm 1: add <squarer4=int64#9,<a0=int64#12
+# asm 2: add <squarer4=%r11,<a0=%r14
+add %r11,%r14
+
+# qhasm: carry? a1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<a1=int64#5
+# asm 2: adc <squarer5=%r12,<a1=%r8
+adc %r12,%r8
+
+# qhasm: carry? a2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<a2=int64#6
+# asm 2: adc <squarer6=%r13,<a2=%r9
+adc %r13,%r9
+
+# qhasm: carry? a3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<a3=int64#8
+# asm 2: adc <squarer7=%rcx,<a3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#3
+# asm 2: mov $0,>squarezero=%rdx
+mov $0,%rdx
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarer8=int64#7
+# asm 2: adc <squarezero=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#4
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? a0 += squarer8
+# asm 1: add <squarer8=int64#4,<a0=int64#12
+# asm 2: add <squarer8=%rcx,<a0=%r14
+add %rcx,%r14
+
+# qhasm: carry? a1 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<a1=int64#5
+# asm 2: adc <squarezero=%rdx,<a1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? a2 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<a2=int64#6
+# asm 2: adc <squarezero=%rdx,<a2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? a3 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<a3=int64#8
+# asm 2: adc <squarezero=%rdx,<a3=%r10
+adc %rdx,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarezero=int64#3
+# asm 2: adc <squarezero=%rdx,<squarezero=%rdx
+adc %rdx,%rdx
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#3,>squarezero=int64#3
+# asm 2: imulq $38,<squarezero=%rdx,>squarezero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += squarezero
+# asm 1: add <squarezero=int64#3,<a0=int64#12
+# asm 2: add <squarezero=%rdx,<a0=%r14
+add %rdx,%r14
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#12,>a0_stack=stack64#8
+# asm 2: movq <a0=%r14,>a0_stack=56(%rsp)
+movq %r14,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#8,>a3_stack=stack64#11
+# asm 2: movq <a3=%r10,>a3_stack=80(%rsp)
+movq %r10,80(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 40(<pp=%rsi),>squarerax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: b1 = squarerax
+# asm 1: mov <squarerax=int64#7,>b1=int64#5
+# asm 2: mov <squarerax=%rax,>b1=%r8
+mov %rax,%r8
+
+# qhasm: b2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>b2=int64#6
+# asm 2: mov <squarerdx=%rdx,>b2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 48(<pp=%rsi),>squarerax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 40)
+# asm 1: mulq 40(<pp=int64#2)
+# asm 2: mulq 40(<pp=%rsi)
+mulq 40(%rsi)
+
+# qhasm: b3 = squarerax
+# asm 1: mov <squarerax=int64#7,>b3=int64#8
+# asm 2: mov <squarerax=%rax,>b3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 48)
+# asm 1: mulq 48(<pp=int64#2)
+# asm 2: mulq 48(<pp=%rsi)
+mulq 48(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 48(<pp=%rsi),>squarerax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: carry? b2 += squarerax
+# asm 1: add <squarerax=int64#7,<b2=int64#6
+# asm 2: add <squarerax=%rax,<b2=%r9
+add %rax,%r9
+
+# qhasm: carry? b3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<b3=int64#8
+# asm 2: adc <squarerdx=%rdx,<b3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 40)
+# asm 1: mulq 40(<pp=int64#2)
+# asm 2: mulq 40(<pp=%rsi)
+mulq 40(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: carry? b3 += squarerax
+# asm 1: add <squarerax=int64#7,<b3=int64#8
+# asm 2: add <squarerax=%rax,<b3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? b1 += b1
+# asm 1: add <b1=int64#5,<b1=int64#5
+# asm 2: add <b1=%r8,<b1=%r8
+add %r8,%r8
+
+# qhasm: carry? b2 += b2 + carry
+# asm 1: adc <b2=int64#6,<b2=int64#6
+# asm 2: adc <b2=%r9,<b2=%r9
+adc %r9,%r9
+
+# qhasm: carry? b3 += b3 + carry
+# asm 1: adc <b3=int64#8,<b3=int64#8
+# asm 2: adc <b3=%r10,<b3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 32(<pp=%rsi),>squarerax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 32)
+# asm 1: mulq 32(<pp=int64#2)
+# asm 2: mulq 32(<pp=%rsi)
+mulq 32(%rsi)
+
+# qhasm: b0 = squarerax
+# asm 1: mov <squarerax=int64#7,>b0=int64#12
+# asm 2: mov <squarerax=%rax,>b0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 40(<pp=%rsi),>squarerax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 40)
+# asm 1: mulq 40(<pp=int64#2)
+# asm 2: mulq 40(<pp=%rsi)
+mulq 40(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 48(<pp=%rsi),>squarerax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 48)
+# asm 1: mulq 48(<pp=int64#2)
+# asm 2: mulq 48(<pp=%rsi)
+mulq 48(%rsi)
+
+# qhasm: carry? b1 += squaret1
+# asm 1: add <squaret1=int64#13,<b1=int64#5
+# asm 2: add <squaret1=%r15,<b1=%r8
+add %r15,%r8
+
+# qhasm: carry? b2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<b2=int64#6
+# asm 2: adc <squaret2=%rbx,<b2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? b3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<b3=int64#8
+# asm 2: adc <squaret3=%rbp,<b3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 56(<pp=%rsi),>squarerax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 56)
+# asm 1: mulq 56(<pp=int64#2)
+# asm 2: mulq 56(<pp=%rsi)
+mulq 56(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#9
+# asm 2: mov <squarerax=%rax,>squarer4=%r11
+mov %rax,%r11
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r12
+mov %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#10
+# asm 2: add <squarerax=%rax,<squarer5=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#11
+# asm 2: mov $0,>squarer6=%r13
+mov $0,%r13
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#11
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r13
+adc %rdx,%r13
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? b0 += squarer4
+# asm 1: add <squarer4=int64#9,<b0=int64#12
+# asm 2: add <squarer4=%r11,<b0=%r14
+add %r11,%r14
+
+# qhasm: carry? b1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<b1=int64#5
+# asm 2: adc <squarer5=%r12,<b1=%r8
+adc %r12,%r8
+
+# qhasm: carry? b2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<b2=int64#6
+# asm 2: adc <squarer6=%r13,<b2=%r9
+adc %r13,%r9
+
+# qhasm: carry? b3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<b3=int64#8
+# asm 2: adc <squarer7=%rcx,<b3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#3
+# asm 2: mov $0,>squarezero=%rdx
+mov $0,%rdx
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarer8=int64#7
+# asm 2: adc <squarezero=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#4
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? b0 += squarer8
+# asm 1: add <squarer8=int64#4,<b0=int64#12
+# asm 2: add <squarer8=%rcx,<b0=%r14
+add %rcx,%r14
+
+# qhasm: carry? b1 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<b1=int64#5
+# asm 2: adc <squarezero=%rdx,<b1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? b2 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<b2=int64#6
+# asm 2: adc <squarezero=%rdx,<b2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? b3 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<b3=int64#8
+# asm 2: adc <squarezero=%rdx,<b3=%r10
+adc %rdx,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarezero=int64#3
+# asm 2: adc <squarezero=%rdx,<squarezero=%rdx
+adc %rdx,%rdx
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#3,>squarezero=int64#3
+# asm 2: imulq $38,<squarezero=%rdx,>squarezero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: b0 += squarezero
+# asm 1: add <squarezero=int64#3,<b0=int64#12
+# asm 2: add <squarezero=%rdx,<b0=%r14
+add %rdx,%r14
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#12,>b0_stack=stack64#12
+# asm 2: movq <b0=%r14,>b0_stack=88(%rsp)
+movq %r14,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#5,>b1_stack=stack64#13
+# asm 2: movq <b1=%r8,>b1_stack=96(%rsp)
+movq %r8,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#6,>b2_stack=stack64#14
+# asm 2: movq <b2=%r9,>b2_stack=104(%rsp)
+movq %r9,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#8,>b3_stack=stack64#15
+# asm 2: movq <b3=%r10,>b3_stack=112(%rsp)
+movq %r10,112(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 72(<pp=%rsi),>squarerax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: c1 = squarerax
+# asm 1: mov <squarerax=int64#7,>c1=int64#5
+# asm 2: mov <squarerax=%rax,>c1=%r8
+mov %rax,%r8
+
+# qhasm: c2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>c2=int64#6
+# asm 2: mov <squarerdx=%rdx,>c2=%r9
+mov %rdx,%r9
+
+# qhasm: squarerax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 80(<pp=%rsi),>squarerax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 72)
+# asm 1: mulq 72(<pp=int64#2)
+# asm 2: mulq 72(<pp=%rsi)
+mulq 72(%rsi)
+
+# qhasm: c3 = squarerax
+# asm 1: mov <squarerax=int64#7,>c3=int64#8
+# asm 2: mov <squarerax=%rax,>c3=%r10
+mov %rax,%r10
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r11
+mov %rdx,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 80)
+# asm 1: mulq 80(<pp=int64#2)
+# asm 2: mulq 80(<pp=%rsi)
+mulq 80(%rsi)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#10
+# asm 2: mov <squarerax=%rax,>squarer5=%r12
+mov %rax,%r12
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#11
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r13
+mov %rdx,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 80(<pp=%rsi),>squarerax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: carry? c2 += squarerax
+# asm 1: add <squarerax=int64#7,<c2=int64#6
+# asm 2: add <squarerax=%rax,<c2=%r9
+add %rax,%r9
+
+# qhasm: carry? c3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<c3=int64#8
+# asm 2: adc <squarerdx=%rdx,<c3=%r10
+adc %rdx,%r10
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#9
+# asm 2: adc $0,<squarer4=%r11
+adc $0,%r11
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 72)
+# asm 1: mulq 72(<pp=int64#2)
+# asm 2: mulq 72(<pp=%rsi)
+mulq 72(%rsi)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#9
+# asm 2: add <squarerax=%rax,<squarer4=%r11
+add %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: carry? c3 += squarerax
+# asm 1: add <squarerax=int64#7,<c3=int64#8
+# asm 2: add <squarerax=%rax,<c3=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#10
+# asm 2: adc $0,<squarer5=%r12
+adc $0,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: carry? c1 += c1
+# asm 1: add <c1=int64#5,<c1=int64#5
+# asm 2: add <c1=%r8,<c1=%r8
+add %r8,%r8
+
+# qhasm: carry? c2 += c2 + carry
+# asm 1: adc <c2=int64#6,<c2=int64#6
+# asm 2: adc <c2=%r9,<c2=%r9
+adc %r9,%r9
+
+# qhasm: carry? c3 += c3 + carry
+# asm 1: adc <c3=int64#8,<c3=int64#8
+# asm 2: adc <c3=%r10,<c3=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#9,<squarer4=int64#9
+# asm 2: adc <squarer4=%r11,<squarer4=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<squarer5=int64#10
+# asm 2: adc <squarer5=%r12,<squarer5=%r12
+adc %r12,%r12
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<squarer6=int64#11
+# asm 2: adc <squarer6=%r13,<squarer6=%r13
+adc %r13,%r13
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<squarer7=int64#4
+# asm 2: adc <squarer7=%rcx,<squarer7=%rcx
+adc %rcx,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 64(<pp=%rsi),>squarerax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 64)
+# asm 1: mulq 64(<pp=int64#2)
+# asm 2: mulq 64(<pp=%rsi)
+mulq 64(%rsi)
+
+# qhasm: c0 = squarerax
+# asm 1: mov <squarerax=int64#7,>c0=int64#12
+# asm 2: mov <squarerax=%rax,>c0=%r14
+mov %rax,%r14
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#13
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r15
+mov %rdx,%r15
+
+# qhasm: squarerax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 72(<pp=%rsi),>squarerax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 72)
+# asm 1: mulq 72(<pp=int64#2)
+# asm 2: mulq 72(<pp=%rsi)
+mulq 72(%rsi)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#14
+# asm 2: mov <squarerax=%rax,>squaret2=%rbx
+mov %rax,%rbx
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#15
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbp
+mov %rdx,%rbp
+
+# qhasm: squarerax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 80(<pp=%rsi),>squarerax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 80)
+# asm 1: mulq 80(<pp=int64#2)
+# asm 2: mulq 80(<pp=%rsi)
+mulq 80(%rsi)
+
+# qhasm: carry? c1 += squaret1
+# asm 1: add <squaret1=int64#13,<c1=int64#5
+# asm 2: add <squaret1=%r15,<c1=%r8
+add %r15,%r8
+
+# qhasm: carry? c2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#14,<c2=int64#6
+# asm 2: adc <squaret2=%rbx,<c2=%r9
+adc %rbx,%r9
+
+# qhasm: carry? c3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#15,<c3=int64#8
+# asm 2: adc <squaret3=%rbp,<c3=%r10
+adc %rbp,%r10
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#9
+# asm 2: adc <squarerax=%rax,<squarer4=%r11
+adc %rax,%r11
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r12
+adc %rdx,%r12
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#11
+# asm 2: adc $0,<squarer6=%r13
+adc $0,%r13
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#4
+# asm 2: adc $0,<squarer7=%rcx
+adc $0,%rcx
+
+# qhasm: squarerax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>squarerax=int64#7
+# asm 2: movq 88(<pp=%rsi),>squarerax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)(pp + 88)
+# asm 1: mulq 88(<pp=int64#2)
+# asm 2: mulq 88(<pp=%rsi)
+mulq 88(%rsi)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer4=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#9
+# asm 2: mov <squarerax=%rax,>squarer4=%r11
+mov %rax,%r11
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer5=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r12
+mov %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#10
+# asm 2: add <squarerax=%rax,<squarer5=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#11,>squarerax=int64#7
+# asm 2: mov <squarer6=%r13,>squarerax=%rax
+mov %r13,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#11
+# asm 2: mov $0,>squarer6=%r13
+mov $0,%r13
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#11
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r13
+adc %rdx,%r13
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#11
+# asm 2: add <squarerax=%rax,<squarer6=%r13
+add %rax,%r13
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#4,>squarerax=int64#7
+# asm 2: mov <squarer7=%rcx,>squarerax=%rax
+mov %rcx,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#4
+# asm 2: mov $0,>squarer7=%rcx
+mov $0,%rcx
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#4
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rcx
+adc %rdx,%rcx
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#4
+# asm 2: add <squarerax=%rax,<squarer7=%rcx
+add %rax,%rcx
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += squarer4
+# asm 1: add <squarer4=int64#9,<c0=int64#12
+# asm 2: add <squarer4=%r11,<c0=%r14
+add %r11,%r14
+
+# qhasm: carry? c1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#10,<c1=int64#5
+# asm 2: adc <squarer5=%r12,<c1=%r8
+adc %r12,%r8
+
+# qhasm: carry? c2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#11,<c2=int64#6
+# asm 2: adc <squarer6=%r13,<c2=%r9
+adc %r13,%r9
+
+# qhasm: carry? c3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#4,<c3=int64#8
+# asm 2: adc <squarer7=%rcx,<c3=%r10
+adc %rcx,%r10
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#3
+# asm 2: mov $0,>squarezero=%rdx
+mov $0,%rdx
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarer8=int64#7
+# asm 2: adc <squarezero=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#4
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? c0 += squarer8
+# asm 1: add <squarer8=int64#4,<c0=int64#12
+# asm 2: add <squarer8=%rcx,<c0=%r14
+add %rcx,%r14
+
+# qhasm: carry? c1 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<c1=int64#5
+# asm 2: adc <squarezero=%rdx,<c1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? c2 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<c2=int64#6
+# asm 2: adc <squarezero=%rdx,<c2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? c3 += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<c3=int64#8
+# asm 2: adc <squarezero=%rdx,<c3=%r10
+adc %rdx,%r10
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#3,<squarezero=int64#3
+# asm 2: adc <squarezero=%rdx,<squarezero=%rdx
+adc %rdx,%rdx
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#3,>squarezero=int64#3
+# asm 2: imulq $38,<squarezero=%rdx,>squarezero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += squarezero
+# asm 1: add <squarezero=int64#3,<c0=int64#12
+# asm 2: add <squarezero=%rdx,<c0=%r14
+add %rdx,%r14
+
+# qhasm: carry? c0 += c0
+# asm 1: add <c0=int64#12,<c0=int64#12
+# asm 2: add <c0=%r14,<c0=%r14
+add %r14,%r14
+
+# qhasm: carry? c1 += c1 + carry
+# asm 1: adc <c1=int64#5,<c1=int64#5
+# asm 2: adc <c1=%r8,<c1=%r8
+adc %r8,%r8
+
+# qhasm: carry? c2 += c2 + carry
+# asm 1: adc <c2=int64#6,<c2=int64#6
+# asm 2: adc <c2=%r9,<c2=%r9
+adc %r9,%r9
+
+# qhasm: carry? c3 += c3 + carry
+# asm 1: adc <c3=int64#8,<c3=int64#8
+# asm 2: adc <c3=%r10,<c3=%r10
+adc %r10,%r10
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#3
+# asm 2: mov $0,>addt0=%rdx
+mov $0,%rdx
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#4
+# asm 2: mov $38,>addt1=%rcx
+mov $38,%rcx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#3,<addt1=int64#4
+# asm 2: cmovae <addt0=%rdx,<addt1=%rcx
+cmovae %rdx,%rcx
+
+# qhasm: carry? c0 += addt1
+# asm 1: add <addt1=int64#4,<c0=int64#12
+# asm 2: add <addt1=%rcx,<c0=%r14
+add %rcx,%r14
+
+# qhasm: carry? c1 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<c1=int64#5
+# asm 2: adc <addt0=%rdx,<c1=%r8
+adc %rdx,%r8
+
+# qhasm: carry? c2 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<c2=int64#6
+# asm 2: adc <addt0=%rdx,<c2=%r9
+adc %rdx,%r9
+
+# qhasm: carry? c3 += addt0 + carry
+# asm 1: adc <addt0=int64#3,<c3=int64#8
+# asm 2: adc <addt0=%rdx,<c3=%r10
+adc %rdx,%r10
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#4,<addt0=int64#3
+# asm 2: cmovc <addt1=%rcx,<addt0=%rdx
+cmovc %rcx,%rdx
+
+# qhasm: c0 += addt0
+# asm 1: add <addt0=int64#3,<c0=int64#12
+# asm 2: add <addt0=%rdx,<c0=%r14
+add %rdx,%r14
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#12,>c0_stack=stack64#16
+# asm 2: movq <c0=%r14,>c0_stack=120(%rsp)
+movq %r14,120(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#5,>c1_stack=stack64#17
+# asm 2: movq <c1=%r8,>c1_stack=128(%rsp)
+movq %r8,128(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#6,>c2_stack=stack64#18
+# asm 2: movq <c2=%r9,>c2_stack=136(%rsp)
+movq %r9,136(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#8,>c3_stack=stack64#19
+# asm 2: movq <c3=%r10,>c3_stack=144(%rsp)
+movq %r10,144(%rsp)
+
+# qhasm: d0 = 0
+# asm 1: mov $0,>d0=int64#3
+# asm 2: mov $0,>d0=%rdx
+mov $0,%rdx
+
+# qhasm: d1 = 0
+# asm 1: mov $0,>d1=int64#4
+# asm 2: mov $0,>d1=%rcx
+mov $0,%rcx
+
+# qhasm: d2 = 0
+# asm 1: mov $0,>d2=int64#5
+# asm 2: mov $0,>d2=%r8
+mov $0,%r8
+
+# qhasm: d3 = 0
+# asm 1: mov $0,>d3=int64#6
+# asm 2: mov $0,>d3=%r9
+mov $0,%r9
+
+# qhasm: carry? d0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<d0=int64#3
+# asm 2: subq <a0_stack=56(%rsp),<d0=%rdx
+subq 56(%rsp),%rdx
+
+# qhasm: carry? d1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<d1=int64#4
+# asm 2: sbbq <a1_stack=64(%rsp),<d1=%rcx
+sbbq 64(%rsp),%rcx
+
+# qhasm: carry? d2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<d2=int64#5
+# asm 2: sbbq <a2_stack=72(%rsp),<d2=%r8
+sbbq 72(%rsp),%r8
+
+# qhasm: carry? d3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<d3=int64#6
+# asm 2: sbbq <a3_stack=80(%rsp),<d3=%r9
+sbbq 80(%rsp),%r9
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#7
+# asm 2: mov $0,>subt0=%rax
+mov $0,%rax
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#8
+# asm 2: mov $38,>subt1=%r10
+mov $38,%r10
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#7,<subt1=int64#8
+# asm 2: cmovae <subt0=%rax,<subt1=%r10
+cmovae %rax,%r10
+
+# qhasm: carry? d0 -= subt1
+# asm 1: sub <subt1=int64#8,<d0=int64#3
+# asm 2: sub <subt1=%r10,<d0=%rdx
+sub %r10,%rdx
+
+# qhasm: carry? d1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<d1=int64#4
+# asm 2: sbb <subt0=%rax,<d1=%rcx
+sbb %rax,%rcx
+
+# qhasm: carry? d2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<d2=int64#5
+# asm 2: sbb <subt0=%rax,<d2=%r8
+sbb %rax,%r8
+
+# qhasm: carry? d3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<d3=int64#6
+# asm 2: sbb <subt0=%rax,<d3=%r9
+sbb %rax,%r9
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#8,<subt0=int64#7
+# asm 2: cmovc <subt1=%r10,<subt0=%rax
+cmovc %r10,%rax
+
+# qhasm: d0 -= subt0
+# asm 1: sub <subt0=int64#7,<d0=int64#3
+# asm 2: sub <subt0=%rax,<d0=%rdx
+sub %rax,%rdx
+
+# qhasm: d0_stack = d0
+# asm 1: movq <d0=int64#3,>d0_stack=stack64#8
+# asm 2: movq <d0=%rdx,>d0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: d1_stack = d1
+# asm 1: movq <d1=int64#4,>d1_stack=stack64#9
+# asm 2: movq <d1=%rcx,>d1_stack=64(%rsp)
+movq %rcx,64(%rsp)
+
+# qhasm: d2_stack = d2
+# asm 1: movq <d2=int64#5,>d2_stack=stack64#10
+# asm 2: movq <d2=%r8,>d2_stack=72(%rsp)
+movq %r8,72(%rsp)
+
+# qhasm: d3_stack = d3
+# asm 1: movq <d3=int64#6,>d3_stack=stack64#11
+# asm 2: movq <d3=%r9,>d3_stack=80(%rsp)
+movq %r9,80(%rsp)
+
+# qhasm: e0 = 0
+# asm 1: mov $0,>e0=int64#7
+# asm 2: mov $0,>e0=%rax
+mov $0,%rax
+
+# qhasm: e1 = 0
+# asm 1: mov $0,>e1=int64#8
+# asm 2: mov $0,>e1=%r10
+mov $0,%r10
+
+# qhasm: e2 = 0
+# asm 1: mov $0,>e2=int64#9
+# asm 2: mov $0,>e2=%r11
+mov $0,%r11
+
+# qhasm: e3 = 0
+# asm 1: mov $0,>e3=int64#10
+# asm 2: mov $0,>e3=%r12
+mov $0,%r12
+
+# qhasm: carry? e0 -= b0_stack
+# asm 1: subq <b0_stack=stack64#12,<e0=int64#7
+# asm 2: subq <b0_stack=88(%rsp),<e0=%rax
+subq 88(%rsp),%rax
+
+# qhasm: carry? e1 -= b1_stack - carry
+# asm 1: sbbq <b1_stack=stack64#13,<e1=int64#8
+# asm 2: sbbq <b1_stack=96(%rsp),<e1=%r10
+sbbq 96(%rsp),%r10
+
+# qhasm: carry? e2 -= b2_stack - carry
+# asm 1: sbbq <b2_stack=stack64#14,<e2=int64#9
+# asm 2: sbbq <b2_stack=104(%rsp),<e2=%r11
+sbbq 104(%rsp),%r11
+
+# qhasm: carry? e3 -= b3_stack - carry
+# asm 1: sbbq <b3_stack=stack64#15,<e3=int64#10
+# asm 2: sbbq <b3_stack=112(%rsp),<e3=%r12
+sbbq 112(%rsp),%r12
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#11
+# asm 2: mov $0,>subt0=%r13
+mov $0,%r13
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#12
+# asm 2: mov $38,>subt1=%r14
+mov $38,%r14
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#11,<subt1=int64#12
+# asm 2: cmovae <subt0=%r13,<subt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? e0 -= subt1
+# asm 1: sub <subt1=int64#12,<e0=int64#7
+# asm 2: sub <subt1=%r14,<e0=%rax
+sub %r14,%rax
+
+# qhasm: carry? e1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<e1=int64#8
+# asm 2: sbb <subt0=%r13,<e1=%r10
+sbb %r13,%r10
+
+# qhasm: carry? e2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<e2=int64#9
+# asm 2: sbb <subt0=%r13,<e2=%r11
+sbb %r13,%r11
+
+# qhasm: carry? e3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<e3=int64#10
+# asm 2: sbb <subt0=%r13,<e3=%r12
+sbb %r13,%r12
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#12,<subt0=int64#11
+# asm 2: cmovc <subt1=%r14,<subt0=%r13
+cmovc %r14,%r13
+
+# qhasm: e0 -= subt0
+# asm 1: sub <subt0=int64#11,<e0=int64#7
+# asm 2: sub <subt0=%r13,<e0=%rax
+sub %r13,%rax
+
+# qhasm: e0_stack = e0
+# asm 1: movq <e0=int64#7,>e0_stack=stack64#20
+# asm 2: movq <e0=%rax,>e0_stack=152(%rsp)
+movq %rax,152(%rsp)
+
+# qhasm: e1_stack = e1
+# asm 1: movq <e1=int64#8,>e1_stack=stack64#21
+# asm 2: movq <e1=%r10,>e1_stack=160(%rsp)
+movq %r10,160(%rsp)
+
+# qhasm: e2_stack = e2
+# asm 1: movq <e2=int64#9,>e2_stack=stack64#22
+# asm 2: movq <e2=%r11,>e2_stack=168(%rsp)
+movq %r11,168(%rsp)
+
+# qhasm: e3_stack = e3
+# asm 1: movq <e3=int64#10,>e3_stack=stack64#23
+# asm 2: movq <e3=%r12,>e3_stack=176(%rsp)
+movq %r12,176(%rsp)
+
+# qhasm: rz0 = d0
+# asm 1: mov <d0=int64#3,>rz0=int64#7
+# asm 2: mov <d0=%rdx,>rz0=%rax
+mov %rdx,%rax
+
+# qhasm: rz1 = d1
+# asm 1: mov <d1=int64#4,>rz1=int64#8
+# asm 2: mov <d1=%rcx,>rz1=%r10
+mov %rcx,%r10
+
+# qhasm: rz2 = d2
+# asm 1: mov <d2=int64#5,>rz2=int64#9
+# asm 2: mov <d2=%r8,>rz2=%r11
+mov %r8,%r11
+
+# qhasm: rz3 = d3
+# asm 1: mov <d3=int64#6,>rz3=int64#10
+# asm 2: mov <d3=%r9,>rz3=%r12
+mov %r9,%r12
+
+# qhasm: carry? rz0 += b0_stack
+# asm 1: addq <b0_stack=stack64#12,<rz0=int64#7
+# asm 2: addq <b0_stack=88(%rsp),<rz0=%rax
+addq 88(%rsp),%rax
+
+# qhasm: carry? rz1 += b1_stack + carry
+# asm 1: adcq <b1_stack=stack64#13,<rz1=int64#8
+# asm 2: adcq <b1_stack=96(%rsp),<rz1=%r10
+adcq 96(%rsp),%r10
+
+# qhasm: carry? rz2 += b2_stack + carry
+# asm 1: adcq <b2_stack=stack64#14,<rz2=int64#9
+# asm 2: adcq <b2_stack=104(%rsp),<rz2=%r11
+adcq 104(%rsp),%r11
+
+# qhasm: carry? rz3 += b3_stack + carry
+# asm 1: adcq <b3_stack=stack64#15,<rz3=int64#10
+# asm 2: adcq <b3_stack=112(%rsp),<rz3=%r12
+adcq 112(%rsp),%r12
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#11
+# asm 2: mov $0,>addt0=%r13
+mov $0,%r13
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#12
+# asm 2: mov $38,>addt1=%r14
+mov $38,%r14
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#11,<addt1=int64#12
+# asm 2: cmovae <addt0=%r13,<addt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? rz0 += addt1
+# asm 1: add <addt1=int64#12,<rz0=int64#7
+# asm 2: add <addt1=%r14,<rz0=%rax
+add %r14,%rax
+
+# qhasm: carry? rz1 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<rz1=int64#8
+# asm 2: adc <addt0=%r13,<rz1=%r10
+adc %r13,%r10
+
+# qhasm: carry? rz2 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<rz2=int64#9
+# asm 2: adc <addt0=%r13,<rz2=%r11
+adc %r13,%r11
+
+# qhasm: carry? rz3 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<rz3=int64#10
+# asm 2: adc <addt0=%r13,<rz3=%r12
+adc %r13,%r12
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#12,<addt0=int64#11
+# asm 2: cmovc <addt1=%r14,<addt0=%r13
+cmovc %r14,%r13
+
+# qhasm: rz0 += addt0
+# asm 1: add <addt0=int64#11,<rz0=int64#7
+# asm 2: add <addt0=%r13,<rz0=%rax
+add %r13,%rax
+
+# qhasm: *(uint64 *) (rp + 32) = rz0
+# asm 1: movq <rz0=int64#7,32(<rp=int64#1)
+# asm 2: movq <rz0=%rax,32(<rp=%rdi)
+movq %rax,32(%rdi)
+
+# qhasm: *(uint64 *) (rp + 40) = rz1
+# asm 1: movq <rz1=int64#8,40(<rp=int64#1)
+# asm 2: movq <rz1=%r10,40(<rp=%rdi)
+movq %r10,40(%rdi)
+
+# qhasm: *(uint64 *) (rp + 48) = rz2
+# asm 1: movq <rz2=int64#9,48(<rp=int64#1)
+# asm 2: movq <rz2=%r11,48(<rp=%rdi)
+movq %r11,48(%rdi)
+
+# qhasm: *(uint64 *) (rp + 56) = rz3
+# asm 1: movq <rz3=int64#10,56(<rp=int64#1)
+# asm 2: movq <rz3=%r12,56(<rp=%rdi)
+movq %r12,56(%rdi)
+
+# qhasm: carry? d0 -= b0_stack
+# asm 1: subq <b0_stack=stack64#12,<d0=int64#3
+# asm 2: subq <b0_stack=88(%rsp),<d0=%rdx
+subq 88(%rsp),%rdx
+
+# qhasm: carry? d1 -= b1_stack - carry
+# asm 1: sbbq <b1_stack=stack64#13,<d1=int64#4
+# asm 2: sbbq <b1_stack=96(%rsp),<d1=%rcx
+sbbq 96(%rsp),%rcx
+
+# qhasm: carry? d2 -= b2_stack - carry
+# asm 1: sbbq <b2_stack=stack64#14,<d2=int64#5
+# asm 2: sbbq <b2_stack=104(%rsp),<d2=%r8
+sbbq 104(%rsp),%r8
+
+# qhasm: carry? d3 -= b3_stack - carry
+# asm 1: sbbq <b3_stack=stack64#15,<d3=int64#6
+# asm 2: sbbq <b3_stack=112(%rsp),<d3=%r9
+sbbq 112(%rsp),%r9
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#11
+# asm 2: mov $0,>subt0=%r13
+mov $0,%r13
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#12
+# asm 2: mov $38,>subt1=%r14
+mov $38,%r14
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#11,<subt1=int64#12
+# asm 2: cmovae <subt0=%r13,<subt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? d0 -= subt1
+# asm 1: sub <subt1=int64#12,<d0=int64#3
+# asm 2: sub <subt1=%r14,<d0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? d1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<d1=int64#4
+# asm 2: sbb <subt0=%r13,<d1=%rcx
+sbb %r13,%rcx
+
+# qhasm: carry? d2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<d2=int64#5
+# asm 2: sbb <subt0=%r13,<d2=%r8
+sbb %r13,%r8
+
+# qhasm: carry? d3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<d3=int64#6
+# asm 2: sbb <subt0=%r13,<d3=%r9
+sbb %r13,%r9
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#12,<subt0=int64#11
+# asm 2: cmovc <subt1=%r14,<subt0=%r13
+cmovc %r14,%r13
+
+# qhasm: d0 -= subt0
+# asm 1: sub <subt0=int64#11,<d0=int64#3
+# asm 2: sub <subt0=%r13,<d0=%rdx
+sub %r13,%rdx
+
+# qhasm: *(uint64 *)(rp + 64) = d0
+# asm 1: movq <d0=int64#3,64(<rp=int64#1)
+# asm 2: movq <d0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = d1
+# asm 1: movq <d1=int64#4,72(<rp=int64#1)
+# asm 2: movq <d1=%rcx,72(<rp=%rdi)
+movq %rcx,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = d2
+# asm 1: movq <d2=int64#5,80(<rp=int64#1)
+# asm 2: movq <d2=%r8,80(<rp=%rdi)
+movq %r8,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = d3
+# asm 1: movq <d3=int64#6,88(<rp=int64#1)
+# asm 2: movq <d3=%r9,88(<rp=%rdi)
+movq %r9,88(%rdi)
+
+# qhasm: carry? rz0 -= c0_stack
+# asm 1: subq <c0_stack=stack64#16,<rz0=int64#7
+# asm 2: subq <c0_stack=120(%rsp),<rz0=%rax
+subq 120(%rsp),%rax
+
+# qhasm: carry? rz1 -= c1_stack - carry
+# asm 1: sbbq <c1_stack=stack64#17,<rz1=int64#8
+# asm 2: sbbq <c1_stack=128(%rsp),<rz1=%r10
+sbbq 128(%rsp),%r10
+
+# qhasm: carry? rz2 -= c2_stack - carry
+# asm 1: sbbq <c2_stack=stack64#18,<rz2=int64#9
+# asm 2: sbbq <c2_stack=136(%rsp),<rz2=%r11
+sbbq 136(%rsp),%r11
+
+# qhasm: carry? rz3 -= c3_stack - carry
+# asm 1: sbbq <c3_stack=stack64#19,<rz3=int64#10
+# asm 2: sbbq <c3_stack=144(%rsp),<rz3=%r12
+sbbq 144(%rsp),%r12
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#3
+# asm 2: mov $0,>subt0=%rdx
+mov $0,%rdx
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#4
+# asm 2: mov $38,>subt1=%rcx
+mov $38,%rcx
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#3,<subt1=int64#4
+# asm 2: cmovae <subt0=%rdx,<subt1=%rcx
+cmovae %rdx,%rcx
+
+# qhasm: carry? rz0 -= subt1
+# asm 1: sub <subt1=int64#4,<rz0=int64#7
+# asm 2: sub <subt1=%rcx,<rz0=%rax
+sub %rcx,%rax
+
+# qhasm: carry? rz1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<rz1=int64#8
+# asm 2: sbb <subt0=%rdx,<rz1=%r10
+sbb %rdx,%r10
+
+# qhasm: carry? rz2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<rz2=int64#9
+# asm 2: sbb <subt0=%rdx,<rz2=%r11
+sbb %rdx,%r11
+
+# qhasm: carry? rz3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#3,<rz3=int64#10
+# asm 2: sbb <subt0=%rdx,<rz3=%r12
+sbb %rdx,%r12
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#4,<subt0=int64#3
+# asm 2: cmovc <subt1=%rcx,<subt0=%rdx
+cmovc %rcx,%rdx
+
+# qhasm: rz0 -= subt0
+# asm 1: sub <subt0=int64#3,<rz0=int64#7
+# asm 2: sub <subt0=%rdx,<rz0=%rax
+sub %rdx,%rax
+
+# qhasm: *(uint64 *) (rp + 96) = rz0
+# asm 1: movq <rz0=int64#7,96(<rp=int64#1)
+# asm 2: movq <rz0=%rax,96(<rp=%rdi)
+movq %rax,96(%rdi)
+
+# qhasm: *(uint64 *) (rp + 104) = rz1
+# asm 1: movq <rz1=int64#8,104(<rp=int64#1)
+# asm 2: movq <rz1=%r10,104(<rp=%rdi)
+movq %r10,104(%rdi)
+
+# qhasm: *(uint64 *) (rp + 112) = rz2
+# asm 1: movq <rz2=int64#9,112(<rp=int64#1)
+# asm 2: movq <rz2=%r11,112(<rp=%rdi)
+movq %r11,112(%rdi)
+
+# qhasm: *(uint64 *) (rp + 120) = rz3
+# asm 1: movq <rz3=int64#10,120(<rp=int64#1)
+# asm 2: movq <rz3=%r12,120(<rp=%rdi)
+movq %r12,120(%rdi)
+
+# qhasm: rx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>rx0=int64#3
+# asm 2: movq 0(<pp=%rsi),>rx0=%rdx
+movq 0(%rsi),%rdx
+
+# qhasm: rx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>rx1=int64#4
+# asm 2: movq 8(<pp=%rsi),>rx1=%rcx
+movq 8(%rsi),%rcx
+
+# qhasm: rx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>rx2=int64#5
+# asm 2: movq 16(<pp=%rsi),>rx2=%r8
+movq 16(%rsi),%r8
+
+# qhasm: rx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>rx3=int64#6
+# asm 2: movq 24(<pp=%rsi),>rx3=%r9
+movq 24(%rsi),%r9
+
+# qhasm: carry? rx0 += *(uint64 *)(pp + 32)
+# asm 1: addq 32(<pp=int64#2),<rx0=int64#3
+# asm 2: addq 32(<pp=%rsi),<rx0=%rdx
+addq 32(%rsi),%rdx
+
+# qhasm: carry? rx1 += *(uint64 *)(pp + 40) + carry
+# asm 1: adcq 40(<pp=int64#2),<rx1=int64#4
+# asm 2: adcq 40(<pp=%rsi),<rx1=%rcx
+adcq 40(%rsi),%rcx
+
+# qhasm: carry? rx2 += *(uint64 *)(pp + 48) + carry
+# asm 1: adcq 48(<pp=int64#2),<rx2=int64#5
+# asm 2: adcq 48(<pp=%rsi),<rx2=%r8
+adcq 48(%rsi),%r8
+
+# qhasm: carry? rx3 += *(uint64 *)(pp + 56) + carry
+# asm 1: adcq 56(<pp=int64#2),<rx3=int64#6
+# asm 2: adcq 56(<pp=%rsi),<rx3=%r9
+adcq 56(%rsi),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#7
+# asm 2: cmovae <addt0=%rsi,<addt1=%rax
+cmovae %rsi,%rax
+
+# qhasm: carry? rx0 += addt1
+# asm 1: add <addt1=int64#7,<rx0=int64#3
+# asm 2: add <addt1=%rax,<rx0=%rdx
+add %rax,%rdx
+
+# qhasm: carry? rx1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx1=int64#4
+# asm 2: adc <addt0=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx2=int64#5
+# asm 2: adc <addt0=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx3=int64#6
+# asm 2: adc <addt0=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#2
+# asm 2: cmovc <addt1=%rax,<addt0=%rsi
+cmovc %rax,%rsi
+
+# qhasm: rx0 += addt0
+# asm 1: add <addt0=int64#2,<rx0=int64#3
+# asm 2: add <addt0=%rsi,<rx0=%rdx
+add %rsi,%rdx
+
+# qhasm: rx0_stack = rx0
+# asm 1: movq <rx0=int64#3,>rx0_stack=stack64#12
+# asm 2: movq <rx0=%rdx,>rx0_stack=88(%rsp)
+movq %rdx,88(%rsp)
+
+# qhasm: rx1_stack = rx1
+# asm 1: movq <rx1=int64#4,>rx1_stack=stack64#13
+# asm 2: movq <rx1=%rcx,>rx1_stack=96(%rsp)
+movq %rcx,96(%rsp)
+
+# qhasm: rx2_stack = rx2
+# asm 1: movq <rx2=int64#5,>rx2_stack=stack64#14
+# asm 2: movq <rx2=%r8,>rx2_stack=104(%rsp)
+movq %r8,104(%rsp)
+
+# qhasm: rx3_stack = rx3
+# asm 1: movq <rx3=int64#6,>rx3_stack=stack64#15
+# asm 2: movq <rx3=%r9,>rx3_stack=112(%rsp)
+movq %r9,112(%rsp)
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#2
+# asm 2: mov $0,>squarer7=%rsi
+mov $0,%rsi
+
+# qhasm: squarerax = rx1_stack
+# asm 1: movq <rx1_stack=stack64#13,>squarerax=int64#7
+# asm 2: movq <rx1_stack=96(%rsp),>squarerax=%rax
+movq 96(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: rx1 = squarerax
+# asm 1: mov <squarerax=int64#7,>rx1=int64#4
+# asm 2: mov <squarerax=%rax,>rx1=%rcx
+mov %rax,%rcx
+
+# qhasm: rx2 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>rx2=int64#5
+# asm 2: mov <squarerdx=%rdx,>rx2=%r8
+mov %rdx,%r8
+
+# qhasm: squarerax = rx2_stack
+# asm 1: movq <rx2_stack=stack64#14,>squarerax=int64#7
+# asm 2: movq <rx2_stack=104(%rsp),>squarerax=%rax
+movq 104(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx1_stack
+# asm 1: mulq <rx1_stack=stack64#13
+# asm 2: mulq <rx1_stack=96(%rsp)
+mulq 96(%rsp)
+
+# qhasm: rx3 = squarerax
+# asm 1: mov <squarerax=int64#7,>rx3=int64#6
+# asm 2: mov <squarerax=%rax,>rx3=%r9
+mov %rax,%r9
+
+# qhasm: squarer4 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer4=int64#8
+# asm 2: mov <squarerdx=%rdx,>squarer4=%r10
+mov %rdx,%r10
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx2_stack
+# asm 1: mulq <rx2_stack=stack64#14
+# asm 2: mulq <rx2_stack=104(%rsp)
+mulq 104(%rsp)
+
+# qhasm: squarer5 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer5=int64#9
+# asm 2: mov <squarerax=%rax,>squarer5=%r11
+mov %rax,%r11
+
+# qhasm: squarer6 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer6=int64#10
+# asm 2: mov <squarerdx=%rdx,>squarer6=%r12
+mov %rdx,%r12
+
+# qhasm: squarerax = rx2_stack
+# asm 1: movq <rx2_stack=stack64#14,>squarerax=int64#7
+# asm 2: movq <rx2_stack=104(%rsp),>squarerax=%rax
+movq 104(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: carry? rx2 += squarerax
+# asm 1: add <squarerax=int64#7,<rx2=int64#5
+# asm 2: add <squarerax=%rax,<rx2=%r8
+add %rax,%r8
+
+# qhasm: carry? rx3 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<rx3=int64#6
+# asm 2: adc <squarerdx=%rdx,<rx3=%r9
+adc %rdx,%r9
+
+# qhasm: squarer4 += 0 + carry
+# asm 1: adc $0,<squarer4=int64#8
+# asm 2: adc $0,<squarer4=%r10
+adc $0,%r10
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx1_stack
+# asm 1: mulq <rx1_stack=stack64#13
+# asm 2: mulq <rx1_stack=96(%rsp)
+mulq 96(%rsp)
+
+# qhasm: carry? squarer4 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer4=int64#8
+# asm 2: add <squarerax=%rax,<squarer4=%r10
+add %rax,%r10
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r11
+adc %rdx,%r11
+
+# qhasm: squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#10
+# asm 2: adc $0,<squarer6=%r12
+adc $0,%r12
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: carry? rx3 += squarerax
+# asm 1: add <squarerax=int64#7,<rx3=int64#6
+# asm 2: add <squarerax=%rax,<rx3=%r9
+add %rax,%r9
+
+# qhasm: carry? squarer4 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer4=int64#8
+# asm 2: adc <squarerdx=%rdx,<squarer4=%r10
+adc %rdx,%r10
+
+# qhasm: carry? squarer5 += 0 + carry
+# asm 1: adc $0,<squarer5=int64#9
+# asm 2: adc $0,<squarer5=%r11
+adc $0,%r11
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#10
+# asm 2: adc $0,<squarer6=%r12
+adc $0,%r12
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#2
+# asm 2: adc $0,<squarer7=%rsi
+adc $0,%rsi
+
+# qhasm: carry? rx1 += rx1
+# asm 1: add <rx1=int64#4,<rx1=int64#4
+# asm 2: add <rx1=%rcx,<rx1=%rcx
+add %rcx,%rcx
+
+# qhasm: carry? rx2 += rx2 + carry
+# asm 1: adc <rx2=int64#5,<rx2=int64#5
+# asm 2: adc <rx2=%r8,<rx2=%r8
+adc %r8,%r8
+
+# qhasm: carry? rx3 += rx3 + carry
+# asm 1: adc <rx3=int64#6,<rx3=int64#6
+# asm 2: adc <rx3=%r9,<rx3=%r9
+adc %r9,%r9
+
+# qhasm: carry? squarer4 += squarer4 + carry
+# asm 1: adc <squarer4=int64#8,<squarer4=int64#8
+# asm 2: adc <squarer4=%r10,<squarer4=%r10
+adc %r10,%r10
+
+# qhasm: carry? squarer5 += squarer5 + carry
+# asm 1: adc <squarer5=int64#9,<squarer5=int64#9
+# asm 2: adc <squarer5=%r11,<squarer5=%r11
+adc %r11,%r11
+
+# qhasm: carry? squarer6 += squarer6 + carry
+# asm 1: adc <squarer6=int64#10,<squarer6=int64#10
+# asm 2: adc <squarer6=%r12,<squarer6=%r12
+adc %r12,%r12
+
+# qhasm: squarer7 += squarer7 + carry
+# asm 1: adc <squarer7=int64#2,<squarer7=int64#2
+# asm 2: adc <squarer7=%rsi,<squarer7=%rsi
+adc %rsi,%rsi
+
+# qhasm: squarerax = rx0_stack
+# asm 1: movq <rx0_stack=stack64#12,>squarerax=int64#7
+# asm 2: movq <rx0_stack=88(%rsp),>squarerax=%rax
+movq 88(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx0_stack
+# asm 1: mulq <rx0_stack=stack64#12
+# asm 2: mulq <rx0_stack=88(%rsp)
+mulq 88(%rsp)
+
+# qhasm: rx0 = squarerax
+# asm 1: mov <squarerax=int64#7,>rx0=int64#11
+# asm 2: mov <squarerax=%rax,>rx0=%r13
+mov %rax,%r13
+
+# qhasm: squaret1 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret1=int64#12
+# asm 2: mov <squarerdx=%rdx,>squaret1=%r14
+mov %rdx,%r14
+
+# qhasm: squarerax = rx1_stack
+# asm 1: movq <rx1_stack=stack64#13,>squarerax=int64#7
+# asm 2: movq <rx1_stack=96(%rsp),>squarerax=%rax
+movq 96(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx1_stack
+# asm 1: mulq <rx1_stack=stack64#13
+# asm 2: mulq <rx1_stack=96(%rsp)
+mulq 96(%rsp)
+
+# qhasm: squaret2 = squarerax
+# asm 1: mov <squarerax=int64#7,>squaret2=int64#13
+# asm 2: mov <squarerax=%rax,>squaret2=%r15
+mov %rax,%r15
+
+# qhasm: squaret3 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squaret3=int64#14
+# asm 2: mov <squarerdx=%rdx,>squaret3=%rbx
+mov %rdx,%rbx
+
+# qhasm: squarerax = rx2_stack
+# asm 1: movq <rx2_stack=stack64#14,>squarerax=int64#7
+# asm 2: movq <rx2_stack=104(%rsp),>squarerax=%rax
+movq 104(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx2_stack
+# asm 1: mulq <rx2_stack=stack64#14
+# asm 2: mulq <rx2_stack=104(%rsp)
+mulq 104(%rsp)
+
+# qhasm: carry? rx1 += squaret1
+# asm 1: add <squaret1=int64#12,<rx1=int64#4
+# asm 2: add <squaret1=%r14,<rx1=%rcx
+add %r14,%rcx
+
+# qhasm: carry? rx2 += squaret2 + carry
+# asm 1: adc <squaret2=int64#13,<rx2=int64#5
+# asm 2: adc <squaret2=%r15,<rx2=%r8
+adc %r15,%r8
+
+# qhasm: carry? rx3 += squaret3 + carry
+# asm 1: adc <squaret3=int64#14,<rx3=int64#6
+# asm 2: adc <squaret3=%rbx,<rx3=%r9
+adc %rbx,%r9
+
+# qhasm: carry? squarer4 += squarerax + carry
+# asm 1: adc <squarerax=int64#7,<squarer4=int64#8
+# asm 2: adc <squarerax=%rax,<squarer4=%r10
+adc %rax,%r10
+
+# qhasm: carry? squarer5 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer5=int64#9
+# asm 2: adc <squarerdx=%rdx,<squarer5=%r11
+adc %rdx,%r11
+
+# qhasm: carry? squarer6 += 0 + carry
+# asm 1: adc $0,<squarer6=int64#10
+# asm 2: adc $0,<squarer6=%r12
+adc $0,%r12
+
+# qhasm: squarer7 += 0 + carry
+# asm 1: adc $0,<squarer7=int64#2
+# asm 2: adc $0,<squarer7=%rsi
+adc $0,%rsi
+
+# qhasm: squarerax = rx3_stack
+# asm 1: movq <rx3_stack=stack64#15,>squarerax=int64#7
+# asm 2: movq <rx3_stack=112(%rsp),>squarerax=%rax
+movq 112(%rsp),%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * rx3_stack
+# asm 1: mulq <rx3_stack=stack64#15
+# asm 2: mulq <rx3_stack=112(%rsp)
+mulq 112(%rsp)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#10
+# asm 2: add <squarerax=%rax,<squarer6=%r12
+add %rax,%r12
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#2
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rsi
+adc %rdx,%rsi
+
+# qhasm: squarerax = squarer4
+# asm 1: mov <squarer4=int64#8,>squarerax=int64#7
+# asm 2: mov <squarer4=%r10,>squarerax=%rax
+mov %r10,%rax
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: squarer4 = squarerax
+# asm 1: mov <squarerax=int64#7,>squarer4=int64#8
+# asm 2: mov <squarerax=%rax,>squarer4=%r10
+mov %rax,%r10
+
+# qhasm: squarerax = squarer5
+# asm 1: mov <squarer5=int64#9,>squarerax=int64#7
+# asm 2: mov <squarer5=%r11,>squarerax=%rax
+mov %r11,%rax
+
+# qhasm: squarer5 = squarerdx
+# asm 1: mov <squarerdx=int64#3,>squarer5=int64#9
+# asm 2: mov <squarerdx=%rdx,>squarer5=%r11
+mov %rdx,%r11
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer5 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer5=int64#9
+# asm 2: add <squarerax=%rax,<squarer5=%r11
+add %rax,%r11
+
+# qhasm: squarerax = squarer6
+# asm 1: mov <squarer6=int64#10,>squarerax=int64#7
+# asm 2: mov <squarer6=%r12,>squarerax=%rax
+mov %r12,%rax
+
+# qhasm: squarer6 = 0
+# asm 1: mov $0,>squarer6=int64#10
+# asm 2: mov $0,>squarer6=%r12
+mov $0,%r12
+
+# qhasm: squarer6 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer6=int64#10
+# asm 2: adc <squarerdx=%rdx,<squarer6=%r12
+adc %rdx,%r12
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer6 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer6=int64#10
+# asm 2: add <squarerax=%rax,<squarer6=%r12
+add %rax,%r12
+
+# qhasm: squarerax = squarer7
+# asm 1: mov <squarer7=int64#2,>squarerax=int64#7
+# asm 2: mov <squarer7=%rsi,>squarerax=%rax
+mov %rsi,%rax
+
+# qhasm: squarer7 = 0
+# asm 1: mov $0,>squarer7=int64#2
+# asm 2: mov $0,>squarer7=%rsi
+mov $0,%rsi
+
+# qhasm: squarer7 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer7=int64#2
+# asm 2: adc <squarerdx=%rdx,<squarer7=%rsi
+adc %rdx,%rsi
+
+# qhasm: (uint128) squarerdx squarerax = squarerax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? squarer7 += squarerax
+# asm 1: add <squarerax=int64#7,<squarer7=int64#2
+# asm 2: add <squarerax=%rax,<squarer7=%rsi
+add %rax,%rsi
+
+# qhasm: squarer8 = 0
+# asm 1: mov $0,>squarer8=int64#7
+# asm 2: mov $0,>squarer8=%rax
+mov $0,%rax
+
+# qhasm: squarer8 += squarerdx + carry
+# asm 1: adc <squarerdx=int64#3,<squarer8=int64#7
+# asm 2: adc <squarerdx=%rdx,<squarer8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += squarer4
+# asm 1: add <squarer4=int64#8,<rx0=int64#11
+# asm 2: add <squarer4=%r10,<rx0=%r13
+add %r10,%r13
+
+# qhasm: carry? rx1 += squarer5 + carry
+# asm 1: adc <squarer5=int64#9,<rx1=int64#4
+# asm 2: adc <squarer5=%r11,<rx1=%rcx
+adc %r11,%rcx
+
+# qhasm: carry? rx2 += squarer6 + carry
+# asm 1: adc <squarer6=int64#10,<rx2=int64#5
+# asm 2: adc <squarer6=%r12,<rx2=%r8
+adc %r12,%r8
+
+# qhasm: carry? rx3 += squarer7 + carry
+# asm 1: adc <squarer7=int64#2,<rx3=int64#6
+# asm 2: adc <squarer7=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: squarezero = 0
+# asm 1: mov $0,>squarezero=int64#2
+# asm 2: mov $0,>squarezero=%rsi
+mov $0,%rsi
+
+# qhasm: squarer8 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarer8=int64#7
+# asm 2: adc <squarezero=%rsi,<squarer8=%rax
+adc %rsi,%rax
+
+# qhasm: squarer8 *= 38
+# asm 1: imulq $38,<squarer8=int64#7,>squarer8=int64#3
+# asm 2: imulq $38,<squarer8=%rax,>squarer8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rx0 += squarer8
+# asm 1: add <squarer8=int64#3,<rx0=int64#11
+# asm 2: add <squarer8=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rx1 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<rx1=int64#4
+# asm 2: adc <squarezero=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<rx2=int64#5
+# asm 2: adc <squarezero=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<rx3=int64#6
+# asm 2: adc <squarezero=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: squarezero += squarezero + carry
+# asm 1: adc <squarezero=int64#2,<squarezero=int64#2
+# asm 2: adc <squarezero=%rsi,<squarezero=%rsi
+adc %rsi,%rsi
+
+# qhasm: squarezero *= 38
+# asm 1: imulq $38,<squarezero=int64#2,>squarezero=int64#2
+# asm 2: imulq $38,<squarezero=%rsi,>squarezero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rx0 += squarezero
+# asm 1: add <squarezero=int64#2,<rx0=int64#11
+# asm 2: add <squarezero=%rsi,<rx0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rx0 += d0_stack
+# asm 1: addq <d0_stack=stack64#8,<rx0=int64#11
+# asm 2: addq <d0_stack=56(%rsp),<rx0=%r13
+addq 56(%rsp),%r13
+
+# qhasm: carry? rx1 += d1_stack + carry
+# asm 1: adcq <d1_stack=stack64#9,<rx1=int64#4
+# asm 2: adcq <d1_stack=64(%rsp),<rx1=%rcx
+adcq 64(%rsp),%rcx
+
+# qhasm: carry? rx2 += d2_stack + carry
+# asm 1: adcq <d2_stack=stack64#10,<rx2=int64#5
+# asm 2: adcq <d2_stack=72(%rsp),<rx2=%r8
+adcq 72(%rsp),%r8
+
+# qhasm: carry? rx3 += d3_stack + carry
+# asm 1: adcq <d3_stack=stack64#11,<rx3=int64#6
+# asm 2: adcq <d3_stack=80(%rsp),<rx3=%r9
+adcq 80(%rsp),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rx0 += addt1
+# asm 1: add <addt1=int64#3,<rx0=int64#11
+# asm 2: add <addt1=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rx1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx1=int64#4
+# asm 2: adc <addt0=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx2=int64#5
+# asm 2: adc <addt0=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx3=int64#6
+# asm 2: adc <addt0=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rx0 += addt0
+# asm 1: add <addt0=int64#2,<rx0=int64#11
+# asm 2: add <addt0=%rsi,<rx0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rx0 += e0_stack
+# asm 1: addq <e0_stack=stack64#20,<rx0=int64#11
+# asm 2: addq <e0_stack=152(%rsp),<rx0=%r13
+addq 152(%rsp),%r13
+
+# qhasm: carry? rx1 += e1_stack + carry
+# asm 1: adcq <e1_stack=stack64#21,<rx1=int64#4
+# asm 2: adcq <e1_stack=160(%rsp),<rx1=%rcx
+adcq 160(%rsp),%rcx
+
+# qhasm: carry? rx2 += e2_stack + carry
+# asm 1: adcq <e2_stack=stack64#22,<rx2=int64#5
+# asm 2: adcq <e2_stack=168(%rsp),<rx2=%r8
+adcq 168(%rsp),%r8
+
+# qhasm: carry? rx3 += e3_stack + carry
+# asm 1: adcq <e3_stack=stack64#23,<rx3=int64#6
+# asm 2: adcq <e3_stack=176(%rsp),<rx3=%r9
+adcq 176(%rsp),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rx0 += addt1
+# asm 1: add <addt1=int64#3,<rx0=int64#11
+# asm 2: add <addt1=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rx1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx1=int64#4
+# asm 2: adc <addt0=%rsi,<rx1=%rcx
+adc %rsi,%rcx
+
+# qhasm: carry? rx2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx2=int64#5
+# asm 2: adc <addt0=%rsi,<rx2=%r8
+adc %rsi,%r8
+
+# qhasm: carry? rx3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rx3=int64#6
+# asm 2: adc <addt0=%rsi,<rx3=%r9
+adc %rsi,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rx0 += addt0
+# asm 1: add <addt0=int64#2,<rx0=int64#11
+# asm 2: add <addt0=%rsi,<rx0=%r13
+add %rsi,%r13
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#11,0(<rp=int64#1)
+# asm 2: movq <rx0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#4,8(<rp=int64#1)
+# asm 2: movq <rx1=%rcx,8(<rp=%rdi)
+movq %rcx,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#5,16(<rp=int64#1)
+# asm 2: movq <rx2=%r8,16(<rp=%rdi)
+movq %r8,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#6,24(<rp=int64#1)
+# asm 2: movq <rx3=%r9,24(<rp=%rdi)
+movq %r9,24(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_double.c b/ext/ed25519-amd64-asm/ge25519_double.c
new file mode 100644
index 00000000..d55e2b4f
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_double.c
@@ -0,0 +1,8 @@
+#include "ge25519.h"
+
+void ge25519_double(ge25519_p3 *r, const ge25519_p3 *p)
+{
+ ge25519_p1p1 grp1p1;
+ ge25519_dbl_p1p1(&grp1p1, (ge25519_p2 *)p);
+ ge25519_p1p1_to_p3(r, &grp1p1);
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_double_scalarmult.c b/ext/ed25519-amd64-asm/ge25519_double_scalarmult.c
new file mode 100644
index 00000000..30c922af
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_double_scalarmult.c
@@ -0,0 +1,102 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+
+#define S1_SWINDOWSIZE 5
+#define PRE1_SIZE (1<<(S1_SWINDOWSIZE-2))
+#define S2_SWINDOWSIZE 7
+#define PRE2_SIZE (1<<(S2_SWINDOWSIZE-2))
+
+ge25519_niels pre2[PRE2_SIZE] = {
+#include "ge25519_base_slide_multiples.data"
+};
+
+static const fe25519 ec2d = {{0xEBD69B9426B2F146, 0x00E0149A8283B156, 0x198E80F2EEF3D130, 0xA406D9DC56DFFCE7}};
+
+static void setneutral(ge25519 *r)
+{
+ fe25519_setint(&r->x,0);
+ fe25519_setint(&r->y,1);
+ fe25519_setint(&r->z,1);
+ fe25519_setint(&r->t,0);
+}
+
+/* computes [s1]p1 + [s2]p2 */
+void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const sc25519 *s2)
+{
+ signed char slide1[256], slide2[256];
+ ge25519_pniels pre1[PRE1_SIZE], neg;
+ ge25519_p3 d1;
+ ge25519_p1p1 t;
+ ge25519_niels nneg;
+ fe25519 d;
+ int i;
+
+ sc25519_slide(slide1, s1, S1_SWINDOWSIZE);
+ sc25519_slide(slide2, s2, S2_SWINDOWSIZE);
+
+ /* precomputation */
+ pre1[0] = *(ge25519_pniels *)p1;
+ ge25519_dbl_p1p1(&t,(ge25519_p2 *)pre1); ge25519_p1p1_to_p3(&d1, &t);
+ /* Convert pre[0] to projective Niels representation */
+ d = pre1[0].ysubx;
+ fe25519_sub(&pre1[0].ysubx, &pre1[0].xaddy, &pre1[0].ysubx);
+ fe25519_add(&pre1[0].xaddy, &pre1[0].xaddy, &d);
+ fe25519_mul(&pre1[0].t2d, &pre1[0].t2d, &ec2d);
+
+ for(i=0;i<PRE1_SIZE-1;i++)
+ {
+ ge25519_pnielsadd_p1p1(&t, &d1, &pre1[i]); ge25519_p1p1_to_p3((ge25519_p3 *)&pre1[i+1], &t);
+ /* Convert pre1[i+1] to projective Niels representation */
+ d = pre1[i+1].ysubx;
+ fe25519_sub(&pre1[i+1].ysubx, &pre1[i+1].xaddy, &pre1[i+1].ysubx);
+ fe25519_add(&pre1[i+1].xaddy, &pre1[i+1].xaddy, &d);
+ fe25519_mul(&pre1[i+1].t2d, &pre1[i+1].t2d, &ec2d);
+ }
+
+ setneutral(r);
+ for (i = 255;i >= 0;--i) {
+ if (slide1[i] || slide2[i]) goto firstbit;
+ }
+
+ for(;i>=0;i--)
+ {
+ firstbit:
+
+ ge25519_dbl_p1p1(&t, (ge25519_p2 *)r);
+
+ if(slide1[i]>0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ ge25519_pnielsadd_p1p1(&t, r, &pre1[slide1[i]/2]);
+ }
+ else if(slide1[i]<0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ neg = pre1[-slide1[i]/2];
+ d = neg.ysubx;
+ neg.ysubx = neg.xaddy;
+ neg.xaddy = d;
+ fe25519_neg(&neg.t2d, &neg.t2d);
+ ge25519_pnielsadd_p1p1(&t, r, &neg);
+ }
+
+ if(slide2[i]>0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ ge25519_nielsadd_p1p1(&t, r, &pre2[slide2[i]/2]);
+ }
+ else if(slide2[i]<0)
+ {
+ ge25519_p1p1_to_p3(r, &t);
+ nneg = pre2[-slide2[i]/2];
+ d = nneg.ysubx;
+ nneg.ysubx = nneg.xaddy;
+ nneg.xaddy = d;
+ fe25519_neg(&nneg.t2d, &nneg.t2d);
+ ge25519_nielsadd_p1p1(&t, r, &nneg);
+ }
+
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &t);
+ }
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_isneutral.c b/ext/ed25519-amd64-asm/ge25519_isneutral.c
new file mode 100644
index 00000000..cf566dba
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_isneutral.c
@@ -0,0 +1,9 @@
+#include "fe25519.h"
+#include "ge25519.h"
+
+int ge25519_isneutral_vartime(const ge25519_p3 *p)
+{
+ if(!fe25519_iszero_vartime(&p->x)) return 0;
+ if(!fe25519_iseq_vartime(&p->y, &p->z)) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c b/ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c
new file mode 100644
index 00000000..afc6aeae
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_multi_scalarmult.c
@@ -0,0 +1,102 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+#include "index_heap.h"
+
+static void setneutral(ge25519 *r)
+{
+ fe25519_setint(&r->x,0);
+ fe25519_setint(&r->y,1);
+ fe25519_setint(&r->z,1);
+ fe25519_setint(&r->t,0);
+}
+
+static void ge25519_scalarmult_vartime_2limbs(ge25519 *r, ge25519 *p, sc25519 *s)
+{
+ if (s->v[1] == 0 && s->v[0] == 1) /* This will happen most of the time after Bos-Coster */
+ *r = *p;
+ else if (s->v[1] == 0 && s->v[0] == 0) /* This won't ever happen, except for all scalars == 0 in Bos-Coster */
+ setneutral(r);
+ else
+ {
+ ge25519 d;
+ unsigned long long mask = (1ULL << 63);
+ int i = 1;
+ while(!(mask & s->v[1]) && mask != 0)
+ mask >>= 1;
+ if(mask == 0)
+ {
+ mask = (1ULL << 63);
+ i = 0;
+ while(!(mask & s->v[0]) && mask != 0)
+ mask >>= 1;
+ }
+ d = *p;
+ mask >>= 1;
+ for(;mask != 0;mask >>= 1)
+ {
+ ge25519_double(&d,&d);
+ if(s->v[i] & mask)
+ ge25519_add(&d,&d,p);
+ }
+ if(i==1)
+ {
+ mask = (1ULL << 63);
+ for(;mask != 0;mask >>= 1)
+ {
+ ge25519_double(&d,&d);
+ if(s->v[0] & mask)
+ ge25519_add(&d,&d,p);
+ }
+ }
+ *r = d;
+ }
+}
+
+/* caller's responsibility to ensure npoints >= 5 */
+void ge25519_multi_scalarmult_vartime(ge25519_p3 *r, ge25519_p3 *p, sc25519 *s, const unsigned long long npoints)
+{
+ unsigned long long pos[npoints];
+ unsigned long long hlen=((npoints+1)/2)|1;
+ unsigned long long max1, max2,i;
+
+ heap_init(pos, hlen, s);
+
+ for(i=0;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if((s[max1].v[3] == 0) || (sc25519_iszero_vartime(&s[max2]))) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced(pos, hlen, s);
+ }
+ for(;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if((s[max1].v[2] == 0) || (sc25519_iszero_vartime(&s[max2]))) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced_3limbs(pos, hlen, s);
+ }
+ /* We know that (npoints-1)/2 scalars are only 128-bit scalars */
+ heap_extend(pos, hlen, npoints, s);
+ hlen = npoints;
+ for(;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if((s[max1].v[1] == 0) || (sc25519_iszero_vartime(&s[max2]))) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced_2limbs(pos, hlen, s);
+ }
+ for(;;i++)
+ {
+ heap_get2max(pos, &max1, &max2, s);
+ if(sc25519_iszero_vartime(&s[max2])) break;
+ sc25519_sub_nored(&s[max1],&s[max1],&s[max2]);
+ ge25519_add(&p[max2],&p[max2],&p[max1]);
+ heap_rootreplaced_1limb(pos, hlen, s);
+ }
+
+ ge25519_scalarmult_vartime_2limbs(r, &p[max1], &s[max1]);
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_nielsadd2.s b/ext/ed25519-amd64-asm/ge25519_nielsadd2.s
new file mode 100644
index 00000000..19d71f11
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_nielsadd2.s
@@ -0,0 +1,5791 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 e0
+
+# qhasm: int64 e1
+
+# qhasm: int64 e2
+
+# qhasm: int64 e3
+
+# qhasm: stack64 e0_stack
+
+# qhasm: stack64 e1_stack
+
+# qhasm: stack64 e2_stack
+
+# qhasm: stack64 e3_stack
+
+# qhasm: int64 f0
+
+# qhasm: int64 f1
+
+# qhasm: int64 f2
+
+# qhasm: int64 f3
+
+# qhasm: stack64 f0_stack
+
+# qhasm: stack64 f1_stack
+
+# qhasm: stack64 f2_stack
+
+# qhasm: stack64 f3_stack
+
+# qhasm: int64 g0
+
+# qhasm: int64 g1
+
+# qhasm: int64 g2
+
+# qhasm: int64 g3
+
+# qhasm: stack64 g0_stack
+
+# qhasm: stack64 g1_stack
+
+# qhasm: stack64 g2_stack
+
+# qhasm: stack64 g3_stack
+
+# qhasm: int64 h0
+
+# qhasm: int64 h1
+
+# qhasm: int64 h2
+
+# qhasm: int64 h3
+
+# qhasm: stack64 h0_stack
+
+# qhasm: stack64 h1_stack
+
+# qhasm: stack64 h2_stack
+
+# qhasm: stack64 h3_stack
+
+# qhasm: int64 qt0
+
+# qhasm: int64 qt1
+
+# qhasm: int64 qt2
+
+# qhasm: int64 qt3
+
+# qhasm: stack64 qt0_stack
+
+# qhasm: stack64 qt1_stack
+
+# qhasm: stack64 qt2_stack
+
+# qhasm: stack64 qt3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+.globl crypto_sign_ed25519_amd64_64_ge25519_nielsadd2
+_crypto_sign_ed25519_amd64_64_ge25519_nielsadd2:
+crypto_sign_ed25519_amd64_64_ge25519_nielsadd2:
+mov %rsp,%r11
+and $31,%r11
+add $192,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: a0 = *(uint64 *)(rp + 32)
+# asm 1: movq 32(<rp=int64#1),>a0=int64#3
+# asm 2: movq 32(<rp=%rdi),>a0=%rdx
+movq 32(%rdi),%rdx
+
+# qhasm: a1 = *(uint64 *)(rp + 40)
+# asm 1: movq 40(<rp=int64#1),>a1=int64#4
+# asm 2: movq 40(<rp=%rdi),>a1=%rcx
+movq 40(%rdi),%rcx
+
+# qhasm: a2 = *(uint64 *)(rp + 48)
+# asm 1: movq 48(<rp=int64#1),>a2=int64#5
+# asm 2: movq 48(<rp=%rdi),>a2=%r8
+movq 48(%rdi),%r8
+
+# qhasm: a3 = *(uint64 *)(rp + 56)
+# asm 1: movq 56(<rp=int64#1),>a3=int64#6
+# asm 2: movq 56(<rp=%rdi),>a3=%r9
+movq 56(%rdi),%r9
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#7
+# asm 2: mov <a0=%rdx,>b0=%rax
+mov %rdx,%rax
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#4,>b1=int64#8
+# asm 2: mov <a1=%rcx,>b1=%r10
+mov %rcx,%r10
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#5,>b2=int64#9
+# asm 2: mov <a2=%r8,>b2=%r11
+mov %r8,%r11
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#6,>b3=int64#10
+# asm 2: mov <a3=%r9,>b3=%r12
+mov %r9,%r12
+
+# qhasm: carry? a0 -= *(uint64 *) (rp + 0)
+# asm 1: subq 0(<rp=int64#1),<a0=int64#3
+# asm 2: subq 0(<rp=%rdi),<a0=%rdx
+subq 0(%rdi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *) (rp + 8) - carry
+# asm 1: sbbq 8(<rp=int64#1),<a1=int64#4
+# asm 2: sbbq 8(<rp=%rdi),<a1=%rcx
+sbbq 8(%rdi),%rcx
+
+# qhasm: carry? a2 -= *(uint64 *) (rp + 16) - carry
+# asm 1: sbbq 16(<rp=int64#1),<a2=int64#5
+# asm 2: sbbq 16(<rp=%rdi),<a2=%r8
+sbbq 16(%rdi),%r8
+
+# qhasm: carry? a3 -= *(uint64 *) (rp + 24) - carry
+# asm 1: sbbq 24(<rp=int64#1),<a3=int64#6
+# asm 2: sbbq 24(<rp=%rdi),<a3=%r9
+sbbq 24(%rdi),%r9
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#11
+# asm 2: mov $0,>subt0=%r13
+mov $0,%r13
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#12
+# asm 2: mov $38,>subt1=%r14
+mov $38,%r14
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#11,<subt1=int64#12
+# asm 2: cmovae <subt0=%r13,<subt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#12,<a0=int64#3
+# asm 2: sub <subt1=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<a1=int64#4
+# asm 2: sbb <subt0=%r13,<a1=%rcx
+sbb %r13,%rcx
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<a2=int64#5
+# asm 2: sbb <subt0=%r13,<a2=%r8
+sbb %r13,%r8
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#11,<a3=int64#6
+# asm 2: sbb <subt0=%r13,<a3=%r9
+sbb %r13,%r9
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#12,<subt0=int64#11
+# asm 2: cmovc <subt1=%r14,<subt0=%r13
+cmovc %r14,%r13
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#11,<a0=int64#3
+# asm 2: sub <subt0=%r13,<a0=%rdx
+sub %r13,%rdx
+
+# qhasm: carry? b0 += *(uint64 *) (rp + 0)
+# asm 1: addq 0(<rp=int64#1),<b0=int64#7
+# asm 2: addq 0(<rp=%rdi),<b0=%rax
+addq 0(%rdi),%rax
+
+# qhasm: carry? b1 += *(uint64 *) (rp + 8) + carry
+# asm 1: adcq 8(<rp=int64#1),<b1=int64#8
+# asm 2: adcq 8(<rp=%rdi),<b1=%r10
+adcq 8(%rdi),%r10
+
+# qhasm: carry? b2 += *(uint64 *) (rp + 16) + carry
+# asm 1: adcq 16(<rp=int64#1),<b2=int64#9
+# asm 2: adcq 16(<rp=%rdi),<b2=%r11
+adcq 16(%rdi),%r11
+
+# qhasm: carry? b3 += *(uint64 *) (rp + 24) + carry
+# asm 1: adcq 24(<rp=int64#1),<b3=int64#10
+# asm 2: adcq 24(<rp=%rdi),<b3=%r12
+adcq 24(%rdi),%r12
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#11
+# asm 2: mov $0,>addt0=%r13
+mov $0,%r13
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#12
+# asm 2: mov $38,>addt1=%r14
+mov $38,%r14
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#11,<addt1=int64#12
+# asm 2: cmovae <addt0=%r13,<addt1=%r14
+cmovae %r13,%r14
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#12,<b0=int64#7
+# asm 2: add <addt1=%r14,<b0=%rax
+add %r14,%rax
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<b1=int64#8
+# asm 2: adc <addt0=%r13,<b1=%r10
+adc %r13,%r10
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<b2=int64#9
+# asm 2: adc <addt0=%r13,<b2=%r11
+adc %r13,%r11
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#11,<b3=int64#10
+# asm 2: adc <addt0=%r13,<b3=%r12
+adc %r13,%r12
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#12,<addt0=int64#11
+# asm 2: cmovc <addt1=%r14,<addt0=%r13
+cmovc %r14,%r13
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#11,<b0=int64#7
+# asm 2: add <addt0=%r13,<b0=%rax
+add %r13,%rax
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#4,>a1_stack=stack64#9
+# asm 2: movq <a1=%rcx,>a1_stack=64(%rsp)
+movq %rcx,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#5,>a2_stack=stack64#10
+# asm 2: movq <a2=%r8,>a2_stack=72(%rsp)
+movq %r8,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#6,>a3_stack=stack64#11
+# asm 2: movq <a3=%r9,>a3_stack=80(%rsp)
+movq %r9,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#7,>b0_stack=stack64#12
+# asm 2: movq <b0=%rax,>b0_stack=88(%rsp)
+movq %rax,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#8,>b1_stack=stack64#13
+# asm 2: movq <b1=%r10,>b1_stack=96(%rsp)
+movq %r10,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#9,>b2_stack=stack64#14
+# asm 2: movq <b2=%r11,>b2_stack=104(%rsp)
+movq %r11,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#10,>b3_stack=stack64#15
+# asm 2: movq <b3=%r12,>b3_stack=112(%rsp)
+movq %r12,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#9
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r11
+movq 56(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#10
+# asm 2: mov <mulrax=%rax,>a0=%r12
+mov %rax,%r12
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#11
+# asm 2: mov <mulrdx=%rdx,>a1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#11
+# asm 2: add <mulrax=%rax,<a1=%r13
+add %rax,%r13
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#12
+# asm 2: mov $0,>a2=%r14
+mov $0,%r14
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#12
+# asm 2: adc <mulrdx=%rdx,<a2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#12
+# asm 2: add <mulrax=%rax,<a2=%r14
+add %rax,%r14
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#13
+# asm 2: mov $0,>a3=%r15
+mov $0,%r15
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#13
+# asm 2: adc <mulrdx=%rdx,<a3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#9
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r11
+movq 64(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#11
+# asm 2: add <mulrax=%rax,<a1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#12
+# asm 2: add <mulrax=%rax,<a2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#14,<a2=int64#12
+# asm 2: add <mulc=%rbx,<a2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#14,<a3=int64#13
+# asm 2: add <mulc=%rbx,<a3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#9
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r11
+movq 72(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#12
+# asm 2: add <mulrax=%rax,<a2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#14,<a3=int64#13
+# asm 2: add <mulc=%rbx,<a3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#9
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r11
+movq 80(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rsi),>mulrax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#13
+# asm 2: add <mulrax=%rax,<a3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rsi),>mulrax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rsi),>mulrax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rsi),>mulrax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#4,<a0=int64#10
+# asm 2: add <mulr4=%rcx,<a0=%r12
+add %rcx,%r12
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<a1=int64#11
+# asm 2: adc <mulr5=%r8,<a1=%r13
+adc %r8,%r13
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<a2=int64#12
+# asm 2: adc <mulr6=%r9,<a2=%r14
+adc %r9,%r14
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<a3=int64#13
+# asm 2: adc <mulr7=%r10,<a3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#4,<a0=int64#10
+# asm 2: add <mulr8=%rcx,<a0=%r12
+add %rcx,%r12
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#11
+# asm 2: adc <mulzero=%rdx,<a1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#12
+# asm 2: adc <mulzero=%rdx,<a2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#13
+# asm 2: adc <mulzero=%rdx,<a3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#10
+# asm 2: add <mulzero=%rdx,<a0=%r12
+add %rdx,%r12
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#10,>a0_stack=stack64#8
+# asm 2: movq <a0=%r12,>a0_stack=56(%rsp)
+movq %r12,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#11,>a1_stack=stack64#9
+# asm 2: movq <a1=%r13,>a1_stack=64(%rsp)
+movq %r13,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#12,>a2_stack=stack64#10
+# asm 2: movq <a2=%r14,>a2_stack=72(%rsp)
+movq %r14,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#13,>a3_stack=stack64#11
+# asm 2: movq <a3=%r15,>a3_stack=80(%rsp)
+movq %r15,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#9
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r11
+movq 88(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: e0 = mulrax
+# asm 1: mov <mulrax=int64#7,>e0=int64#10
+# asm 2: mov <mulrax=%rax,>e0=%r12
+mov %rax,%r12
+
+# qhasm: e1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>e1=int64#11
+# asm 2: mov <mulrdx=%rdx,>e1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#11
+# asm 2: add <mulrax=%rax,<e1=%r13
+add %rax,%r13
+
+# qhasm: e2 = 0
+# asm 1: mov $0,>e2=int64#12
+# asm 2: mov $0,>e2=%r14
+mov $0,%r14
+
+# qhasm: e2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e2=int64#12
+# asm 2: adc <mulrdx=%rdx,<e2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#12
+# asm 2: add <mulrax=%rax,<e2=%r14
+add %rax,%r14
+
+# qhasm: e3 = 0
+# asm 1: mov $0,>e3=int64#13
+# asm 2: mov $0,>e3=%r15
+mov $0,%r15
+
+# qhasm: e3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e3=int64#13
+# asm 2: adc <mulrdx=%rdx,<e3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#9
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r11
+movq 96(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#11
+# asm 2: add <mulrax=%rax,<e1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#12
+# asm 2: add <mulrax=%rax,<e2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e2 += mulc
+# asm 1: add <mulc=int64#14,<e2=int64#12
+# asm 2: add <mulc=%rbx,<e2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#14,<e3=int64#13
+# asm 2: add <mulc=%rbx,<e3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#9
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r11
+movq 104(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#12
+# asm 2: add <mulrax=%rax,<e2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#14,<e3=int64#13
+# asm 2: add <mulc=%rbx,<e3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#9
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r11
+movq 112(%rsp),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#13
+# asm 2: add <mulrax=%rax,<e3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? e0 += mulr4
+# asm 1: add <mulr4=int64#4,<e0=int64#10
+# asm 2: add <mulr4=%rcx,<e0=%r12
+add %rcx,%r12
+
+# qhasm: carry? e1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<e1=int64#11
+# asm 2: adc <mulr5=%r8,<e1=%r13
+adc %r8,%r13
+
+# qhasm: carry? e2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<e2=int64#12
+# asm 2: adc <mulr6=%r9,<e2=%r14
+adc %r9,%r14
+
+# qhasm: carry? e3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<e3=int64#13
+# asm 2: adc <mulr7=%r10,<e3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? e0 += mulr8
+# asm 1: add <mulr8=int64#4,<e0=int64#10
+# asm 2: add <mulr8=%rcx,<e0=%r12
+add %rcx,%r12
+
+# qhasm: carry? e1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e1=int64#11
+# asm 2: adc <mulzero=%rdx,<e1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? e2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e2=int64#12
+# asm 2: adc <mulzero=%rdx,<e2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? e3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e3=int64#13
+# asm 2: adc <mulzero=%rdx,<e3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: e0 += mulzero
+# asm 1: add <mulzero=int64#3,<e0=int64#10
+# asm 2: add <mulzero=%rdx,<e0=%r12
+add %rdx,%r12
+
+# qhasm: h0 = e0
+# asm 1: mov <e0=int64#10,>h0=int64#3
+# asm 2: mov <e0=%r12,>h0=%rdx
+mov %r12,%rdx
+
+# qhasm: h1 = e1
+# asm 1: mov <e1=int64#11,>h1=int64#4
+# asm 2: mov <e1=%r13,>h1=%rcx
+mov %r13,%rcx
+
+# qhasm: h2 = e2
+# asm 1: mov <e2=int64#12,>h2=int64#5
+# asm 2: mov <e2=%r14,>h2=%r8
+mov %r14,%r8
+
+# qhasm: h3 = e3
+# asm 1: mov <e3=int64#13,>h3=int64#6
+# asm 2: mov <e3=%r15,>h3=%r9
+mov %r15,%r9
+
+# qhasm: carry? e0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<e0=int64#10
+# asm 2: subq <a0_stack=56(%rsp),<e0=%r12
+subq 56(%rsp),%r12
+
+# qhasm: carry? e1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<e1=int64#11
+# asm 2: sbbq <a1_stack=64(%rsp),<e1=%r13
+sbbq 64(%rsp),%r13
+
+# qhasm: carry? e2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<e2=int64#12
+# asm 2: sbbq <a2_stack=72(%rsp),<e2=%r14
+sbbq 72(%rsp),%r14
+
+# qhasm: carry? e3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<e3=int64#13
+# asm 2: sbbq <a3_stack=80(%rsp),<e3=%r15
+sbbq 80(%rsp),%r15
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#7
+# asm 2: mov $0,>subt0=%rax
+mov $0,%rax
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#8
+# asm 2: mov $38,>subt1=%r10
+mov $38,%r10
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#7,<subt1=int64#8
+# asm 2: cmovae <subt0=%rax,<subt1=%r10
+cmovae %rax,%r10
+
+# qhasm: carry? e0 -= subt1
+# asm 1: sub <subt1=int64#8,<e0=int64#10
+# asm 2: sub <subt1=%r10,<e0=%r12
+sub %r10,%r12
+
+# qhasm: carry? e1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<e1=int64#11
+# asm 2: sbb <subt0=%rax,<e1=%r13
+sbb %rax,%r13
+
+# qhasm: carry? e2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<e2=int64#12
+# asm 2: sbb <subt0=%rax,<e2=%r14
+sbb %rax,%r14
+
+# qhasm: carry? e3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#7,<e3=int64#13
+# asm 2: sbb <subt0=%rax,<e3=%r15
+sbb %rax,%r15
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#8,<subt0=int64#7
+# asm 2: cmovc <subt1=%r10,<subt0=%rax
+cmovc %r10,%rax
+
+# qhasm: e0 -= subt0
+# asm 1: sub <subt0=int64#7,<e0=int64#10
+# asm 2: sub <subt0=%rax,<e0=%r12
+sub %rax,%r12
+
+# qhasm: carry? h0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<h0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<h0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? h1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<h1=int64#4
+# asm 2: adcq <a1_stack=64(%rsp),<h1=%rcx
+adcq 64(%rsp),%rcx
+
+# qhasm: carry? h2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<h2=int64#5
+# asm 2: adcq <a2_stack=72(%rsp),<h2=%r8
+adcq 72(%rsp),%r8
+
+# qhasm: carry? h3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<h3=int64#6
+# asm 2: adcq <a3_stack=80(%rsp),<h3=%r9
+adcq 80(%rsp),%r9
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#7
+# asm 2: mov $0,>addt0=%rax
+mov $0,%rax
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#8
+# asm 2: mov $38,>addt1=%r10
+mov $38,%r10
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#7,<addt1=int64#8
+# asm 2: cmovae <addt0=%rax,<addt1=%r10
+cmovae %rax,%r10
+
+# qhasm: carry? h0 += addt1
+# asm 1: add <addt1=int64#8,<h0=int64#3
+# asm 2: add <addt1=%r10,<h0=%rdx
+add %r10,%rdx
+
+# qhasm: carry? h1 += addt0 + carry
+# asm 1: adc <addt0=int64#7,<h1=int64#4
+# asm 2: adc <addt0=%rax,<h1=%rcx
+adc %rax,%rcx
+
+# qhasm: carry? h2 += addt0 + carry
+# asm 1: adc <addt0=int64#7,<h2=int64#5
+# asm 2: adc <addt0=%rax,<h2=%r8
+adc %rax,%r8
+
+# qhasm: carry? h3 += addt0 + carry
+# asm 1: adc <addt0=int64#7,<h3=int64#6
+# asm 2: adc <addt0=%rax,<h3=%r9
+adc %rax,%r9
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#8,<addt0=int64#7
+# asm 2: cmovc <addt1=%r10,<addt0=%rax
+cmovc %r10,%rax
+
+# qhasm: h0 += addt0
+# asm 1: add <addt0=int64#7,<h0=int64#3
+# asm 2: add <addt0=%rax,<h0=%rdx
+add %rax,%rdx
+
+# qhasm: h0_stack = h0
+# asm 1: movq <h0=int64#3,>h0_stack=stack64#8
+# asm 2: movq <h0=%rdx,>h0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: h1_stack = h1
+# asm 1: movq <h1=int64#4,>h1_stack=stack64#9
+# asm 2: movq <h1=%rcx,>h1_stack=64(%rsp)
+movq %rcx,64(%rsp)
+
+# qhasm: h2_stack = h2
+# asm 1: movq <h2=int64#5,>h2_stack=stack64#10
+# asm 2: movq <h2=%r8,>h2_stack=72(%rsp)
+movq %r8,72(%rsp)
+
+# qhasm: h3_stack = h3
+# asm 1: movq <h3=int64#6,>h3_stack=stack64#11
+# asm 2: movq <h3=%r9,>h3_stack=80(%rsp)
+movq %r9,80(%rsp)
+
+# qhasm: e0_stack = e0
+# asm 1: movq <e0=int64#10,>e0_stack=stack64#12
+# asm 2: movq <e0=%r12,>e0_stack=88(%rsp)
+movq %r12,88(%rsp)
+
+# qhasm: e1_stack = e1
+# asm 1: movq <e1=int64#11,>e1_stack=stack64#13
+# asm 2: movq <e1=%r13,>e1_stack=96(%rsp)
+movq %r13,96(%rsp)
+
+# qhasm: e2_stack = e2
+# asm 1: movq <e2=int64#12,>e2_stack=stack64#14
+# asm 2: movq <e2=%r14,>e2_stack=104(%rsp)
+movq %r14,104(%rsp)
+
+# qhasm: e3_stack = e3
+# asm 1: movq <e3=int64#13,>e3_stack=stack64#15
+# asm 2: movq <e3=%r15,>e3_stack=112(%rsp)
+movq %r15,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(rp + 96)
+# asm 1: movq 96(<rp=int64#1),>mulx0=int64#9
+# asm 2: movq 96(<rp=%rdi),>mulx0=%r11
+movq 96(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#10
+# asm 2: mov <mulrax=%rax,>c0=%r12
+mov %rax,%r12
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#11
+# asm 2: mov <mulrdx=%rdx,>c1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#11
+# asm 2: add <mulrax=%rax,<c1=%r13
+add %rax,%r13
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#12
+# asm 2: mov $0,>c2=%r14
+mov $0,%r14
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#12
+# asm 2: adc <mulrdx=%rdx,<c2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#12
+# asm 2: add <mulrax=%rax,<c2=%r14
+add %rax,%r14
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#13
+# asm 2: mov $0,>c3=%r15
+mov $0,%r15
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#13
+# asm 2: adc <mulrdx=%rdx,<c3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(rp + 104)
+# asm 1: movq 104(<rp=int64#1),>mulx1=int64#9
+# asm 2: movq 104(<rp=%rdi),>mulx1=%r11
+movq 104(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#11
+# asm 2: add <mulrax=%rax,<c1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#12
+# asm 2: add <mulrax=%rax,<c2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#14,<c2=int64#12
+# asm 2: add <mulc=%rbx,<c2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#14,<c3=int64#13
+# asm 2: add <mulc=%rbx,<c3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(rp + 112)
+# asm 1: movq 112(<rp=int64#1),>mulx2=int64#9
+# asm 2: movq 112(<rp=%rdi),>mulx2=%r11
+movq 112(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#12
+# asm 2: add <mulrax=%rax,<c2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#14,<c3=int64#13
+# asm 2: add <mulc=%rbx,<c3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(rp + 120)
+# asm 1: movq 120(<rp=int64#1),>mulx3=int64#9
+# asm 2: movq 120(<rp=%rdi),>mulx3=%r11
+movq 120(%rdi),%r11
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#13
+# asm 2: add <mulrax=%rax,<c3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#2,<c0=int64#10
+# asm 2: add <mulr4=%rsi,<c0=%r12
+add %rsi,%r12
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<c1=int64#11
+# asm 2: adc <mulr5=%rcx,<c1=%r13
+adc %rcx,%r13
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<c2=int64#12
+# asm 2: adc <mulr6=%r8,<c2=%r14
+adc %r8,%r14
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<c3=int64#13
+# asm 2: adc <mulr7=%r9,<c3=%r15
+adc %r9,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#3,<c0=int64#10
+# asm 2: add <mulr8=%rdx,<c0=%r12
+add %rdx,%r12
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<c1=int64#11
+# asm 2: adc <mulzero=%rsi,<c1=%r13
+adc %rsi,%r13
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<c2=int64#12
+# asm 2: adc <mulzero=%rsi,<c2=%r14
+adc %rsi,%r14
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<c3=int64#13
+# asm 2: adc <mulzero=%rsi,<c3=%r15
+adc %rsi,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#2,<c0=int64#10
+# asm 2: add <mulzero=%rsi,<c0=%r12
+add %rsi,%r12
+
+# qhasm: f0 = *(uint64 *)(rp + 64)
+# asm 1: movq 64(<rp=int64#1),>f0=int64#2
+# asm 2: movq 64(<rp=%rdi),>f0=%rsi
+movq 64(%rdi),%rsi
+
+# qhasm: f1 = *(uint64 *)(rp + 72)
+# asm 1: movq 72(<rp=int64#1),>f1=int64#3
+# asm 2: movq 72(<rp=%rdi),>f1=%rdx
+movq 72(%rdi),%rdx
+
+# qhasm: f2 = *(uint64 *)(rp + 80)
+# asm 1: movq 80(<rp=int64#1),>f2=int64#4
+# asm 2: movq 80(<rp=%rdi),>f2=%rcx
+movq 80(%rdi),%rcx
+
+# qhasm: f3 = *(uint64 *)(rp + 88)
+# asm 1: movq 88(<rp=int64#1),>f3=int64#5
+# asm 2: movq 88(<rp=%rdi),>f3=%r8
+movq 88(%rdi),%r8
+
+# qhasm: carry? f0 += f0
+# asm 1: add <f0=int64#2,<f0=int64#2
+# asm 2: add <f0=%rsi,<f0=%rsi
+add %rsi,%rsi
+
+# qhasm: carry? f1 += f1 + carry
+# asm 1: adc <f1=int64#3,<f1=int64#3
+# asm 2: adc <f1=%rdx,<f1=%rdx
+adc %rdx,%rdx
+
+# qhasm: carry? f2 += f2 + carry
+# asm 1: adc <f2=int64#4,<f2=int64#4
+# asm 2: adc <f2=%rcx,<f2=%rcx
+adc %rcx,%rcx
+
+# qhasm: carry? f3 += f3 + carry
+# asm 1: adc <f3=int64#5,<f3=int64#5
+# asm 2: adc <f3=%r8,<f3=%r8
+adc %r8,%r8
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? f0 += addt1
+# asm 1: add <addt1=int64#7,<f0=int64#2
+# asm 2: add <addt1=%rax,<f0=%rsi
+add %rax,%rsi
+
+# qhasm: carry? f1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f1=int64#3
+# asm 2: adc <addt0=%r9,<f1=%rdx
+adc %r9,%rdx
+
+# qhasm: carry? f2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f2=int64#4
+# asm 2: adc <addt0=%r9,<f2=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? f3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f3=int64#5
+# asm 2: adc <addt0=%r9,<f3=%r8
+adc %r9,%r8
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: f0 += addt0
+# asm 1: add <addt0=int64#6,<f0=int64#2
+# asm 2: add <addt0=%r9,<f0=%rsi
+add %r9,%rsi
+
+# qhasm: g0 = f0
+# asm 1: mov <f0=int64#2,>g0=int64#6
+# asm 2: mov <f0=%rsi,>g0=%r9
+mov %rsi,%r9
+
+# qhasm: g1 = f1
+# asm 1: mov <f1=int64#3,>g1=int64#7
+# asm 2: mov <f1=%rdx,>g1=%rax
+mov %rdx,%rax
+
+# qhasm: g2 = f2
+# asm 1: mov <f2=int64#4,>g2=int64#8
+# asm 2: mov <f2=%rcx,>g2=%r10
+mov %rcx,%r10
+
+# qhasm: g3 = f3
+# asm 1: mov <f3=int64#5,>g3=int64#9
+# asm 2: mov <f3=%r8,>g3=%r11
+mov %r8,%r11
+
+# qhasm: carry? f0 -= c0
+# asm 1: sub <c0=int64#10,<f0=int64#2
+# asm 2: sub <c0=%r12,<f0=%rsi
+sub %r12,%rsi
+
+# qhasm: carry? f1 -= c1 - carry
+# asm 1: sbb <c1=int64#11,<f1=int64#3
+# asm 2: sbb <c1=%r13,<f1=%rdx
+sbb %r13,%rdx
+
+# qhasm: carry? f2 -= c2 - carry
+# asm 1: sbb <c2=int64#12,<f2=int64#4
+# asm 2: sbb <c2=%r14,<f2=%rcx
+sbb %r14,%rcx
+
+# qhasm: carry? f3 -= c3 - carry
+# asm 1: sbb <c3=int64#13,<f3=int64#5
+# asm 2: sbb <c3=%r15,<f3=%r8
+sbb %r15,%r8
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#14
+# asm 2: mov $0,>subt0=%rbx
+mov $0,%rbx
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#15
+# asm 2: mov $38,>subt1=%rbp
+mov $38,%rbp
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#14,<subt1=int64#15
+# asm 2: cmovae <subt0=%rbx,<subt1=%rbp
+cmovae %rbx,%rbp
+
+# qhasm: carry? f0 -= subt1
+# asm 1: sub <subt1=int64#15,<f0=int64#2
+# asm 2: sub <subt1=%rbp,<f0=%rsi
+sub %rbp,%rsi
+
+# qhasm: carry? f1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#14,<f1=int64#3
+# asm 2: sbb <subt0=%rbx,<f1=%rdx
+sbb %rbx,%rdx
+
+# qhasm: carry? f2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#14,<f2=int64#4
+# asm 2: sbb <subt0=%rbx,<f2=%rcx
+sbb %rbx,%rcx
+
+# qhasm: carry? f3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#14,<f3=int64#5
+# asm 2: sbb <subt0=%rbx,<f3=%r8
+sbb %rbx,%r8
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#15,<subt0=int64#14
+# asm 2: cmovc <subt1=%rbp,<subt0=%rbx
+cmovc %rbp,%rbx
+
+# qhasm: f0 -= subt0
+# asm 1: sub <subt0=int64#14,<f0=int64#2
+# asm 2: sub <subt0=%rbx,<f0=%rsi
+sub %rbx,%rsi
+
+# qhasm: carry? g0 += c0
+# asm 1: add <c0=int64#10,<g0=int64#6
+# asm 2: add <c0=%r12,<g0=%r9
+add %r12,%r9
+
+# qhasm: carry? g1 += c1 + carry
+# asm 1: adc <c1=int64#11,<g1=int64#7
+# asm 2: adc <c1=%r13,<g1=%rax
+adc %r13,%rax
+
+# qhasm: carry? g2 += c2 + carry
+# asm 1: adc <c2=int64#12,<g2=int64#8
+# asm 2: adc <c2=%r14,<g2=%r10
+adc %r14,%r10
+
+# qhasm: carry? g3 += c3 + carry
+# asm 1: adc <c3=int64#13,<g3=int64#9
+# asm 2: adc <c3=%r15,<g3=%r11
+adc %r15,%r11
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#10
+# asm 2: mov $0,>addt0=%r12
+mov $0,%r12
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#11
+# asm 2: mov $38,>addt1=%r13
+mov $38,%r13
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#10,<addt1=int64#11
+# asm 2: cmovae <addt0=%r12,<addt1=%r13
+cmovae %r12,%r13
+
+# qhasm: carry? g0 += addt1
+# asm 1: add <addt1=int64#11,<g0=int64#6
+# asm 2: add <addt1=%r13,<g0=%r9
+add %r13,%r9
+
+# qhasm: carry? g1 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g1=int64#7
+# asm 2: adc <addt0=%r12,<g1=%rax
+adc %r12,%rax
+
+# qhasm: carry? g2 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g2=int64#8
+# asm 2: adc <addt0=%r12,<g2=%r10
+adc %r12,%r10
+
+# qhasm: carry? g3 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g3=int64#9
+# asm 2: adc <addt0=%r12,<g3=%r11
+adc %r12,%r11
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#11,<addt0=int64#10
+# asm 2: cmovc <addt1=%r13,<addt0=%r12
+cmovc %r13,%r12
+
+# qhasm: g0 += addt0
+# asm 1: add <addt0=int64#10,<g0=int64#6
+# asm 2: add <addt0=%r12,<g0=%r9
+add %r12,%r9
+
+# qhasm: g0_stack = g0
+# asm 1: movq <g0=int64#6,>g0_stack=stack64#16
+# asm 2: movq <g0=%r9,>g0_stack=120(%rsp)
+movq %r9,120(%rsp)
+
+# qhasm: g1_stack = g1
+# asm 1: movq <g1=int64#7,>g1_stack=stack64#17
+# asm 2: movq <g1=%rax,>g1_stack=128(%rsp)
+movq %rax,128(%rsp)
+
+# qhasm: g2_stack = g2
+# asm 1: movq <g2=int64#8,>g2_stack=stack64#18
+# asm 2: movq <g2=%r10,>g2_stack=136(%rsp)
+movq %r10,136(%rsp)
+
+# qhasm: g3_stack = g3
+# asm 1: movq <g3=int64#9,>g3_stack=stack64#19
+# asm 2: movq <g3=%r11,>g3_stack=144(%rsp)
+movq %r11,144(%rsp)
+
+# qhasm: f0_stack = f0
+# asm 1: movq <f0=int64#2,>f0_stack=stack64#20
+# asm 2: movq <f0=%rsi,>f0_stack=152(%rsp)
+movq %rsi,152(%rsp)
+
+# qhasm: f1_stack = f1
+# asm 1: movq <f1=int64#3,>f1_stack=stack64#21
+# asm 2: movq <f1=%rdx,>f1_stack=160(%rsp)
+movq %rdx,160(%rsp)
+
+# qhasm: f2_stack = f2
+# asm 1: movq <f2=int64#4,>f2_stack=stack64#22
+# asm 2: movq <f2=%rcx,>f2_stack=168(%rsp)
+movq %rcx,168(%rsp)
+
+# qhasm: f3_stack = f3
+# asm 1: movq <f3=int64#5,>f3_stack=stack64#23
+# asm 2: movq <f3=%r8,>f3_stack=176(%rsp)
+movq %r8,176(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = e0_stack
+# asm 1: movq <e0_stack=stack64#12,>mulx0=int64#8
+# asm 2: movq <e0_stack=88(%rsp),>mulx0=%r10
+movq 88(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#9
+# asm 2: mov <mulrax=%rax,>rx0=%r11
+mov %rax,%r11
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#10
+# asm 2: mov <mulrdx=%rdx,>rx1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#10
+# asm 2: add <mulrax=%rax,<rx1=%r12
+add %rax,%r12
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#11
+# asm 2: mov $0,>rx2=%r13
+mov $0,%r13
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#11
+# asm 2: adc <mulrdx=%rdx,<rx2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#11
+# asm 2: add <mulrax=%rax,<rx2=%r13
+add %rax,%r13
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#12
+# asm 2: mov $0,>rx3=%r14
+mov $0,%r14
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#12
+# asm 2: adc <mulrdx=%rdx,<rx3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = e1_stack
+# asm 1: movq <e1_stack=stack64#13,>mulx1=int64#8
+# asm 2: movq <e1_stack=96(%rsp),>mulx1=%r10
+movq 96(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#10
+# asm 2: add <mulrax=%rax,<rx1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#11
+# asm 2: add <mulrax=%rax,<rx2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#13,<rx2=int64#11
+# asm 2: add <mulc=%r15,<rx2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#13,<rx3=int64#12
+# asm 2: add <mulc=%r15,<rx3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = e2_stack
+# asm 1: movq <e2_stack=stack64#14,>mulx2=int64#8
+# asm 2: movq <e2_stack=104(%rsp),>mulx2=%r10
+movq 104(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#11
+# asm 2: add <mulrax=%rax,<rx2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#13,<rx3=int64#12
+# asm 2: add <mulc=%r15,<rx3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = e3_stack
+# asm 1: movq <e3_stack=stack64#15,>mulx3=int64#8
+# asm 2: movq <e3_stack=112(%rsp),>mulx3=%r10
+movq 112(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#12
+# asm 2: add <mulrax=%rax,<rx3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#2,<rx0=int64#9
+# asm 2: add <mulr4=%rsi,<rx0=%r11
+add %rsi,%r11
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rx1=int64#10
+# asm 2: adc <mulr5=%rcx,<rx1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rx2=int64#11
+# asm 2: adc <mulr6=%r8,<rx2=%r13
+adc %r8,%r13
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rx3=int64#12
+# asm 2: adc <mulr7=%r9,<rx3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#3,<rx0=int64#9
+# asm 2: add <mulr8=%rdx,<rx0=%r11
+add %rdx,%r11
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rx1=int64#10
+# asm 2: adc <mulzero=%rsi,<rx1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rx2=int64#11
+# asm 2: adc <mulzero=%rsi,<rx2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rx3=int64#12
+# asm 2: adc <mulzero=%rsi,<rx3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#2,<rx0=int64#9
+# asm 2: add <mulzero=%rsi,<rx0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#9,0(<rp=int64#1)
+# asm 2: movq <rx0=%r11,0(<rp=%rdi)
+movq %r11,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#10,8(<rp=int64#1)
+# asm 2: movq <rx1=%r12,8(<rp=%rdi)
+movq %r12,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#11,16(<rp=int64#1)
+# asm 2: movq <rx2=%r13,16(<rp=%rdi)
+movq %r13,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#12,24(<rp=int64#1)
+# asm 2: movq <rx3=%r14,24(<rp=%rdi)
+movq %r14,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulx0=int64#8
+# asm 2: movq <h0_stack=56(%rsp),>mulx0=%r10
+movq 56(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: ry0 = mulrax
+# asm 1: mov <mulrax=int64#7,>ry0=int64#9
+# asm 2: mov <mulrax=%rax,>ry0=%r11
+mov %rax,%r11
+
+# qhasm: ry1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>ry1=int64#10
+# asm 2: mov <mulrdx=%rdx,>ry1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#10
+# asm 2: add <mulrax=%rax,<ry1=%r12
+add %rax,%r12
+
+# qhasm: ry2 = 0
+# asm 1: mov $0,>ry2=int64#11
+# asm 2: mov $0,>ry2=%r13
+mov $0,%r13
+
+# qhasm: ry2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry2=int64#11
+# asm 2: adc <mulrdx=%rdx,<ry2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#11
+# asm 2: add <mulrax=%rax,<ry2=%r13
+add %rax,%r13
+
+# qhasm: ry3 = 0
+# asm 1: mov $0,>ry3=int64#12
+# asm 2: mov $0,>ry3=%r14
+mov $0,%r14
+
+# qhasm: ry3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry3=int64#12
+# asm 2: adc <mulrdx=%rdx,<ry3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulx1=int64#8
+# asm 2: movq <h1_stack=64(%rsp),>mulx1=%r10
+movq 64(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#10
+# asm 2: add <mulrax=%rax,<ry1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#11
+# asm 2: add <mulrax=%rax,<ry2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry2 += mulc
+# asm 1: add <mulc=int64#13,<ry2=int64#11
+# asm 2: add <mulc=%r15,<ry2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#13,<ry3=int64#12
+# asm 2: add <mulc=%r15,<ry3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulx2=int64#8
+# asm 2: movq <h2_stack=72(%rsp),>mulx2=%r10
+movq 72(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#11
+# asm 2: add <mulrax=%rax,<ry2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#13,<ry3=int64#12
+# asm 2: add <mulc=%r15,<ry3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulx3=int64#8
+# asm 2: movq <h3_stack=80(%rsp),>mulx3=%r10
+movq 80(%rsp),%r10
+
+# qhasm: mulrax = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulrax=int64#7
+# asm 2: movq <g0_stack=120(%rsp),>mulrax=%rax
+movq 120(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#12
+# asm 2: add <mulrax=%rax,<ry3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulrax=int64#7
+# asm 2: movq <g1_stack=128(%rsp),>mulrax=%rax
+movq 128(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulrax=int64#7
+# asm 2: movq <g2_stack=136(%rsp),>mulrax=%rax
+movq 136(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulrax=int64#7
+# asm 2: movq <g3_stack=144(%rsp),>mulrax=%rax
+movq 144(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? ry0 += mulr4
+# asm 1: add <mulr4=int64#2,<ry0=int64#9
+# asm 2: add <mulr4=%rsi,<ry0=%r11
+add %rsi,%r11
+
+# qhasm: carry? ry1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<ry1=int64#10
+# asm 2: adc <mulr5=%rcx,<ry1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? ry2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<ry2=int64#11
+# asm 2: adc <mulr6=%r8,<ry2=%r13
+adc %r8,%r13
+
+# qhasm: carry? ry3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<ry3=int64#12
+# asm 2: adc <mulr7=%r9,<ry3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? ry0 += mulr8
+# asm 1: add <mulr8=int64#3,<ry0=int64#9
+# asm 2: add <mulr8=%rdx,<ry0=%r11
+add %rdx,%r11
+
+# qhasm: carry? ry1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<ry1=int64#10
+# asm 2: adc <mulzero=%rsi,<ry1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? ry2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<ry2=int64#11
+# asm 2: adc <mulzero=%rsi,<ry2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? ry3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<ry3=int64#12
+# asm 2: adc <mulzero=%rsi,<ry3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: ry0 += mulzero
+# asm 1: add <mulzero=int64#2,<ry0=int64#9
+# asm 2: add <mulzero=%rsi,<ry0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 32) = ry0
+# asm 1: movq <ry0=int64#9,32(<rp=int64#1)
+# asm 2: movq <ry0=%r11,32(<rp=%rdi)
+movq %r11,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = ry1
+# asm 1: movq <ry1=int64#10,40(<rp=int64#1)
+# asm 2: movq <ry1=%r12,40(<rp=%rdi)
+movq %r12,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = ry2
+# asm 1: movq <ry2=int64#11,48(<rp=int64#1)
+# asm 2: movq <ry2=%r13,48(<rp=%rdi)
+movq %r13,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = ry3
+# asm 1: movq <ry3=int64#12,56(<rp=int64#1)
+# asm 2: movq <ry3=%r14,56(<rp=%rdi)
+movq %r14,56(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = g0_stack
+# asm 1: movq <g0_stack=stack64#16,>mulx0=int64#8
+# asm 2: movq <g0_stack=120(%rsp),>mulx0=%r10
+movq 120(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: rz0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rz0=int64#9
+# asm 2: mov <mulrax=%rax,>rz0=%r11
+mov %rax,%r11
+
+# qhasm: rz1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rz1=int64#10
+# asm 2: mov <mulrdx=%rdx,>rz1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#10
+# asm 2: add <mulrax=%rax,<rz1=%r12
+add %rax,%r12
+
+# qhasm: rz2 = 0
+# asm 1: mov $0,>rz2=int64#11
+# asm 2: mov $0,>rz2=%r13
+mov $0,%r13
+
+# qhasm: rz2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz2=int64#11
+# asm 2: adc <mulrdx=%rdx,<rz2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#11
+# asm 2: add <mulrax=%rax,<rz2=%r13
+add %rax,%r13
+
+# qhasm: rz3 = 0
+# asm 1: mov $0,>rz3=int64#12
+# asm 2: mov $0,>rz3=%r14
+mov $0,%r14
+
+# qhasm: rz3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz3=int64#12
+# asm 2: adc <mulrdx=%rdx,<rz3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = g1_stack
+# asm 1: movq <g1_stack=stack64#17,>mulx1=int64#8
+# asm 2: movq <g1_stack=128(%rsp),>mulx1=%r10
+movq 128(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#10
+# asm 2: add <mulrax=%rax,<rz1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#11
+# asm 2: add <mulrax=%rax,<rz2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz2 += mulc
+# asm 1: add <mulc=int64#13,<rz2=int64#11
+# asm 2: add <mulc=%r15,<rz2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#13,<rz3=int64#12
+# asm 2: add <mulc=%r15,<rz3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = g2_stack
+# asm 1: movq <g2_stack=stack64#18,>mulx2=int64#8
+# asm 2: movq <g2_stack=136(%rsp),>mulx2=%r10
+movq 136(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#11
+# asm 2: add <mulrax=%rax,<rz2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#13,<rz3=int64#12
+# asm 2: add <mulc=%r15,<rz3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = g3_stack
+# asm 1: movq <g3_stack=stack64#19,>mulx3=int64#8
+# asm 2: movq <g3_stack=144(%rsp),>mulx3=%r10
+movq 144(%rsp),%r10
+
+# qhasm: mulrax = f0_stack
+# asm 1: movq <f0_stack=stack64#20,>mulrax=int64#7
+# asm 2: movq <f0_stack=152(%rsp),>mulrax=%rax
+movq 152(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#12
+# asm 2: add <mulrax=%rax,<rz3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f1_stack
+# asm 1: movq <f1_stack=stack64#21,>mulrax=int64#7
+# asm 2: movq <f1_stack=160(%rsp),>mulrax=%rax
+movq 160(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f2_stack
+# asm 1: movq <f2_stack=stack64#22,>mulrax=int64#7
+# asm 2: movq <f2_stack=168(%rsp),>mulrax=%rax
+movq 168(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = f3_stack
+# asm 1: movq <f3_stack=stack64#23,>mulrax=int64#7
+# asm 2: movq <f3_stack=176(%rsp),>mulrax=%rax
+movq 176(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rz0 += mulr4
+# asm 1: add <mulr4=int64#2,<rz0=int64#9
+# asm 2: add <mulr4=%rsi,<rz0=%r11
+add %rsi,%r11
+
+# qhasm: carry? rz1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rz1=int64#10
+# asm 2: adc <mulr5=%rcx,<rz1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? rz2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rz2=int64#11
+# asm 2: adc <mulr6=%r8,<rz2=%r13
+adc %r8,%r13
+
+# qhasm: carry? rz3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rz3=int64#12
+# asm 2: adc <mulr7=%r9,<rz3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rz0 += mulr8
+# asm 1: add <mulr8=int64#3,<rz0=int64#9
+# asm 2: add <mulr8=%rdx,<rz0=%r11
+add %rdx,%r11
+
+# qhasm: carry? rz1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz1=int64#10
+# asm 2: adc <mulzero=%rsi,<rz1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? rz2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz2=int64#11
+# asm 2: adc <mulzero=%rsi,<rz2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rz3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz3=int64#12
+# asm 2: adc <mulzero=%rsi,<rz3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rz0 += mulzero
+# asm 1: add <mulzero=int64#2,<rz0=int64#9
+# asm 2: add <mulzero=%rsi,<rz0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 64) = rz0
+# asm 1: movq <rz0=int64#9,64(<rp=int64#1)
+# asm 2: movq <rz0=%r11,64(<rp=%rdi)
+movq %r11,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = rz1
+# asm 1: movq <rz1=int64#10,72(<rp=int64#1)
+# asm 2: movq <rz1=%r12,72(<rp=%rdi)
+movq %r12,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = rz2
+# asm 1: movq <rz2=int64#11,80(<rp=int64#1)
+# asm 2: movq <rz2=%r13,80(<rp=%rdi)
+movq %r13,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = rz3
+# asm 1: movq <rz3=int64#12,88(<rp=int64#1)
+# asm 2: movq <rz3=%r14,88(<rp=%rdi)
+movq %r14,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#2
+# asm 2: mov $0,>mulr4=%rsi
+mov $0,%rsi
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#4
+# asm 2: mov $0,>mulr5=%rcx
+mov $0,%rcx
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulx0 = e0_stack
+# asm 1: movq <e0_stack=stack64#12,>mulx0=int64#8
+# asm 2: movq <e0_stack=88(%rsp),>mulx0=%r10
+movq 88(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#9
+# asm 2: mov <mulrax=%rax,>rt0=%r11
+mov %rax,%r11
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#10
+# asm 2: mov <mulrdx=%rdx,>rt1=%r12
+mov %rdx,%r12
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#10
+# asm 2: add <mulrax=%rax,<rt1=%r12
+add %rax,%r12
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#11
+# asm 2: mov $0,>rt2=%r13
+mov $0,%r13
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#11
+# asm 2: adc <mulrdx=%rdx,<rt2=%r13
+adc %rdx,%r13
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#11
+# asm 2: add <mulrax=%rax,<rt2=%r13
+add %rax,%r13
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#12
+# asm 2: mov $0,>rt3=%r14
+mov $0,%r14
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#12
+# asm 2: adc <mulrdx=%rdx,<rt3=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#8
+# asm 2: mul <mulx0=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#2
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rsi
+adc %rdx,%rsi
+
+# qhasm: mulx1 = e1_stack
+# asm 1: movq <e1_stack=stack64#13,>mulx1=int64#8
+# asm 2: movq <e1_stack=96(%rsp),>mulx1=%r10
+movq 96(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#10
+# asm 2: add <mulrax=%rax,<rt1=%r12
+add %rax,%r12
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#11
+# asm 2: add <mulrax=%rax,<rt2=%r13
+add %rax,%r13
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#13,<rt2=int64#11
+# asm 2: add <mulc=%r15,<rt2=%r13
+add %r15,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#13,<rt3=int64#12
+# asm 2: add <mulc=%r15,<rt3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#8
+# asm 2: mul <mulx1=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr5=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx2 = e2_stack
+# asm 1: movq <e2_stack=stack64#14,>mulx2=int64#8
+# asm 2: movq <e2_stack=104(%rsp),>mulx2=%r10
+movq 104(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#11
+# asm 2: add <mulrax=%rax,<rt2=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#13,<rt3=int64#12
+# asm 2: add <mulc=%r15,<rt3=%r14
+add %r15,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#8
+# asm 2: mul <mulx2=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: mulx3 = e3_stack
+# asm 1: movq <e3_stack=stack64#15,>mulx3=int64#8
+# asm 2: movq <e3_stack=112(%rsp),>mulx3=%r10
+movq 112(%rsp),%r10
+
+# qhasm: mulrax = h0_stack
+# asm 1: movq <h0_stack=stack64#8,>mulrax=int64#7
+# asm 2: movq <h0_stack=56(%rsp),>mulrax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#12
+# asm 2: add <mulrax=%rax,<rt3=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h1_stack
+# asm 1: movq <h1_stack=stack64#9,>mulrax=int64#7
+# asm 2: movq <h1_stack=64(%rsp),>mulrax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#2
+# asm 2: add <mulrax=%rax,<mulr4=%rsi
+add %rax,%rsi
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#13,<mulr4=int64#2
+# asm 2: add <mulc=%r15,<mulr4=%rsi
+add %r15,%rsi
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h2_stack
+# asm 1: movq <h2_stack=stack64#10,>mulrax=int64#7
+# asm 2: movq <h2_stack=72(%rsp),>mulrax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#13,<mulr5=int64#4
+# asm 2: add <mulc=%r15,<mulr5=%rcx
+add %r15,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#13
+# asm 2: mov $0,>mulc=%r15
+mov $0,%r15
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#13
+# asm 2: adc <mulrdx=%rdx,<mulc=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = h3_stack
+# asm 1: movq <h3_stack=stack64#11,>mulrax=int64#7
+# asm 2: movq <h3_stack=80(%rsp),>mulrax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#8
+# asm 2: mul <mulx3=%r10
+mul %r10
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#13,<mulr6=int64#5
+# asm 2: add <mulc=%r15,<mulr6=%r8
+add %r15,%r8
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#2,>mulrax=int64#7
+# asm 2: mov <mulr4=%rsi,>mulrax=%rax
+mov %rsi,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr5=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr6=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr7=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#9
+# asm 2: add <mulr4=%rsi,<rt0=%r11
+add %rsi,%r11
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#10
+# asm 2: adc <mulr5=%rcx,<rt1=%r12
+adc %rcx,%r12
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#11
+# asm 2: adc <mulr6=%r8,<rt2=%r13
+adc %r8,%r13
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#12
+# asm 2: adc <mulr7=%r9,<rt3=%r14
+adc %r9,%r14
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#9
+# asm 2: add <mulr8=%rdx,<rt0=%r11
+add %rdx,%r11
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#10
+# asm 2: adc <mulzero=%rsi,<rt1=%r12
+adc %rsi,%r12
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#11
+# asm 2: adc <mulzero=%rsi,<rt2=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#12
+# asm 2: adc <mulzero=%rsi,<rt3=%r14
+adc %rsi,%r14
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#9
+# asm 2: add <mulzero=%rsi,<rt0=%r11
+add %rsi,%r11
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#9,96(<rp=int64#1)
+# asm 2: movq <rt0=%r11,96(<rp=%rdi)
+movq %r11,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#10,104(<rp=int64#1)
+# asm 2: movq <rt1=%r12,104(<rp=%rdi)
+movq %r12,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#11,112(<rp=int64#1)
+# asm 2: movq <rt2=%r13,112(<rp=%rdi)
+movq %r13,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#12,120(<rp=int64#1)
+# asm 2: movq <rt3=%r14,120(<rp=%rdi)
+movq %r14,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s b/ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s
new file mode 100644
index 00000000..b5629462
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.s
@@ -0,0 +1,3072 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 e0
+
+# qhasm: int64 e1
+
+# qhasm: int64 e2
+
+# qhasm: int64 e3
+
+# qhasm: stack64 e0_stack
+
+# qhasm: stack64 e1_stack
+
+# qhasm: stack64 e2_stack
+
+# qhasm: stack64 e3_stack
+
+# qhasm: int64 f0
+
+# qhasm: int64 f1
+
+# qhasm: int64 f2
+
+# qhasm: int64 f3
+
+# qhasm: stack64 f0_stack
+
+# qhasm: stack64 f1_stack
+
+# qhasm: stack64 f2_stack
+
+# qhasm: stack64 f3_stack
+
+# qhasm: int64 g0
+
+# qhasm: int64 g1
+
+# qhasm: int64 g2
+
+# qhasm: int64 g3
+
+# qhasm: stack64 g0_stack
+
+# qhasm: stack64 g1_stack
+
+# qhasm: stack64 g2_stack
+
+# qhasm: stack64 g3_stack
+
+# qhasm: int64 h0
+
+# qhasm: int64 h1
+
+# qhasm: int64 h2
+
+# qhasm: int64 h3
+
+# qhasm: stack64 h0_stack
+
+# qhasm: stack64 h1_stack
+
+# qhasm: stack64 h2_stack
+
+# qhasm: stack64 h3_stack
+
+# qhasm: int64 qt0
+
+# qhasm: int64 qt1
+
+# qhasm: int64 qt2
+
+# qhasm: int64 qt3
+
+# qhasm: stack64 qt0_stack
+
+# qhasm: stack64 qt1_stack
+
+# qhasm: stack64 qt2_stack
+
+# qhasm: stack64 qt3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_nielsadd_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $128,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: qp = qp
+# asm 1: mov <qp=int64#3,>qp=int64#4
+# asm 2: mov <qp=%rdx,>qp=%rcx
+mov %rdx,%rcx
+
+# qhasm: a0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>a0=int64#3
+# asm 2: movq 32(<pp=%rsi),>a0=%rdx
+movq 32(%rsi),%rdx
+
+# qhasm: a1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>a1=int64#5
+# asm 2: movq 40(<pp=%rsi),>a1=%r8
+movq 40(%rsi),%r8
+
+# qhasm: a2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>a2=int64#6
+# asm 2: movq 48(<pp=%rsi),>a2=%r9
+movq 48(%rsi),%r9
+
+# qhasm: a3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>a3=int64#7
+# asm 2: movq 56(<pp=%rsi),>a3=%rax
+movq 56(%rsi),%rax
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#8
+# asm 2: mov <a0=%rdx,>b0=%r10
+mov %rdx,%r10
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#5,>b1=int64#9
+# asm 2: mov <a1=%r8,>b1=%r11
+mov %r8,%r11
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#6,>b2=int64#10
+# asm 2: mov <a2=%r9,>b2=%r12
+mov %r9,%r12
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#7,>b3=int64#11
+# asm 2: mov <a3=%rax,>b3=%r13
+mov %rax,%r13
+
+# qhasm: carry? a0 -= *(uint64 *) (pp + 0)
+# asm 1: subq 0(<pp=int64#2),<a0=int64#3
+# asm 2: subq 0(<pp=%rsi),<a0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *) (pp + 8) - carry
+# asm 1: sbbq 8(<pp=int64#2),<a1=int64#5
+# asm 2: sbbq 8(<pp=%rsi),<a1=%r8
+sbbq 8(%rsi),%r8
+
+# qhasm: carry? a2 -= *(uint64 *) (pp + 16) - carry
+# asm 1: sbbq 16(<pp=int64#2),<a2=int64#6
+# asm 2: sbbq 16(<pp=%rsi),<a2=%r9
+sbbq 16(%rsi),%r9
+
+# qhasm: carry? a3 -= *(uint64 *) (pp + 24) - carry
+# asm 1: sbbq 24(<pp=int64#2),<a3=int64#7
+# asm 2: sbbq 24(<pp=%rsi),<a3=%rax
+sbbq 24(%rsi),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#13,<a0=int64#3
+# asm 2: sub <subt1=%r15,<a0=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a1=int64#5
+# asm 2: sbb <subt0=%r14,<a1=%r8
+sbb %r14,%r8
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a2=int64#6
+# asm 2: sbb <subt0=%r14,<a2=%r9
+sbb %r14,%r9
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a3=int64#7
+# asm 2: sbb <subt0=%r14,<a3=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#12,<a0=int64#3
+# asm 2: sub <subt0=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? b0 += *(uint64 *) (pp + 0)
+# asm 1: addq 0(<pp=int64#2),<b0=int64#8
+# asm 2: addq 0(<pp=%rsi),<b0=%r10
+addq 0(%rsi),%r10
+
+# qhasm: carry? b1 += *(uint64 *) (pp + 8) + carry
+# asm 1: adcq 8(<pp=int64#2),<b1=int64#9
+# asm 2: adcq 8(<pp=%rsi),<b1=%r11
+adcq 8(%rsi),%r11
+
+# qhasm: carry? b2 += *(uint64 *) (pp + 16) + carry
+# asm 1: adcq 16(<pp=int64#2),<b2=int64#10
+# asm 2: adcq 16(<pp=%rsi),<b2=%r12
+adcq 16(%rsi),%r12
+
+# qhasm: carry? b3 += *(uint64 *) (pp + 24) + carry
+# asm 1: adcq 24(<pp=int64#2),<b3=int64#11
+# asm 2: adcq 24(<pp=%rsi),<b3=%r13
+adcq 24(%rsi),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#13,<b0=int64#8
+# asm 2: add <addt1=%r15,<b0=%r10
+add %r15,%r10
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b1=int64#9
+# asm 2: adc <addt0=%r14,<b1=%r11
+adc %r14,%r11
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b2=int64#10
+# asm 2: adc <addt0=%r14,<b2=%r12
+adc %r14,%r12
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b3=int64#11
+# asm 2: adc <addt0=%r14,<b3=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#12,<b0=int64#8
+# asm 2: add <addt0=%r14,<b0=%r10
+add %r14,%r10
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#7,>a3_stack=stack64#11
+# asm 2: movq <a3=%rax,>a3_stack=80(%rsp)
+movq %rax,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#8,>b0_stack=stack64#12
+# asm 2: movq <b0=%r10,>b0_stack=88(%rsp)
+movq %r10,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#9,>b1_stack=stack64#13
+# asm 2: movq <b1=%r11,>b1_stack=96(%rsp)
+movq %r11,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#10,>b2_stack=stack64#14
+# asm 2: movq <b2=%r12,>b2_stack=104(%rsp)
+movq %r12,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#11,>b3_stack=stack64#15
+# asm 2: movq <b3=%r13,>b3_stack=112(%rsp)
+movq %r13,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#11
+# asm 2: mov <mulrax=%rax,>a0=%r13
+mov %rax,%r13
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#12
+# asm 2: mov <mulrdx=%rdx,>a1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#13
+# asm 2: mov $0,>a2=%r15
+mov $0,%r15
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#13
+# asm 2: adc <mulrdx=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#14
+# asm 2: mov $0,>a3=%rbx
+mov $0,%rbx
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#14
+# asm 2: adc <mulrdx=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#15,<a2=int64#13
+# asm 2: add <mulc=%rbp,<a2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#5,<a0=int64#11
+# asm 2: add <mulr4=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<a1=int64#12
+# asm 2: adc <mulr5=%r9,<a1=%r14
+adc %r9,%r14
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<a2=int64#13
+# asm 2: adc <mulr6=%r10,<a2=%r15
+adc %r10,%r15
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<a3=int64#14
+# asm 2: adc <mulr7=%r11,<a3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#5,<a0=int64#11
+# asm 2: add <mulr8=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#12
+# asm 2: adc <mulzero=%rdx,<a1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#13
+# asm 2: adc <mulzero=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#14
+# asm 2: adc <mulzero=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#11
+# asm 2: add <mulzero=%rdx,<a0=%r13
+add %rdx,%r13
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#11,>a0_stack=stack64#8
+# asm 2: movq <a0=%r13,>a0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#12,>a1_stack=stack64#9
+# asm 2: movq <a1=%r14,>a1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#13,>a2_stack=stack64#10
+# asm 2: movq <a2=%r15,>a2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#14,>a3_stack=stack64#11
+# asm 2: movq <a3=%rbx,>a3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#10
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r12
+movq 88(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: e0 = mulrax
+# asm 1: mov <mulrax=int64#7,>e0=int64#11
+# asm 2: mov <mulrax=%rax,>e0=%r13
+mov %rax,%r13
+
+# qhasm: e1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>e1=int64#12
+# asm 2: mov <mulrdx=%rdx,>e1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#12
+# asm 2: add <mulrax=%rax,<e1=%r14
+add %rax,%r14
+
+# qhasm: e2 = 0
+# asm 1: mov $0,>e2=int64#13
+# asm 2: mov $0,>e2=%r15
+mov $0,%r15
+
+# qhasm: e2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e2=int64#13
+# asm 2: adc <mulrdx=%rdx,<e2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#13
+# asm 2: add <mulrax=%rax,<e2=%r15
+add %rax,%r15
+
+# qhasm: e3 = 0
+# asm 1: mov $0,>e3=int64#14
+# asm 2: mov $0,>e3=%rbx
+mov $0,%rbx
+
+# qhasm: e3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<e3=int64#14
+# asm 2: adc <mulrdx=%rdx,<e3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#10
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r12
+movq 96(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? e1 += mulrax
+# asm 1: add <mulrax=int64#7,<e1=int64#12
+# asm 2: add <mulrax=%rax,<e1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#13
+# asm 2: add <mulrax=%rax,<e2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e2 += mulc
+# asm 1: add <mulc=int64#15,<e2=int64#13
+# asm 2: add <mulc=%rbp,<e2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#15,<e3=int64#14
+# asm 2: add <mulc=%rbp,<e3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#10
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r12
+movq 104(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? e2 += mulrax
+# asm 1: add <mulrax=int64#7,<e2=int64#13
+# asm 2: add <mulrax=%rax,<e2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? e3 += mulc
+# asm 1: add <mulc=int64#15,<e3=int64#14
+# asm 2: add <mulc=%rbp,<e3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#10
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r12
+movq 112(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? e3 += mulrax
+# asm 1: add <mulrax=int64#7,<e3=int64#14
+# asm 2: add <mulrax=%rax,<e3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? e0 += mulr4
+# asm 1: add <mulr4=int64#5,<e0=int64#11
+# asm 2: add <mulr4=%r8,<e0=%r13
+add %r8,%r13
+
+# qhasm: carry? e1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<e1=int64#12
+# asm 2: adc <mulr5=%r9,<e1=%r14
+adc %r9,%r14
+
+# qhasm: carry? e2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<e2=int64#13
+# asm 2: adc <mulr6=%r10,<e2=%r15
+adc %r10,%r15
+
+# qhasm: carry? e3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<e3=int64#14
+# asm 2: adc <mulr7=%r11,<e3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? e0 += mulr8
+# asm 1: add <mulr8=int64#5,<e0=int64#11
+# asm 2: add <mulr8=%r8,<e0=%r13
+add %r8,%r13
+
+# qhasm: carry? e1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e1=int64#12
+# asm 2: adc <mulzero=%rdx,<e1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? e2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e2=int64#13
+# asm 2: adc <mulzero=%rdx,<e2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? e3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<e3=int64#14
+# asm 2: adc <mulzero=%rdx,<e3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: e0 += mulzero
+# asm 1: add <mulzero=int64#3,<e0=int64#11
+# asm 2: add <mulzero=%rdx,<e0=%r13
+add %rdx,%r13
+
+# qhasm: h0 = e0
+# asm 1: mov <e0=int64#11,>h0=int64#3
+# asm 2: mov <e0=%r13,>h0=%rdx
+mov %r13,%rdx
+
+# qhasm: h1 = e1
+# asm 1: mov <e1=int64#12,>h1=int64#5
+# asm 2: mov <e1=%r14,>h1=%r8
+mov %r14,%r8
+
+# qhasm: h2 = e2
+# asm 1: mov <e2=int64#13,>h2=int64#6
+# asm 2: mov <e2=%r15,>h2=%r9
+mov %r15,%r9
+
+# qhasm: h3 = e3
+# asm 1: mov <e3=int64#14,>h3=int64#7
+# asm 2: mov <e3=%rbx,>h3=%rax
+mov %rbx,%rax
+
+# qhasm: carry? e0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<e0=int64#11
+# asm 2: subq <a0_stack=56(%rsp),<e0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? e1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<e1=int64#12
+# asm 2: sbbq <a1_stack=64(%rsp),<e1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? e2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<e2=int64#13
+# asm 2: sbbq <a2_stack=72(%rsp),<e2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? e3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<e3=int64#14
+# asm 2: sbbq <a3_stack=80(%rsp),<e3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#8
+# asm 2: mov $0,>subt0=%r10
+mov $0,%r10
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#9
+# asm 2: mov $38,>subt1=%r11
+mov $38,%r11
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#8,<subt1=int64#9
+# asm 2: cmovae <subt0=%r10,<subt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? e0 -= subt1
+# asm 1: sub <subt1=int64#9,<e0=int64#11
+# asm 2: sub <subt1=%r11,<e0=%r13
+sub %r11,%r13
+
+# qhasm: carry? e1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<e1=int64#12
+# asm 2: sbb <subt0=%r10,<e1=%r14
+sbb %r10,%r14
+
+# qhasm: carry? e2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<e2=int64#13
+# asm 2: sbb <subt0=%r10,<e2=%r15
+sbb %r10,%r15
+
+# qhasm: carry? e3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<e3=int64#14
+# asm 2: sbb <subt0=%r10,<e3=%rbx
+sbb %r10,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#9,<subt0=int64#8
+# asm 2: cmovc <subt1=%r11,<subt0=%r10
+cmovc %r11,%r10
+
+# qhasm: e0 -= subt0
+# asm 1: sub <subt0=int64#8,<e0=int64#11
+# asm 2: sub <subt0=%r10,<e0=%r13
+sub %r10,%r13
+
+# qhasm: carry? h0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<h0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<h0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? h1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<h1=int64#5
+# asm 2: adcq <a1_stack=64(%rsp),<h1=%r8
+adcq 64(%rsp),%r8
+
+# qhasm: carry? h2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<h2=int64#6
+# asm 2: adcq <a2_stack=72(%rsp),<h2=%r9
+adcq 72(%rsp),%r9
+
+# qhasm: carry? h3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<h3=int64#7
+# asm 2: adcq <a3_stack=80(%rsp),<h3=%rax
+adcq 80(%rsp),%rax
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#8
+# asm 2: mov $0,>addt0=%r10
+mov $0,%r10
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#9
+# asm 2: mov $38,>addt1=%r11
+mov $38,%r11
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#8,<addt1=int64#9
+# asm 2: cmovae <addt0=%r10,<addt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? h0 += addt1
+# asm 1: add <addt1=int64#9,<h0=int64#3
+# asm 2: add <addt1=%r11,<h0=%rdx
+add %r11,%rdx
+
+# qhasm: carry? h1 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<h1=int64#5
+# asm 2: adc <addt0=%r10,<h1=%r8
+adc %r10,%r8
+
+# qhasm: carry? h2 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<h2=int64#6
+# asm 2: adc <addt0=%r10,<h2=%r9
+adc %r10,%r9
+
+# qhasm: carry? h3 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<h3=int64#7
+# asm 2: adc <addt0=%r10,<h3=%rax
+adc %r10,%rax
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#9,<addt0=int64#8
+# asm 2: cmovc <addt1=%r11,<addt0=%r10
+cmovc %r11,%r10
+
+# qhasm: h0 += addt0
+# asm 1: add <addt0=int64#8,<h0=int64#3
+# asm 2: add <addt0=%r10,<h0=%rdx
+add %r10,%rdx
+
+# qhasm: *(uint64 *)(rp + 64) = h0
+# asm 1: movq <h0=int64#3,64(<rp=int64#1)
+# asm 2: movq <h0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = h1
+# asm 1: movq <h1=int64#5,72(<rp=int64#1)
+# asm 2: movq <h1=%r8,72(<rp=%rdi)
+movq %r8,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = h2
+# asm 1: movq <h2=int64#6,80(<rp=int64#1)
+# asm 2: movq <h2=%r9,80(<rp=%rdi)
+movq %r9,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = h3
+# asm 1: movq <h3=int64#7,88(<rp=int64#1)
+# asm 2: movq <h3=%rax,88(<rp=%rdi)
+movq %rax,88(%rdi)
+
+# qhasm: *(uint64 *)(rp + 0) = e0
+# asm 1: movq <e0=int64#11,0(<rp=int64#1)
+# asm 2: movq <e0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = e1
+# asm 1: movq <e1=int64#12,8(<rp=int64#1)
+# asm 2: movq <e1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = e2
+# asm 1: movq <e2=int64#13,16(<rp=int64#1)
+# asm 2: movq <e2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = e3
+# asm 1: movq <e3=int64#14,24(<rp=int64#1)
+# asm 2: movq <e3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 96(<pp=%rsi),>mulx0=%r12
+movq 96(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 104(<pp=%rsi),>mulx1=%r12
+movq 104(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 112(<pp=%rsi),>mulx2=%r12
+movq 112(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulx3=int64#10
+# asm 2: movq 120(<pp=%rsi),>mulx3=%r12
+movq 120(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#4,<c0=int64#11
+# asm 2: add <mulr4=%rcx,<c0=%r13
+add %rcx,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<c1=int64#12
+# asm 2: adc <mulr5=%r8,<c1=%r14
+adc %r8,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<c2=int64#13
+# asm 2: adc <mulr6=%r9,<c2=%r15
+adc %r9,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<c3=int64#14
+# asm 2: adc <mulr7=%r10,<c3=%rbx
+adc %r10,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#4,<c0=int64#11
+# asm 2: add <mulr8=%rcx,<c0=%r13
+add %rcx,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: f0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>f0=int64#3
+# asm 2: movq 64(<pp=%rsi),>f0=%rdx
+movq 64(%rsi),%rdx
+
+# qhasm: f1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>f1=int64#4
+# asm 2: movq 72(<pp=%rsi),>f1=%rcx
+movq 72(%rsi),%rcx
+
+# qhasm: f2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>f2=int64#5
+# asm 2: movq 80(<pp=%rsi),>f2=%r8
+movq 80(%rsi),%r8
+
+# qhasm: f3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>f3=int64#2
+# asm 2: movq 88(<pp=%rsi),>f3=%rsi
+movq 88(%rsi),%rsi
+
+# qhasm: carry? f0 += f0
+# asm 1: add <f0=int64#3,<f0=int64#3
+# asm 2: add <f0=%rdx,<f0=%rdx
+add %rdx,%rdx
+
+# qhasm: carry? f1 += f1 + carry
+# asm 1: adc <f1=int64#4,<f1=int64#4
+# asm 2: adc <f1=%rcx,<f1=%rcx
+adc %rcx,%rcx
+
+# qhasm: carry? f2 += f2 + carry
+# asm 1: adc <f2=int64#5,<f2=int64#5
+# asm 2: adc <f2=%r8,<f2=%r8
+adc %r8,%r8
+
+# qhasm: carry? f3 += f3 + carry
+# asm 1: adc <f3=int64#2,<f3=int64#2
+# asm 2: adc <f3=%rsi,<f3=%rsi
+adc %rsi,%rsi
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? f0 += addt1
+# asm 1: add <addt1=int64#7,<f0=int64#3
+# asm 2: add <addt1=%rax,<f0=%rdx
+add %rax,%rdx
+
+# qhasm: carry? f1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f1=int64#4
+# asm 2: adc <addt0=%r9,<f1=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? f2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f2=int64#5
+# asm 2: adc <addt0=%r9,<f2=%r8
+adc %r9,%r8
+
+# qhasm: carry? f3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<f3=int64#2
+# asm 2: adc <addt0=%r9,<f3=%rsi
+adc %r9,%rsi
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: f0 += addt0
+# asm 1: add <addt0=int64#6,<f0=int64#3
+# asm 2: add <addt0=%r9,<f0=%rdx
+add %r9,%rdx
+
+# qhasm: g0 = f0
+# asm 1: mov <f0=int64#3,>g0=int64#6
+# asm 2: mov <f0=%rdx,>g0=%r9
+mov %rdx,%r9
+
+# qhasm: g1 = f1
+# asm 1: mov <f1=int64#4,>g1=int64#7
+# asm 2: mov <f1=%rcx,>g1=%rax
+mov %rcx,%rax
+
+# qhasm: g2 = f2
+# asm 1: mov <f2=int64#5,>g2=int64#8
+# asm 2: mov <f2=%r8,>g2=%r10
+mov %r8,%r10
+
+# qhasm: g3 = f3
+# asm 1: mov <f3=int64#2,>g3=int64#9
+# asm 2: mov <f3=%rsi,>g3=%r11
+mov %rsi,%r11
+
+# qhasm: carry? f0 -= c0
+# asm 1: sub <c0=int64#11,<f0=int64#3
+# asm 2: sub <c0=%r13,<f0=%rdx
+sub %r13,%rdx
+
+# qhasm: carry? f1 -= c1 - carry
+# asm 1: sbb <c1=int64#12,<f1=int64#4
+# asm 2: sbb <c1=%r14,<f1=%rcx
+sbb %r14,%rcx
+
+# qhasm: carry? f2 -= c2 - carry
+# asm 1: sbb <c2=int64#13,<f2=int64#5
+# asm 2: sbb <c2=%r15,<f2=%r8
+sbb %r15,%r8
+
+# qhasm: carry? f3 -= c3 - carry
+# asm 1: sbb <c3=int64#14,<f3=int64#2
+# asm 2: sbb <c3=%rbx,<f3=%rsi
+sbb %rbx,%rsi
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#10
+# asm 2: mov $0,>subt0=%r12
+mov $0,%r12
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#15
+# asm 2: mov $38,>subt1=%rbp
+mov $38,%rbp
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#10,<subt1=int64#15
+# asm 2: cmovae <subt0=%r12,<subt1=%rbp
+cmovae %r12,%rbp
+
+# qhasm: carry? f0 -= subt1
+# asm 1: sub <subt1=int64#15,<f0=int64#3
+# asm 2: sub <subt1=%rbp,<f0=%rdx
+sub %rbp,%rdx
+
+# qhasm: carry? f1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<f1=int64#4
+# asm 2: sbb <subt0=%r12,<f1=%rcx
+sbb %r12,%rcx
+
+# qhasm: carry? f2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<f2=int64#5
+# asm 2: sbb <subt0=%r12,<f2=%r8
+sbb %r12,%r8
+
+# qhasm: carry? f3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#10,<f3=int64#2
+# asm 2: sbb <subt0=%r12,<f3=%rsi
+sbb %r12,%rsi
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#15,<subt0=int64#10
+# asm 2: cmovc <subt1=%rbp,<subt0=%r12
+cmovc %rbp,%r12
+
+# qhasm: f0 -= subt0
+# asm 1: sub <subt0=int64#10,<f0=int64#3
+# asm 2: sub <subt0=%r12,<f0=%rdx
+sub %r12,%rdx
+
+# qhasm: carry? g0 += c0
+# asm 1: add <c0=int64#11,<g0=int64#6
+# asm 2: add <c0=%r13,<g0=%r9
+add %r13,%r9
+
+# qhasm: carry? g1 += c1 + carry
+# asm 1: adc <c1=int64#12,<g1=int64#7
+# asm 2: adc <c1=%r14,<g1=%rax
+adc %r14,%rax
+
+# qhasm: carry? g2 += c2 + carry
+# asm 1: adc <c2=int64#13,<g2=int64#8
+# asm 2: adc <c2=%r15,<g2=%r10
+adc %r15,%r10
+
+# qhasm: carry? g3 += c3 + carry
+# asm 1: adc <c3=int64#14,<g3=int64#9
+# asm 2: adc <c3=%rbx,<g3=%r11
+adc %rbx,%r11
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#10
+# asm 2: mov $0,>addt0=%r12
+mov $0,%r12
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#11
+# asm 2: mov $38,>addt1=%r13
+mov $38,%r13
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#10,<addt1=int64#11
+# asm 2: cmovae <addt0=%r12,<addt1=%r13
+cmovae %r12,%r13
+
+# qhasm: carry? g0 += addt1
+# asm 1: add <addt1=int64#11,<g0=int64#6
+# asm 2: add <addt1=%r13,<g0=%r9
+add %r13,%r9
+
+# qhasm: carry? g1 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g1=int64#7
+# asm 2: adc <addt0=%r12,<g1=%rax
+adc %r12,%rax
+
+# qhasm: carry? g2 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g2=int64#8
+# asm 2: adc <addt0=%r12,<g2=%r10
+adc %r12,%r10
+
+# qhasm: carry? g3 += addt0 + carry
+# asm 1: adc <addt0=int64#10,<g3=int64#9
+# asm 2: adc <addt0=%r12,<g3=%r11
+adc %r12,%r11
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#11,<addt0=int64#10
+# asm 2: cmovc <addt1=%r13,<addt0=%r12
+cmovc %r13,%r12
+
+# qhasm: g0 += addt0
+# asm 1: add <addt0=int64#10,<g0=int64#6
+# asm 2: add <addt0=%r12,<g0=%r9
+add %r12,%r9
+
+# qhasm: *(uint64 *)(rp + 32) = g0
+# asm 1: movq <g0=int64#6,32(<rp=int64#1)
+# asm 2: movq <g0=%r9,32(<rp=%rdi)
+movq %r9,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = g1
+# asm 1: movq <g1=int64#7,40(<rp=int64#1)
+# asm 2: movq <g1=%rax,40(<rp=%rdi)
+movq %rax,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = g2
+# asm 1: movq <g2=int64#8,48(<rp=int64#1)
+# asm 2: movq <g2=%r10,48(<rp=%rdi)
+movq %r10,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = g3
+# asm 1: movq <g3=int64#9,56(<rp=int64#1)
+# asm 2: movq <g3=%r11,56(<rp=%rdi)
+movq %r11,56(%rdi)
+
+# qhasm: *(uint64 *)(rp + 96) = f0
+# asm 1: movq <f0=int64#3,96(<rp=int64#1)
+# asm 2: movq <f0=%rdx,96(<rp=%rdi)
+movq %rdx,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = f1
+# asm 1: movq <f1=int64#4,104(<rp=int64#1)
+# asm 2: movq <f1=%rcx,104(<rp=%rdi)
+movq %rcx,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = f2
+# asm 1: movq <f2=int64#5,112(<rp=int64#1)
+# asm 2: movq <f2=%r8,112(<rp=%rdi)
+movq %r8,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = f3
+# asm 1: movq <f3=int64#2,120(<rp=int64#1)
+# asm 2: movq <f3=%rsi,120(<rp=%rdi)
+movq %rsi,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s
new file mode 100644
index 00000000..beddbc79
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.s
@@ -0,0 +1,2236 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+.globl crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2
+_crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2:
+crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p2:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 0(<pp=%rsi),>mulx0=%r11
+movq 0(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#10
+# asm 2: mov <mulrax=%rax,>rx0=%r12
+mov %rax,%r12
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rx1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#12
+# asm 2: mov $0,>rx2=%r14
+mov $0,%r14
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#13
+# asm 2: mov $0,>rx3=%r15
+mov $0,%r15
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 8(<pp=%rsi),>mulx1=%r11
+movq 8(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#14,<rx2=int64#12
+# asm 2: add <mulc=%rbx,<rx2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 16(<pp=%rsi),>mulx2=%r11
+movq 16(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 24(<pp=%rsi),>mulx3=%r11
+movq 24(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#4,<rx0=int64#10
+# asm 2: add <mulr4=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<rx1=int64#11
+# asm 2: adc <mulr5=%r8,<rx1=%r13
+adc %r8,%r13
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<rx2=int64#12
+# asm 2: adc <mulr6=%r9,<rx2=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<rx3=int64#13
+# asm 2: adc <mulr7=%r10,<rx3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#4,<rx0=int64#10
+# asm 2: add <mulr8=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#11
+# asm 2: adc <mulzero=%rdx,<rx1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#12
+# asm 2: adc <mulzero=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#13
+# asm 2: adc <mulzero=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#10
+# asm 2: add <mulzero=%rdx,<rx0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#10,0(<rp=int64#1)
+# asm 2: movq <rx0=%r12,0(<rp=%rdi)
+movq %r12,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#11,8(<rp=int64#1)
+# asm 2: movq <rx1=%r13,8(<rp=%rdi)
+movq %r13,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#12,16(<rp=int64#1)
+# asm 2: movq <rx2=%r14,16(<rp=%rdi)
+movq %r14,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#13,24(<rp=int64#1)
+# asm 2: movq <rx3=%r15,24(<rp=%rdi)
+movq %r15,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r11
+movq 64(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: ry0 = mulrax
+# asm 1: mov <mulrax=int64#7,>ry0=int64#10
+# asm 2: mov <mulrax=%rax,>ry0=%r12
+mov %rax,%r12
+
+# qhasm: ry1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>ry1=int64#11
+# asm 2: mov <mulrdx=%rdx,>ry1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: ry2 = 0
+# asm 1: mov $0,>ry2=int64#12
+# asm 2: mov $0,>ry2=%r14
+mov $0,%r14
+
+# qhasm: ry2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry2=int64#12
+# asm 2: adc <mulrdx=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: ry3 = 0
+# asm 1: mov $0,>ry3=int64#13
+# asm 2: mov $0,>ry3=%r15
+mov $0,%r15
+
+# qhasm: ry3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry3=int64#13
+# asm 2: adc <mulrdx=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r11
+movq 72(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry2 += mulc
+# asm 1: add <mulc=int64#14,<ry2=int64#12
+# asm 2: add <mulc=%rbx,<ry2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r11
+movq 80(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 88(<pp=%rsi),>mulx3=%r11
+movq 88(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? ry0 += mulr4
+# asm 1: add <mulr4=int64#4,<ry0=int64#10
+# asm 2: add <mulr4=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<ry1=int64#11
+# asm 2: adc <mulr5=%r8,<ry1=%r13
+adc %r8,%r13
+
+# qhasm: carry? ry2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<ry2=int64#12
+# asm 2: adc <mulr6=%r9,<ry2=%r14
+adc %r9,%r14
+
+# qhasm: carry? ry3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<ry3=int64#13
+# asm 2: adc <mulr7=%r10,<ry3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? ry0 += mulr8
+# asm 1: add <mulr8=int64#4,<ry0=int64#10
+# asm 2: add <mulr8=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry1=int64#11
+# asm 2: adc <mulzero=%rdx,<ry1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? ry2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry2=int64#12
+# asm 2: adc <mulzero=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? ry3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry3=int64#13
+# asm 2: adc <mulzero=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: ry0 += mulzero
+# asm 1: add <mulzero=int64#3,<ry0=int64#10
+# asm 2: add <mulzero=%rdx,<ry0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 32) = ry0
+# asm 1: movq <ry0=int64#10,32(<rp=int64#1)
+# asm 2: movq <ry0=%r12,32(<rp=%rdi)
+movq %r12,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = ry1
+# asm 1: movq <ry1=int64#11,40(<rp=int64#1)
+# asm 2: movq <ry1=%r13,40(<rp=%rdi)
+movq %r13,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = ry2
+# asm 1: movq <ry2=int64#12,48(<rp=int64#1)
+# asm 2: movq <ry2=%r14,48(<rp=%rdi)
+movq %r14,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = ry3
+# asm 1: movq <ry3=int64#13,56(<rp=int64#1)
+# asm 2: movq <ry3=%r15,56(<rp=%rdi)
+movq %r15,56(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 32(<pp=%rsi),>mulx0=%r11
+movq 32(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rz0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rz0=int64#10
+# asm 2: mov <mulrax=%rax,>rz0=%r12
+mov %rax,%r12
+
+# qhasm: rz1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rz1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rz1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: rz2 = 0
+# asm 1: mov $0,>rz2=int64#12
+# asm 2: mov $0,>rz2=%r14
+mov $0,%r14
+
+# qhasm: rz2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rz2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: rz3 = 0
+# asm 1: mov $0,>rz3=int64#13
+# asm 2: mov $0,>rz3=%r15
+mov $0,%r15
+
+# qhasm: rz3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rz3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 40(<pp=%rsi),>mulx1=%r11
+movq 40(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz2 += mulc
+# asm 1: add <mulc=int64#14,<rz2=int64#12
+# asm 2: add <mulc=%rbx,<rz2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 48(<pp=%rsi),>mulx2=%r11
+movq 48(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 56(<pp=%rsi),>mulx3=%r11
+movq 56(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rz0 += mulr4
+# asm 1: add <mulr4=int64#2,<rz0=int64#10
+# asm 2: add <mulr4=%rsi,<rz0=%r12
+add %rsi,%r12
+
+# qhasm: carry? rz1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rz1=int64#11
+# asm 2: adc <mulr5=%rcx,<rz1=%r13
+adc %rcx,%r13
+
+# qhasm: carry? rz2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rz2=int64#12
+# asm 2: adc <mulr6=%r8,<rz2=%r14
+adc %r8,%r14
+
+# qhasm: carry? rz3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rz3=int64#13
+# asm 2: adc <mulr7=%r9,<rz3=%r15
+adc %r9,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rz0 += mulr8
+# asm 1: add <mulr8=int64#3,<rz0=int64#10
+# asm 2: add <mulr8=%rdx,<rz0=%r12
+add %rdx,%r12
+
+# qhasm: carry? rz1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz1=int64#11
+# asm 2: adc <mulzero=%rsi,<rz1=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rz2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz2=int64#12
+# asm 2: adc <mulzero=%rsi,<rz2=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rz3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rz3=int64#13
+# asm 2: adc <mulzero=%rsi,<rz3=%r15
+adc %rsi,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rz0 += mulzero
+# asm 1: add <mulzero=int64#2,<rz0=int64#10
+# asm 2: add <mulzero=%rsi,<rz0=%r12
+add %rsi,%r12
+
+# qhasm: *(uint64 *)(rp + 64) = rz0
+# asm 1: movq <rz0=int64#10,64(<rp=int64#1)
+# asm 2: movq <rz0=%r12,64(<rp=%rdi)
+movq %r12,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = rz1
+# asm 1: movq <rz1=int64#11,72(<rp=int64#1)
+# asm 2: movq <rz1=%r13,72(<rp=%rdi)
+movq %r13,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = rz2
+# asm 1: movq <rz2=int64#12,80(<rp=int64#1)
+# asm 2: movq <rz2=%r14,80(<rp=%rdi)
+movq %r14,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = rz3
+# asm 1: movq <rz3=int64#13,88(<rp=int64#1)
+# asm 2: movq <rz3=%r15,88(<rp=%rdi)
+movq %r15,88(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s
new file mode 100644
index 00000000..82433fba
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.s
@@ -0,0 +1,2926 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+.globl crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3
+_crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3:
+crypto_sign_ed25519_amd64_64_ge25519_p1p1_to_p3:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 0(<pp=%rsi),>mulx0=%r11
+movq 0(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#10
+# asm 2: mov <mulrax=%rax,>rx0=%r12
+mov %rax,%r12
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rx1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#12
+# asm 2: mov $0,>rx2=%r14
+mov $0,%r14
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#13
+# asm 2: mov $0,>rx3=%r15
+mov $0,%r15
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 8(<pp=%rsi),>mulx1=%r11
+movq 8(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#11
+# asm 2: add <mulrax=%rax,<rx1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#14,<rx2=int64#12
+# asm 2: add <mulc=%rbx,<rx2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 16(<pp=%rsi),>mulx2=%r11
+movq 16(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#12
+# asm 2: add <mulrax=%rax,<rx2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#14,<rx3=int64#13
+# asm 2: add <mulc=%rbx,<rx3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 24(<pp=%rsi),>mulx3=%r11
+movq 24(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#13
+# asm 2: add <mulrax=%rax,<rx3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#4,<rx0=int64#10
+# asm 2: add <mulr4=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<rx1=int64#11
+# asm 2: adc <mulr5=%r8,<rx1=%r13
+adc %r8,%r13
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<rx2=int64#12
+# asm 2: adc <mulr6=%r9,<rx2=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<rx3=int64#13
+# asm 2: adc <mulr7=%r10,<rx3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#4,<rx0=int64#10
+# asm 2: add <mulr8=%rcx,<rx0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#11
+# asm 2: adc <mulzero=%rdx,<rx1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#12
+# asm 2: adc <mulzero=%rdx,<rx2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#13
+# asm 2: adc <mulzero=%rdx,<rx3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#10
+# asm 2: add <mulzero=%rdx,<rx0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 0) = rx0
+# asm 1: movq <rx0=int64#10,0(<rp=int64#1)
+# asm 2: movq <rx0=%r12,0(<rp=%rdi)
+movq %r12,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = rx1
+# asm 1: movq <rx1=int64#11,8(<rp=int64#1)
+# asm 2: movq <rx1=%r13,8(<rp=%rdi)
+movq %r13,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = rx2
+# asm 1: movq <rx2=int64#12,16(<rp=int64#1)
+# asm 2: movq <rx2=%r14,16(<rp=%rdi)
+movq %r14,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = rx3
+# asm 1: movq <rx3=int64#13,24(<rp=int64#1)
+# asm 2: movq <rx3=%r15,24(<rp=%rdi)
+movq %r15,24(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r11
+movq 64(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: ry0 = mulrax
+# asm 1: mov <mulrax=int64#7,>ry0=int64#10
+# asm 2: mov <mulrax=%rax,>ry0=%r12
+mov %rax,%r12
+
+# qhasm: ry1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>ry1=int64#11
+# asm 2: mov <mulrdx=%rdx,>ry1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: ry2 = 0
+# asm 1: mov $0,>ry2=int64#12
+# asm 2: mov $0,>ry2=%r14
+mov $0,%r14
+
+# qhasm: ry2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry2=int64#12
+# asm 2: adc <mulrdx=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: ry3 = 0
+# asm 1: mov $0,>ry3=int64#13
+# asm 2: mov $0,>ry3=%r15
+mov $0,%r15
+
+# qhasm: ry3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<ry3=int64#13
+# asm 2: adc <mulrdx=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r11
+movq 72(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry1 += mulrax
+# asm 1: add <mulrax=int64#7,<ry1=int64#11
+# asm 2: add <mulrax=%rax,<ry1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry2 += mulc
+# asm 1: add <mulc=int64#14,<ry2=int64#12
+# asm 2: add <mulc=%rbx,<ry2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r11
+movq 80(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry2 += mulrax
+# asm 1: add <mulrax=int64#7,<ry2=int64#12
+# asm 2: add <mulrax=%rax,<ry2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? ry3 += mulc
+# asm 1: add <mulc=int64#14,<ry3=int64#13
+# asm 2: add <mulc=%rbx,<ry3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 88(<pp=%rsi),>mulx3=%r11
+movq 88(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 32(<pp=%rsi),>mulrax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? ry3 += mulrax
+# asm 1: add <mulrax=int64#7,<ry3=int64#13
+# asm 2: add <mulrax=%rax,<ry3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 40(<pp=%rsi),>mulrax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 48(<pp=%rsi),>mulrax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 56(<pp=%rsi),>mulrax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? ry0 += mulr4
+# asm 1: add <mulr4=int64#4,<ry0=int64#10
+# asm 2: add <mulr4=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<ry1=int64#11
+# asm 2: adc <mulr5=%r8,<ry1=%r13
+adc %r8,%r13
+
+# qhasm: carry? ry2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<ry2=int64#12
+# asm 2: adc <mulr6=%r9,<ry2=%r14
+adc %r9,%r14
+
+# qhasm: carry? ry3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<ry3=int64#13
+# asm 2: adc <mulr7=%r10,<ry3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? ry0 += mulr8
+# asm 1: add <mulr8=int64#4,<ry0=int64#10
+# asm 2: add <mulr8=%rcx,<ry0=%r12
+add %rcx,%r12
+
+# qhasm: carry? ry1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry1=int64#11
+# asm 2: adc <mulzero=%rdx,<ry1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? ry2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry2=int64#12
+# asm 2: adc <mulzero=%rdx,<ry2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? ry3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<ry3=int64#13
+# asm 2: adc <mulzero=%rdx,<ry3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: ry0 += mulzero
+# asm 1: add <mulzero=int64#3,<ry0=int64#10
+# asm 2: add <mulzero=%rdx,<ry0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 32) = ry0
+# asm 1: movq <ry0=int64#10,32(<rp=int64#1)
+# asm 2: movq <ry0=%r12,32(<rp=%rdi)
+movq %r12,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = ry1
+# asm 1: movq <ry1=int64#11,40(<rp=int64#1)
+# asm 2: movq <ry1=%r13,40(<rp=%rdi)
+movq %r13,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = ry2
+# asm 1: movq <ry2=int64#12,48(<rp=int64#1)
+# asm 2: movq <ry2=%r14,48(<rp=%rdi)
+movq %r14,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = ry3
+# asm 1: movq <ry3=int64#13,56(<rp=int64#1)
+# asm 2: movq <ry3=%r15,56(<rp=%rdi)
+movq %r15,56(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 32(<pp=%rsi),>mulx0=%r11
+movq 32(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rz0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rz0=int64#10
+# asm 2: mov <mulrax=%rax,>rz0=%r12
+mov %rax,%r12
+
+# qhasm: rz1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rz1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rz1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: rz2 = 0
+# asm 1: mov $0,>rz2=int64#12
+# asm 2: mov $0,>rz2=%r14
+mov $0,%r14
+
+# qhasm: rz2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rz2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: rz3 = 0
+# asm 1: mov $0,>rz3=int64#13
+# asm 2: mov $0,>rz3=%r15
+mov $0,%r15
+
+# qhasm: rz3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rz3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rz3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 40(<pp=%rsi),>mulx1=%r11
+movq 40(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz1 += mulrax
+# asm 1: add <mulrax=int64#7,<rz1=int64#11
+# asm 2: add <mulrax=%rax,<rz1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz2 += mulc
+# asm 1: add <mulc=int64#14,<rz2=int64#12
+# asm 2: add <mulc=%rbx,<rz2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 48(<pp=%rsi),>mulx2=%r11
+movq 48(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz2 += mulrax
+# asm 1: add <mulrax=int64#7,<rz2=int64#12
+# asm 2: add <mulrax=%rax,<rz2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rz3 += mulc
+# asm 1: add <mulc=int64#14,<rz3=int64#13
+# asm 2: add <mulc=%rbx,<rz3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 56(<pp=%rsi),>mulx3=%r11
+movq 56(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 96(<pp=%rsi),>mulrax=%rax
+movq 96(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rz3 += mulrax
+# asm 1: add <mulrax=int64#7,<rz3=int64#13
+# asm 2: add <mulrax=%rax,<rz3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 104(<pp=%rsi),>mulrax=%rax
+movq 104(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 112(<pp=%rsi),>mulrax=%rax
+movq 112(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 120(<pp=%rsi),>mulrax=%rax
+movq 120(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#4
+# asm 2: mov <mulrax=%rax,>mulr4=%rcx
+mov %rax,%rcx
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#5
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r8
+mov %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#8
+# asm 2: add <mulrax=%rax,<mulr7=%r10
+add %rax,%r10
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rz0 += mulr4
+# asm 1: add <mulr4=int64#4,<rz0=int64#10
+# asm 2: add <mulr4=%rcx,<rz0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rz1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#5,<rz1=int64#11
+# asm 2: adc <mulr5=%r8,<rz1=%r13
+adc %r8,%r13
+
+# qhasm: carry? rz2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#6,<rz2=int64#12
+# asm 2: adc <mulr6=%r9,<rz2=%r14
+adc %r9,%r14
+
+# qhasm: carry? rz3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#8,<rz3=int64#13
+# asm 2: adc <mulr7=%r10,<rz3=%r15
+adc %r10,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#4
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rcx
+imulq $38,%rax,%rcx
+
+# qhasm: carry? rz0 += mulr8
+# asm 1: add <mulr8=int64#4,<rz0=int64#10
+# asm 2: add <mulr8=%rcx,<rz0=%r12
+add %rcx,%r12
+
+# qhasm: carry? rz1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rz1=int64#11
+# asm 2: adc <mulzero=%rdx,<rz1=%r13
+adc %rdx,%r13
+
+# qhasm: carry? rz2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rz2=int64#12
+# asm 2: adc <mulzero=%rdx,<rz2=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rz3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rz3=int64#13
+# asm 2: adc <mulzero=%rdx,<rz3=%r15
+adc %rdx,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rz0 += mulzero
+# asm 1: add <mulzero=int64#3,<rz0=int64#10
+# asm 2: add <mulzero=%rdx,<rz0=%r12
+add %rdx,%r12
+
+# qhasm: *(uint64 *)(rp + 64) = rz0
+# asm 1: movq <rz0=int64#10,64(<rp=int64#1)
+# asm 2: movq <rz0=%r12,64(<rp=%rdi)
+movq %r12,64(%rdi)
+
+# qhasm: *(uint64 *)(rp + 72) = rz1
+# asm 1: movq <rz1=int64#11,72(<rp=int64#1)
+# asm 2: movq <rz1=%r13,72(<rp=%rdi)
+movq %r13,72(%rdi)
+
+# qhasm: *(uint64 *)(rp + 80) = rz2
+# asm 1: movq <rz2=int64#12,80(<rp=int64#1)
+# asm 2: movq <rz2=%r14,80(<rp=%rdi)
+movq %r14,80(%rdi)
+
+# qhasm: *(uint64 *)(rp + 88) = rz3
+# asm 1: movq <rz3=int64#13,88(<rp=int64#1)
+# asm 2: movq <rz3=%r15,88(<rp=%rdi)
+movq %r15,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#4
+# asm 2: mov $0,>mulr4=%rcx
+mov $0,%rcx
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#5
+# asm 2: mov $0,>mulr5=%r8
+mov $0,%r8
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#6
+# asm 2: mov $0,>mulr6=%r9
+mov $0,%r9
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#8
+# asm 2: mov $0,>mulr7=%r10
+mov $0,%r10
+
+# qhasm: mulx0 = *(uint64 *)(pp + 0)
+# asm 1: movq 0(<pp=int64#2),>mulx0=int64#9
+# asm 2: movq 0(<pp=%rsi),>mulx0=%r11
+movq 0(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#10
+# asm 2: mov <mulrax=%rax,>rt0=%r12
+mov %rax,%r12
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#11
+# asm 2: mov <mulrdx=%rdx,>rt1=%r13
+mov %rdx,%r13
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#11
+# asm 2: add <mulrax=%rax,<rt1=%r13
+add %rax,%r13
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#12
+# asm 2: mov $0,>rt2=%r14
+mov $0,%r14
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#12
+# asm 2: adc <mulrdx=%rdx,<rt2=%r14
+adc %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#12
+# asm 2: add <mulrax=%rax,<rt2=%r14
+add %rax,%r14
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#13
+# asm 2: mov $0,>rt3=%r15
+mov $0,%r15
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#13
+# asm 2: adc <mulrdx=%rdx,<rt3=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#9
+# asm 2: mul <mulx0=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#4
+# asm 2: adc <mulrdx=%rdx,<mulr4=%rcx
+adc %rdx,%rcx
+
+# qhasm: mulx1 = *(uint64 *)(pp + 8)
+# asm 1: movq 8(<pp=int64#2),>mulx1=int64#9
+# asm 2: movq 8(<pp=%rsi),>mulx1=%r11
+movq 8(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#11
+# asm 2: add <mulrax=%rax,<rt1=%r13
+add %rax,%r13
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#12
+# asm 2: add <mulrax=%rax,<rt2=%r14
+add %rax,%r14
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#14,<rt2=int64#12
+# asm 2: add <mulc=%rbx,<rt2=%r14
+add %rbx,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#14,<rt3=int64#13
+# asm 2: add <mulc=%rbx,<rt3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#9
+# asm 2: mul <mulx1=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r8
+adc %rdx,%r8
+
+# qhasm: mulx2 = *(uint64 *)(pp + 16)
+# asm 1: movq 16(<pp=int64#2),>mulx2=int64#9
+# asm 2: movq 16(<pp=%rsi),>mulx2=%r11
+movq 16(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#12
+# asm 2: add <mulrax=%rax,<rt2=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#14,<rt3=int64#13
+# asm 2: add <mulc=%rbx,<rt3=%r15
+add %rbx,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#9
+# asm 2: mul <mulx2=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r9
+adc %rdx,%r9
+
+# qhasm: mulx3 = *(uint64 *)(pp + 24)
+# asm 1: movq 24(<pp=int64#2),>mulx3=int64#9
+# asm 2: movq 24(<pp=%rsi),>mulx3=%r11
+movq 24(%rsi),%r11
+
+# qhasm: mulrax = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 64(<pp=%rsi),>mulrax=%rax
+movq 64(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#13
+# asm 2: add <mulrax=%rax,<rt3=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 72(<pp=%rsi),>mulrax=%rax
+movq 72(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#4
+# asm 2: add <mulrax=%rax,<mulr4=%rcx
+add %rax,%rcx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#14,<mulr4=int64#4
+# asm 2: add <mulc=%rbx,<mulr4=%rcx
+add %rbx,%rcx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 80(<pp=%rsi),>mulrax=%rax
+movq 80(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#5
+# asm 2: add <mulrax=%rax,<mulr5=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#14,<mulr5=int64#5
+# asm 2: add <mulc=%rbx,<mulr5=%r8
+add %rbx,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#14
+# asm 2: mov $0,>mulc=%rbx
+mov $0,%rbx
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#14
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulrax=int64#7
+# asm 2: movq 88(<pp=%rsi),>mulrax=%rax
+movq 88(%rsi),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#9
+# asm 2: mul <mulx3=%r11
+mul %r11
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#6
+# asm 2: add <mulrax=%rax,<mulr6=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#14,<mulr6=int64#6
+# asm 2: add <mulc=%rbx,<mulr6=%r9
+add %rbx,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r10
+adc %rdx,%r10
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#4,>mulrax=int64#7
+# asm 2: mov <mulr4=%rcx,>mulrax=%rax
+mov %rcx,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr5=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr6=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr7=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#10
+# asm 2: add <mulr4=%rsi,<rt0=%r12
+add %rsi,%r12
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#11
+# asm 2: adc <mulr5=%rcx,<rt1=%r13
+adc %rcx,%r13
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#12
+# asm 2: adc <mulr6=%r8,<rt2=%r14
+adc %r8,%r14
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#13
+# asm 2: adc <mulr7=%r9,<rt3=%r15
+adc %r9,%r15
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#10
+# asm 2: add <mulr8=%rdx,<rt0=%r12
+add %rdx,%r12
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#11
+# asm 2: adc <mulzero=%rsi,<rt1=%r13
+adc %rsi,%r13
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#12
+# asm 2: adc <mulzero=%rsi,<rt2=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#13
+# asm 2: adc <mulzero=%rsi,<rt3=%r15
+adc %rsi,%r15
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#10
+# asm 2: add <mulzero=%rsi,<rt0=%r12
+add %rsi,%r12
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#10,96(<rp=int64#1)
+# asm 2: movq <rt0=%r12,96(<rp=%rdi)
+movq %r12,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#11,104(<rp=int64#1)
+# asm 2: movq <rt1=%r13,104(<rp=%rdi)
+movq %r13,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#12,112(<rp=int64#1)
+# asm 2: movq <rt2=%r14,112(<rp=%rdi)
+movq %r14,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#13,120(<rp=int64#1)
+# asm 2: movq <rt3=%r15,120(<rp=%rdi)
+movq %r15,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_pack.c b/ext/ed25519-amd64-asm/ge25519_pack.c
new file mode 100644
index 00000000..f289fe57
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_pack.c
@@ -0,0 +1,13 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+
+void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
+{
+ fe25519 tx, ty, zi;
+ fe25519_invert(&zi, &p->z);
+ fe25519_mul(&tx, &p->x, &zi);
+ fe25519_mul(&ty, &p->y, &zi);
+ fe25519_pack(r, &ty);
+ r[31] ^= fe25519_getparity(&tx) << 7;
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s b/ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s
new file mode 100644
index 00000000..aff2e75d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.s
@@ -0,0 +1,3662 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 pp
+
+# qhasm: int64 qp
+
+# qhasm: input rp
+
+# qhasm: input pp
+
+# qhasm: input qp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 a0
+
+# qhasm: int64 a1
+
+# qhasm: int64 a2
+
+# qhasm: int64 a3
+
+# qhasm: stack64 a0_stack
+
+# qhasm: stack64 a1_stack
+
+# qhasm: stack64 a2_stack
+
+# qhasm: stack64 a3_stack
+
+# qhasm: int64 b0
+
+# qhasm: int64 b1
+
+# qhasm: int64 b2
+
+# qhasm: int64 b3
+
+# qhasm: stack64 b0_stack
+
+# qhasm: stack64 b1_stack
+
+# qhasm: stack64 b2_stack
+
+# qhasm: stack64 b3_stack
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: stack64 c0_stack
+
+# qhasm: stack64 c1_stack
+
+# qhasm: stack64 c2_stack
+
+# qhasm: stack64 c3_stack
+
+# qhasm: int64 d0
+
+# qhasm: int64 d1
+
+# qhasm: int64 d2
+
+# qhasm: int64 d3
+
+# qhasm: stack64 d0_stack
+
+# qhasm: stack64 d1_stack
+
+# qhasm: stack64 d2_stack
+
+# qhasm: stack64 d3_stack
+
+# qhasm: int64 t10
+
+# qhasm: int64 t11
+
+# qhasm: int64 t12
+
+# qhasm: int64 t13
+
+# qhasm: stack64 t10_stack
+
+# qhasm: stack64 t11_stack
+
+# qhasm: stack64 t12_stack
+
+# qhasm: stack64 t13_stack
+
+# qhasm: int64 t20
+
+# qhasm: int64 t21
+
+# qhasm: int64 t22
+
+# qhasm: int64 t23
+
+# qhasm: stack64 t20_stack
+
+# qhasm: stack64 t21_stack
+
+# qhasm: stack64 t22_stack
+
+# qhasm: stack64 t23_stack
+
+# qhasm: int64 rx0
+
+# qhasm: int64 rx1
+
+# qhasm: int64 rx2
+
+# qhasm: int64 rx3
+
+# qhasm: int64 ry0
+
+# qhasm: int64 ry1
+
+# qhasm: int64 ry2
+
+# qhasm: int64 ry3
+
+# qhasm: int64 rz0
+
+# qhasm: int64 rz1
+
+# qhasm: int64 rz2
+
+# qhasm: int64 rz3
+
+# qhasm: int64 rt0
+
+# qhasm: int64 rt1
+
+# qhasm: int64 rt2
+
+# qhasm: int64 rt3
+
+# qhasm: int64 x0
+
+# qhasm: int64 x1
+
+# qhasm: int64 x2
+
+# qhasm: int64 x3
+
+# qhasm: int64 mulr4
+
+# qhasm: int64 mulr5
+
+# qhasm: int64 mulr6
+
+# qhasm: int64 mulr7
+
+# qhasm: int64 mulr8
+
+# qhasm: int64 mulrax
+
+# qhasm: int64 mulrdx
+
+# qhasm: int64 mulx0
+
+# qhasm: int64 mulx1
+
+# qhasm: int64 mulx2
+
+# qhasm: int64 mulx3
+
+# qhasm: int64 mulc
+
+# qhasm: int64 mulzero
+
+# qhasm: int64 muli38
+
+# qhasm: int64 addt0
+
+# qhasm: int64 addt1
+
+# qhasm: int64 subt0
+
+# qhasm: int64 subt1
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+.globl crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1
+_crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1:
+crypto_sign_ed25519_amd64_64_ge25519_pnielsadd_p1p1:
+mov %rsp,%r11
+and $31,%r11
+add $128,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: qp = qp
+# asm 1: mov <qp=int64#3,>qp=int64#4
+# asm 2: mov <qp=%rdx,>qp=%rcx
+mov %rdx,%rcx
+
+# qhasm: a0 = *(uint64 *)(pp + 32)
+# asm 1: movq 32(<pp=int64#2),>a0=int64#3
+# asm 2: movq 32(<pp=%rsi),>a0=%rdx
+movq 32(%rsi),%rdx
+
+# qhasm: a1 = *(uint64 *)(pp + 40)
+# asm 1: movq 40(<pp=int64#2),>a1=int64#5
+# asm 2: movq 40(<pp=%rsi),>a1=%r8
+movq 40(%rsi),%r8
+
+# qhasm: a2 = *(uint64 *)(pp + 48)
+# asm 1: movq 48(<pp=int64#2),>a2=int64#6
+# asm 2: movq 48(<pp=%rsi),>a2=%r9
+movq 48(%rsi),%r9
+
+# qhasm: a3 = *(uint64 *)(pp + 56)
+# asm 1: movq 56(<pp=int64#2),>a3=int64#7
+# asm 2: movq 56(<pp=%rsi),>a3=%rax
+movq 56(%rsi),%rax
+
+# qhasm: b0 = a0
+# asm 1: mov <a0=int64#3,>b0=int64#8
+# asm 2: mov <a0=%rdx,>b0=%r10
+mov %rdx,%r10
+
+# qhasm: b1 = a1
+# asm 1: mov <a1=int64#5,>b1=int64#9
+# asm 2: mov <a1=%r8,>b1=%r11
+mov %r8,%r11
+
+# qhasm: b2 = a2
+# asm 1: mov <a2=int64#6,>b2=int64#10
+# asm 2: mov <a2=%r9,>b2=%r12
+mov %r9,%r12
+
+# qhasm: b3 = a3
+# asm 1: mov <a3=int64#7,>b3=int64#11
+# asm 2: mov <a3=%rax,>b3=%r13
+mov %rax,%r13
+
+# qhasm: carry? a0 -= *(uint64 *)(pp + 0)
+# asm 1: subq 0(<pp=int64#2),<a0=int64#3
+# asm 2: subq 0(<pp=%rsi),<a0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? a1 -= *(uint64 *)(pp + 8) - carry
+# asm 1: sbbq 8(<pp=int64#2),<a1=int64#5
+# asm 2: sbbq 8(<pp=%rsi),<a1=%r8
+sbbq 8(%rsi),%r8
+
+# qhasm: carry? a2 -= *(uint64 *)(pp + 16) - carry
+# asm 1: sbbq 16(<pp=int64#2),<a2=int64#6
+# asm 2: sbbq 16(<pp=%rsi),<a2=%r9
+sbbq 16(%rsi),%r9
+
+# qhasm: carry? a3 -= *(uint64 *)(pp + 24) - carry
+# asm 1: sbbq 24(<pp=int64#2),<a3=int64#7
+# asm 2: sbbq 24(<pp=%rsi),<a3=%rax
+sbbq 24(%rsi),%rax
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#12
+# asm 2: mov $0,>subt0=%r14
+mov $0,%r14
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#13
+# asm 2: mov $38,>subt1=%r15
+mov $38,%r15
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#12,<subt1=int64#13
+# asm 2: cmovae <subt0=%r14,<subt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? a0 -= subt1
+# asm 1: sub <subt1=int64#13,<a0=int64#3
+# asm 2: sub <subt1=%r15,<a0=%rdx
+sub %r15,%rdx
+
+# qhasm: carry? a1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a1=int64#5
+# asm 2: sbb <subt0=%r14,<a1=%r8
+sbb %r14,%r8
+
+# qhasm: carry? a2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a2=int64#6
+# asm 2: sbb <subt0=%r14,<a2=%r9
+sbb %r14,%r9
+
+# qhasm: carry? a3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#12,<a3=int64#7
+# asm 2: sbb <subt0=%r14,<a3=%rax
+sbb %r14,%rax
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#13,<subt0=int64#12
+# asm 2: cmovc <subt1=%r15,<subt0=%r14
+cmovc %r15,%r14
+
+# qhasm: a0 -= subt0
+# asm 1: sub <subt0=int64#12,<a0=int64#3
+# asm 2: sub <subt0=%r14,<a0=%rdx
+sub %r14,%rdx
+
+# qhasm: carry? b0 += *(uint64 *)(pp + 0)
+# asm 1: addq 0(<pp=int64#2),<b0=int64#8
+# asm 2: addq 0(<pp=%rsi),<b0=%r10
+addq 0(%rsi),%r10
+
+# qhasm: carry? b1 += *(uint64 *)(pp + 8) + carry
+# asm 1: adcq 8(<pp=int64#2),<b1=int64#9
+# asm 2: adcq 8(<pp=%rsi),<b1=%r11
+adcq 8(%rsi),%r11
+
+# qhasm: carry? b2 += *(uint64 *)(pp + 16) + carry
+# asm 1: adcq 16(<pp=int64#2),<b2=int64#10
+# asm 2: adcq 16(<pp=%rsi),<b2=%r12
+adcq 16(%rsi),%r12
+
+# qhasm: carry? b3 += *(uint64 *)(pp + 24) + carry
+# asm 1: adcq 24(<pp=int64#2),<b3=int64#11
+# asm 2: adcq 24(<pp=%rsi),<b3=%r13
+adcq 24(%rsi),%r13
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#12
+# asm 2: mov $0,>addt0=%r14
+mov $0,%r14
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#13
+# asm 2: mov $38,>addt1=%r15
+mov $38,%r15
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#12,<addt1=int64#13
+# asm 2: cmovae <addt0=%r14,<addt1=%r15
+cmovae %r14,%r15
+
+# qhasm: carry? b0 += addt1
+# asm 1: add <addt1=int64#13,<b0=int64#8
+# asm 2: add <addt1=%r15,<b0=%r10
+add %r15,%r10
+
+# qhasm: carry? b1 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b1=int64#9
+# asm 2: adc <addt0=%r14,<b1=%r11
+adc %r14,%r11
+
+# qhasm: carry? b2 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b2=int64#10
+# asm 2: adc <addt0=%r14,<b2=%r12
+adc %r14,%r12
+
+# qhasm: carry? b3 += addt0 + carry
+# asm 1: adc <addt0=int64#12,<b3=int64#11
+# asm 2: adc <addt0=%r14,<b3=%r13
+adc %r14,%r13
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#13,<addt0=int64#12
+# asm 2: cmovc <addt1=%r15,<addt0=%r14
+cmovc %r15,%r14
+
+# qhasm: b0 += addt0
+# asm 1: add <addt0=int64#12,<b0=int64#8
+# asm 2: add <addt0=%r14,<b0=%r10
+add %r14,%r10
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#3,>a0_stack=stack64#8
+# asm 2: movq <a0=%rdx,>a0_stack=56(%rsp)
+movq %rdx,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#5,>a1_stack=stack64#9
+# asm 2: movq <a1=%r8,>a1_stack=64(%rsp)
+movq %r8,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#6,>a2_stack=stack64#10
+# asm 2: movq <a2=%r9,>a2_stack=72(%rsp)
+movq %r9,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#7,>a3_stack=stack64#11
+# asm 2: movq <a3=%rax,>a3_stack=80(%rsp)
+movq %rax,80(%rsp)
+
+# qhasm: b0_stack = b0
+# asm 1: movq <b0=int64#8,>b0_stack=stack64#12
+# asm 2: movq <b0=%r10,>b0_stack=88(%rsp)
+movq %r10,88(%rsp)
+
+# qhasm: b1_stack = b1
+# asm 1: movq <b1=int64#9,>b1_stack=stack64#13
+# asm 2: movq <b1=%r11,>b1_stack=96(%rsp)
+movq %r11,96(%rsp)
+
+# qhasm: b2_stack = b2
+# asm 1: movq <b2=int64#10,>b2_stack=stack64#14
+# asm 2: movq <b2=%r12,>b2_stack=104(%rsp)
+movq %r12,104(%rsp)
+
+# qhasm: b3_stack = b3
+# asm 1: movq <b3=int64#11,>b3_stack=stack64#15
+# asm 2: movq <b3=%r13,>b3_stack=112(%rsp)
+movq %r13,112(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = a0_stack
+# asm 1: movq <a0_stack=stack64#8,>mulx0=int64#10
+# asm 2: movq <a0_stack=56(%rsp),>mulx0=%r12
+movq 56(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: a0 = mulrax
+# asm 1: mov <mulrax=int64#7,>a0=int64#11
+# asm 2: mov <mulrax=%rax,>a0=%r13
+mov %rax,%r13
+
+# qhasm: a1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>a1=int64#12
+# asm 2: mov <mulrdx=%rdx,>a1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: a2 = 0
+# asm 1: mov $0,>a2=int64#13
+# asm 2: mov $0,>a2=%r15
+mov $0,%r15
+
+# qhasm: a2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a2=int64#13
+# asm 2: adc <mulrdx=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: a3 = 0
+# asm 1: mov $0,>a3=int64#14
+# asm 2: mov $0,>a3=%rbx
+mov $0,%rbx
+
+# qhasm: a3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<a3=int64#14
+# asm 2: adc <mulrdx=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = a1_stack
+# asm 1: movq <a1_stack=stack64#9,>mulx1=int64#10
+# asm 2: movq <a1_stack=64(%rsp),>mulx1=%r12
+movq 64(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a1 += mulrax
+# asm 1: add <mulrax=int64#7,<a1=int64#12
+# asm 2: add <mulrax=%rax,<a1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a2 += mulc
+# asm 1: add <mulc=int64#15,<a2=int64#13
+# asm 2: add <mulc=%rbp,<a2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = a2_stack
+# asm 1: movq <a2_stack=stack64#10,>mulx2=int64#10
+# asm 2: movq <a2_stack=72(%rsp),>mulx2=%r12
+movq 72(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a2 += mulrax
+# asm 1: add <mulrax=int64#7,<a2=int64#13
+# asm 2: add <mulrax=%rax,<a2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? a3 += mulc
+# asm 1: add <mulc=int64#15,<a3=int64#14
+# asm 2: add <mulc=%rbp,<a3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = a3_stack
+# asm 1: movq <a3_stack=stack64#11,>mulx3=int64#10
+# asm 2: movq <a3_stack=80(%rsp),>mulx3=%r12
+movq 80(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 0)
+# asm 1: movq 0(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 0(<qp=%rcx),>mulrax=%rax
+movq 0(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? a3 += mulrax
+# asm 1: add <mulrax=int64#7,<a3=int64#14
+# asm 2: add <mulrax=%rax,<a3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 8)
+# asm 1: movq 8(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 8(<qp=%rcx),>mulrax=%rax
+movq 8(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 16)
+# asm 1: movq 16(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 16(<qp=%rcx),>mulrax=%rax
+movq 16(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 24)
+# asm 1: movq 24(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 24(<qp=%rcx),>mulrax=%rax
+movq 24(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? a0 += mulr4
+# asm 1: add <mulr4=int64#5,<a0=int64#11
+# asm 2: add <mulr4=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<a1=int64#12
+# asm 2: adc <mulr5=%r9,<a1=%r14
+adc %r9,%r14
+
+# qhasm: carry? a2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<a2=int64#13
+# asm 2: adc <mulr6=%r10,<a2=%r15
+adc %r10,%r15
+
+# qhasm: carry? a3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<a3=int64#14
+# asm 2: adc <mulr7=%r11,<a3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? a0 += mulr8
+# asm 1: add <mulr8=int64#5,<a0=int64#11
+# asm 2: add <mulr8=%r8,<a0=%r13
+add %r8,%r13
+
+# qhasm: carry? a1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a1=int64#12
+# asm 2: adc <mulzero=%rdx,<a1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? a2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a2=int64#13
+# asm 2: adc <mulzero=%rdx,<a2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? a3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<a3=int64#14
+# asm 2: adc <mulzero=%rdx,<a3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: a0 += mulzero
+# asm 1: add <mulzero=int64#3,<a0=int64#11
+# asm 2: add <mulzero=%rdx,<a0=%r13
+add %rdx,%r13
+
+# qhasm: a0_stack = a0
+# asm 1: movq <a0=int64#11,>a0_stack=stack64#8
+# asm 2: movq <a0=%r13,>a0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: a1_stack = a1
+# asm 1: movq <a1=int64#12,>a1_stack=stack64#9
+# asm 2: movq <a1=%r14,>a1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: a2_stack = a2
+# asm 1: movq <a2=int64#13,>a2_stack=stack64#10
+# asm 2: movq <a2=%r15,>a2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: a3_stack = a3
+# asm 1: movq <a3=int64#14,>a3_stack=stack64#11
+# asm 2: movq <a3=%rbx,>a3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = b0_stack
+# asm 1: movq <b0_stack=stack64#12,>mulx0=int64#10
+# asm 2: movq <b0_stack=88(%rsp),>mulx0=%r12
+movq 88(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rx0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rx0=int64#11
+# asm 2: mov <mulrax=%rax,>rx0=%r13
+mov %rax,%r13
+
+# qhasm: rx1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rx1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rx1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: rx2 = 0
+# asm 1: mov $0,>rx2=int64#13
+# asm 2: mov $0,>rx2=%r15
+mov $0,%r15
+
+# qhasm: rx2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: rx3 = 0
+# asm 1: mov $0,>rx3=int64#14
+# asm 2: mov $0,>rx3=%rbx
+mov $0,%rbx
+
+# qhasm: rx3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rx3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = b1_stack
+# asm 1: movq <b1_stack=stack64#13,>mulx1=int64#10
+# asm 2: movq <b1_stack=96(%rsp),>mulx1=%r12
+movq 96(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx1 += mulrax
+# asm 1: add <mulrax=int64#7,<rx1=int64#12
+# asm 2: add <mulrax=%rax,<rx1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx2 += mulc
+# asm 1: add <mulc=int64#15,<rx2=int64#13
+# asm 2: add <mulc=%rbp,<rx2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = b2_stack
+# asm 1: movq <b2_stack=stack64#14,>mulx2=int64#10
+# asm 2: movq <b2_stack=104(%rsp),>mulx2=%r12
+movq 104(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx2 += mulrax
+# asm 1: add <mulrax=int64#7,<rx2=int64#13
+# asm 2: add <mulrax=%rax,<rx2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rx3 += mulc
+# asm 1: add <mulc=int64#15,<rx3=int64#14
+# asm 2: add <mulc=%rbp,<rx3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = b3_stack
+# asm 1: movq <b3_stack=stack64#15,>mulx3=int64#10
+# asm 2: movq <b3_stack=112(%rsp),>mulx3=%r12
+movq 112(%rsp),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 32)
+# asm 1: movq 32(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 32(<qp=%rcx),>mulrax=%rax
+movq 32(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? rx3 += mulrax
+# asm 1: add <mulrax=int64#7,<rx3=int64#14
+# asm 2: add <mulrax=%rax,<rx3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 40)
+# asm 1: movq 40(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 40(<qp=%rcx),>mulrax=%rax
+movq 40(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 48)
+# asm 1: movq 48(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 48(<qp=%rcx),>mulrax=%rax
+movq 48(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 56)
+# asm 1: movq 56(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 56(<qp=%rcx),>mulrax=%rax
+movq 56(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rx0 += mulr4
+# asm 1: add <mulr4=int64#5,<rx0=int64#11
+# asm 2: add <mulr4=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<rx1=int64#12
+# asm 2: adc <mulr5=%r9,<rx1=%r14
+adc %r9,%r14
+
+# qhasm: carry? rx2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<rx2=int64#13
+# asm 2: adc <mulr6=%r10,<rx2=%r15
+adc %r10,%r15
+
+# qhasm: carry? rx3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<rx3=int64#14
+# asm 2: adc <mulr7=%r11,<rx3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? rx0 += mulr8
+# asm 1: add <mulr8=int64#5,<rx0=int64#11
+# asm 2: add <mulr8=%r8,<rx0=%r13
+add %r8,%r13
+
+# qhasm: carry? rx1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx1=int64#12
+# asm 2: adc <mulzero=%rdx,<rx1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? rx2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx2=int64#13
+# asm 2: adc <mulzero=%rdx,<rx2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? rx3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<rx3=int64#14
+# asm 2: adc <mulzero=%rdx,<rx3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: rx0 += mulzero
+# asm 1: add <mulzero=int64#3,<rx0=int64#11
+# asm 2: add <mulzero=%rdx,<rx0=%r13
+add %rdx,%r13
+
+# qhasm: ry0 = rx0
+# asm 1: mov <rx0=int64#11,>ry0=int64#3
+# asm 2: mov <rx0=%r13,>ry0=%rdx
+mov %r13,%rdx
+
+# qhasm: ry1 = rx1
+# asm 1: mov <rx1=int64#12,>ry1=int64#5
+# asm 2: mov <rx1=%r14,>ry1=%r8
+mov %r14,%r8
+
+# qhasm: ry2 = rx2
+# asm 1: mov <rx2=int64#13,>ry2=int64#6
+# asm 2: mov <rx2=%r15,>ry2=%r9
+mov %r15,%r9
+
+# qhasm: ry3 = rx3
+# asm 1: mov <rx3=int64#14,>ry3=int64#7
+# asm 2: mov <rx3=%rbx,>ry3=%rax
+mov %rbx,%rax
+
+# qhasm: carry? ry0 += a0_stack
+# asm 1: addq <a0_stack=stack64#8,<ry0=int64#3
+# asm 2: addq <a0_stack=56(%rsp),<ry0=%rdx
+addq 56(%rsp),%rdx
+
+# qhasm: carry? ry1 += a1_stack + carry
+# asm 1: adcq <a1_stack=stack64#9,<ry1=int64#5
+# asm 2: adcq <a1_stack=64(%rsp),<ry1=%r8
+adcq 64(%rsp),%r8
+
+# qhasm: carry? ry2 += a2_stack + carry
+# asm 1: adcq <a2_stack=stack64#10,<ry2=int64#6
+# asm 2: adcq <a2_stack=72(%rsp),<ry2=%r9
+adcq 72(%rsp),%r9
+
+# qhasm: carry? ry3 += a3_stack + carry
+# asm 1: adcq <a3_stack=stack64#11,<ry3=int64#7
+# asm 2: adcq <a3_stack=80(%rsp),<ry3=%rax
+adcq 80(%rsp),%rax
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#8
+# asm 2: mov $0,>addt0=%r10
+mov $0,%r10
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#9
+# asm 2: mov $38,>addt1=%r11
+mov $38,%r11
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#8,<addt1=int64#9
+# asm 2: cmovae <addt0=%r10,<addt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? ry0 += addt1
+# asm 1: add <addt1=int64#9,<ry0=int64#3
+# asm 2: add <addt1=%r11,<ry0=%rdx
+add %r11,%rdx
+
+# qhasm: carry? ry1 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry1=int64#5
+# asm 2: adc <addt0=%r10,<ry1=%r8
+adc %r10,%r8
+
+# qhasm: carry? ry2 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry2=int64#6
+# asm 2: adc <addt0=%r10,<ry2=%r9
+adc %r10,%r9
+
+# qhasm: carry? ry3 += addt0 + carry
+# asm 1: adc <addt0=int64#8,<ry3=int64#7
+# asm 2: adc <addt0=%r10,<ry3=%rax
+adc %r10,%rax
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#9,<addt0=int64#8
+# asm 2: cmovc <addt1=%r11,<addt0=%r10
+cmovc %r11,%r10
+
+# qhasm: ry0 += addt0
+# asm 1: add <addt0=int64#8,<ry0=int64#3
+# asm 2: add <addt0=%r10,<ry0=%rdx
+add %r10,%rdx
+
+# qhasm: carry? rx0 -= a0_stack
+# asm 1: subq <a0_stack=stack64#8,<rx0=int64#11
+# asm 2: subq <a0_stack=56(%rsp),<rx0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rx1 -= a1_stack - carry
+# asm 1: sbbq <a1_stack=stack64#9,<rx1=int64#12
+# asm 2: sbbq <a1_stack=64(%rsp),<rx1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rx2 -= a2_stack - carry
+# asm 1: sbbq <a2_stack=stack64#10,<rx2=int64#13
+# asm 2: sbbq <a2_stack=72(%rsp),<rx2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rx3 -= a3_stack - carry
+# asm 1: sbbq <a3_stack=stack64#11,<rx3=int64#14
+# asm 2: sbbq <a3_stack=80(%rsp),<rx3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#8
+# asm 2: mov $0,>subt0=%r10
+mov $0,%r10
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#9
+# asm 2: mov $38,>subt1=%r11
+mov $38,%r11
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#8,<subt1=int64#9
+# asm 2: cmovae <subt0=%r10,<subt1=%r11
+cmovae %r10,%r11
+
+# qhasm: carry? rx0 -= subt1
+# asm 1: sub <subt1=int64#9,<rx0=int64#11
+# asm 2: sub <subt1=%r11,<rx0=%r13
+sub %r11,%r13
+
+# qhasm: carry? rx1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx1=int64#12
+# asm 2: sbb <subt0=%r10,<rx1=%r14
+sbb %r10,%r14
+
+# qhasm: carry? rx2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx2=int64#13
+# asm 2: sbb <subt0=%r10,<rx2=%r15
+sbb %r10,%r15
+
+# qhasm: carry? rx3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#8,<rx3=int64#14
+# asm 2: sbb <subt0=%r10,<rx3=%rbx
+sbb %r10,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#9,<subt0=int64#8
+# asm 2: cmovc <subt1=%r11,<subt0=%r10
+cmovc %r11,%r10
+
+# qhasm: rx0 -= subt0
+# asm 1: sub <subt0=int64#8,<rx0=int64#11
+# asm 2: sub <subt0=%r10,<rx0=%r13
+sub %r10,%r13
+
+# qhasm: *(uint64 *) (rp + 0) = rx0
+# asm 1: movq <rx0=int64#11,0(<rp=int64#1)
+# asm 2: movq <rx0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *) (rp + 8) = rx1
+# asm 1: movq <rx1=int64#12,8(<rp=int64#1)
+# asm 2: movq <rx1=%r14,8(<rp=%rdi)
+movq %r14,8(%rdi)
+
+# qhasm: *(uint64 *) (rp + 16) = rx2
+# asm 1: movq <rx2=int64#13,16(<rp=int64#1)
+# asm 2: movq <rx2=%r15,16(<rp=%rdi)
+movq %r15,16(%rdi)
+
+# qhasm: *(uint64 *) (rp + 24) = rx3
+# asm 1: movq <rx3=int64#14,24(<rp=int64#1)
+# asm 2: movq <rx3=%rbx,24(<rp=%rdi)
+movq %rbx,24(%rdi)
+
+# qhasm: *(uint64 *) (rp + 64) = ry0
+# asm 1: movq <ry0=int64#3,64(<rp=int64#1)
+# asm 2: movq <ry0=%rdx,64(<rp=%rdi)
+movq %rdx,64(%rdi)
+
+# qhasm: *(uint64 *) (rp + 72) = ry1
+# asm 1: movq <ry1=int64#5,72(<rp=int64#1)
+# asm 2: movq <ry1=%r8,72(<rp=%rdi)
+movq %r8,72(%rdi)
+
+# qhasm: *(uint64 *) (rp + 80) = ry2
+# asm 1: movq <ry2=int64#6,80(<rp=int64#1)
+# asm 2: movq <ry2=%r9,80(<rp=%rdi)
+movq %r9,80(%rdi)
+
+# qhasm: *(uint64 *) (rp + 88) = ry3
+# asm 1: movq <ry3=int64#7,88(<rp=int64#1)
+# asm 2: movq <ry3=%rax,88(<rp=%rdi)
+movq %rax,88(%rdi)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 96)
+# asm 1: movq 96(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 96(<pp=%rsi),>mulx0=%r12
+movq 96(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: c0 = mulrax
+# asm 1: mov <mulrax=int64#7,>c0=int64#11
+# asm 2: mov <mulrax=%rax,>c0=%r13
+mov %rax,%r13
+
+# qhasm: c1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>c1=int64#12
+# asm 2: mov <mulrdx=%rdx,>c1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: c2 = 0
+# asm 1: mov $0,>c2=int64#13
+# asm 2: mov $0,>c2=%r15
+mov $0,%r15
+
+# qhasm: c2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c2=int64#13
+# asm 2: adc <mulrdx=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: c3 = 0
+# asm 1: mov $0,>c3=int64#14
+# asm 2: mov $0,>c3=%rbx
+mov $0,%rbx
+
+# qhasm: c3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<c3=int64#14
+# asm 2: adc <mulrdx=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 104)
+# asm 1: movq 104(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 104(<pp=%rsi),>mulx1=%r12
+movq 104(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c1 += mulrax
+# asm 1: add <mulrax=int64#7,<c1=int64#12
+# asm 2: add <mulrax=%rax,<c1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c2 += mulc
+# asm 1: add <mulc=int64#15,<c2=int64#13
+# asm 2: add <mulc=%rbp,<c2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 112)
+# asm 1: movq 112(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 112(<pp=%rsi),>mulx2=%r12
+movq 112(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c2 += mulrax
+# asm 1: add <mulrax=int64#7,<c2=int64#13
+# asm 2: add <mulrax=%rax,<c2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? c3 += mulc
+# asm 1: add <mulc=int64#15,<c3=int64#14
+# asm 2: add <mulc=%rbp,<c3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 120)
+# asm 1: movq 120(<pp=int64#2),>mulx3=int64#10
+# asm 2: movq 120(<pp=%rsi),>mulx3=%r12
+movq 120(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 96)
+# asm 1: movq 96(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 96(<qp=%rcx),>mulrax=%rax
+movq 96(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? c3 += mulrax
+# asm 1: add <mulrax=int64#7,<c3=int64#14
+# asm 2: add <mulrax=%rax,<c3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 104)
+# asm 1: movq 104(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 104(<qp=%rcx),>mulrax=%rax
+movq 104(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 112)
+# asm 1: movq 112(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 112(<qp=%rcx),>mulrax=%rax
+movq 112(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 120)
+# asm 1: movq 120(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 120(<qp=%rcx),>mulrax=%rax
+movq 120(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#10
+# asm 2: mul <mulx3=%r12
+mul %r12
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#15,<mulr6=int64#8
+# asm 2: add <mulc=%rbp,<mulr6=%r10
+add %rbp,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#5
+# asm 2: mov <mulrax=%rax,>mulr4=%r8
+mov %rax,%r8
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#6
+# asm 2: mov <mulrdx=%rdx,>mulr5=%r9
+mov %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#9
+# asm 2: add <mulrax=%rax,<mulr7=%r11
+add %rax,%r11
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? c0 += mulr4
+# asm 1: add <mulr4=int64#5,<c0=int64#11
+# asm 2: add <mulr4=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#6,<c1=int64#12
+# asm 2: adc <mulr5=%r9,<c1=%r14
+adc %r9,%r14
+
+# qhasm: carry? c2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#8,<c2=int64#13
+# asm 2: adc <mulr6=%r10,<c2=%r15
+adc %r10,%r15
+
+# qhasm: carry? c3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#9,<c3=int64#14
+# asm 2: adc <mulr7=%r11,<c3=%rbx
+adc %r11,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#3
+# asm 2: mov $0,>mulzero=%rdx
+mov $0,%rdx
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulr8=int64#7
+# asm 2: adc <mulzero=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#5
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%r8
+imulq $38,%rax,%r8
+
+# qhasm: carry? c0 += mulr8
+# asm 1: add <mulr8=int64#5,<c0=int64#11
+# asm 2: add <mulr8=%r8,<c0=%r13
+add %r8,%r13
+
+# qhasm: carry? c1 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c1=int64#12
+# asm 2: adc <mulzero=%rdx,<c1=%r14
+adc %rdx,%r14
+
+# qhasm: carry? c2 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c2=int64#13
+# asm 2: adc <mulzero=%rdx,<c2=%r15
+adc %rdx,%r15
+
+# qhasm: carry? c3 += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<c3=int64#14
+# asm 2: adc <mulzero=%rdx,<c3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#3,<mulzero=int64#3
+# asm 2: adc <mulzero=%rdx,<mulzero=%rdx
+adc %rdx,%rdx
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#3,>mulzero=int64#3
+# asm 2: imulq $38,<mulzero=%rdx,>mulzero=%rdx
+imulq $38,%rdx,%rdx
+
+# qhasm: c0 += mulzero
+# asm 1: add <mulzero=int64#3,<c0=int64#11
+# asm 2: add <mulzero=%rdx,<c0=%r13
+add %rdx,%r13
+
+# qhasm: c0_stack = c0
+# asm 1: movq <c0=int64#11,>c0_stack=stack64#8
+# asm 2: movq <c0=%r13,>c0_stack=56(%rsp)
+movq %r13,56(%rsp)
+
+# qhasm: c1_stack = c1
+# asm 1: movq <c1=int64#12,>c1_stack=stack64#9
+# asm 2: movq <c1=%r14,>c1_stack=64(%rsp)
+movq %r14,64(%rsp)
+
+# qhasm: c2_stack = c2
+# asm 1: movq <c2=int64#13,>c2_stack=stack64#10
+# asm 2: movq <c2=%r15,>c2_stack=72(%rsp)
+movq %r15,72(%rsp)
+
+# qhasm: c3_stack = c3
+# asm 1: movq <c3=int64#14,>c3_stack=stack64#11
+# asm 2: movq <c3=%rbx,>c3_stack=80(%rsp)
+movq %rbx,80(%rsp)
+
+# qhasm: mulr4 = 0
+# asm 1: mov $0,>mulr4=int64#5
+# asm 2: mov $0,>mulr4=%r8
+mov $0,%r8
+
+# qhasm: mulr5 = 0
+# asm 1: mov $0,>mulr5=int64#6
+# asm 2: mov $0,>mulr5=%r9
+mov $0,%r9
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#8
+# asm 2: mov $0,>mulr6=%r10
+mov $0,%r10
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#9
+# asm 2: mov $0,>mulr7=%r11
+mov $0,%r11
+
+# qhasm: mulx0 = *(uint64 *)(pp + 64)
+# asm 1: movq 64(<pp=int64#2),>mulx0=int64#10
+# asm 2: movq 64(<pp=%rsi),>mulx0=%r12
+movq 64(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: rt0 = mulrax
+# asm 1: mov <mulrax=int64#7,>rt0=int64#11
+# asm 2: mov <mulrax=%rax,>rt0=%r13
+mov %rax,%r13
+
+# qhasm: rt1 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>rt1=int64#12
+# asm 2: mov <mulrdx=%rdx,>rt1=%r14
+mov %rdx,%r14
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: rt2 = 0
+# asm 1: mov $0,>rt2=int64#13
+# asm 2: mov $0,>rt2=%r15
+mov $0,%r15
+
+# qhasm: rt2 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt2=int64#13
+# asm 2: adc <mulrdx=%rdx,<rt2=%r15
+adc %rdx,%r15
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: rt3 = 0
+# asm 1: mov $0,>rt3=int64#14
+# asm 2: mov $0,>rt3=%rbx
+mov $0,%rbx
+
+# qhasm: rt3 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<rt3=int64#14
+# asm 2: adc <mulrdx=%rdx,<rt3=%rbx
+adc %rdx,%rbx
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx0
+# asm 1: mul <mulx0=int64#10
+# asm 2: mul <mulx0=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulr4 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr4=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr4=%r8
+adc %rdx,%r8
+
+# qhasm: mulx1 = *(uint64 *)(pp + 72)
+# asm 1: movq 72(<pp=int64#2),>mulx1=int64#10
+# asm 2: movq 72(<pp=%rsi),>mulx1=%r12
+movq 72(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt1 += mulrax
+# asm 1: add <mulrax=int64#7,<rt1=int64#12
+# asm 2: add <mulrax=%rax,<rt1=%r14
+add %rax,%r14
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt2 += mulc
+# asm 1: add <mulc=int64#15,<rt2=int64#13
+# asm 2: add <mulc=%rbp,<rt2=%r15
+add %rbp,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx1
+# asm 1: mul <mulx1=int64#10
+# asm 2: mul <mulx1=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulr5 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr5=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr5=%r9
+adc %rdx,%r9
+
+# qhasm: mulx2 = *(uint64 *)(pp + 80)
+# asm 1: movq 80(<pp=int64#2),>mulx2=int64#10
+# asm 2: movq 80(<pp=%rsi),>mulx2=%r12
+movq 80(%rsi),%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt2 += mulrax
+# asm 1: add <mulrax=int64#7,<rt2=int64#13
+# asm 2: add <mulrax=%rax,<rt2=%r15
+add %rax,%r15
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? rt3 += mulc
+# asm 1: add <mulc=int64#15,<rt3=int64#14
+# asm 2: add <mulc=%rbp,<rt3=%rbx
+add %rbp,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#15,<mulr4=int64#5
+# asm 2: add <mulc=%rbp,<mulr4=%r8
+add %rbp,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#15
+# asm 2: mov $0,>mulc=%rbp
+mov $0,%rbp
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#15
+# asm 2: adc <mulrdx=%rdx,<mulc=%rbp
+adc %rdx,%rbp
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx2
+# asm 1: mul <mulx2=int64#10
+# asm 2: mul <mulx2=%r12
+mul %r12
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#15,<mulr5=int64#6
+# asm 2: add <mulc=%rbp,<mulr5=%r9
+add %rbp,%r9
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#8
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r10
+adc %rdx,%r10
+
+# qhasm: mulx3 = *(uint64 *)(pp + 88)
+# asm 1: movq 88(<pp=int64#2),>mulx3=int64#2
+# asm 2: movq 88(<pp=%rsi),>mulx3=%rsi
+movq 88(%rsi),%rsi
+
+# qhasm: mulrax = *(uint64 *)(qp + 64)
+# asm 1: movq 64(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 64(<qp=%rcx),>mulrax=%rax
+movq 64(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? rt3 += mulrax
+# asm 1: add <mulrax=int64#7,<rt3=int64#14
+# asm 2: add <mulrax=%rax,<rt3=%rbx
+add %rax,%rbx
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 72)
+# asm 1: movq 72(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 72(<qp=%rcx),>mulrax=%rax
+movq 72(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr4 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr4=int64#5
+# asm 2: add <mulrax=%rax,<mulr4=%r8
+add %rax,%r8
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr4 += mulc
+# asm 1: add <mulc=int64#10,<mulr4=int64#5
+# asm 2: add <mulc=%r12,<mulr4=%r8
+add %r12,%r8
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 80)
+# asm 1: movq 80(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 80(<qp=%rcx),>mulrax=%rax
+movq 80(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#6
+# asm 2: add <mulrax=%rax,<mulr5=%r9
+add %rax,%r9
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr5 += mulc
+# asm 1: add <mulc=int64#10,<mulr5=int64#6
+# asm 2: add <mulc=%r12,<mulr5=%r9
+add %r12,%r9
+
+# qhasm: mulc = 0
+# asm 1: mov $0,>mulc=int64#10
+# asm 2: mov $0,>mulc=%r12
+mov $0,%r12
+
+# qhasm: mulc += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulc=int64#10
+# asm 2: adc <mulrdx=%rdx,<mulc=%r12
+adc %rdx,%r12
+
+# qhasm: mulrax = *(uint64 *)(qp + 88)
+# asm 1: movq 88(<qp=int64#4),>mulrax=int64#7
+# asm 2: movq 88(<qp=%rcx),>mulrax=%rax
+movq 88(%rcx),%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * mulx3
+# asm 1: mul <mulx3=int64#2
+# asm 2: mul <mulx3=%rsi
+mul %rsi
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#8
+# asm 2: add <mulrax=%rax,<mulr6=%r10
+add %rax,%r10
+
+# qhasm: mulrdx += 0 + carry
+# asm 1: adc $0,<mulrdx=int64#3
+# asm 2: adc $0,<mulrdx=%rdx
+adc $0,%rdx
+
+# qhasm: carry? mulr6 += mulc
+# asm 1: add <mulc=int64#10,<mulr6=int64#8
+# asm 2: add <mulc=%r12,<mulr6=%r10
+add %r12,%r10
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#9
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r11
+adc %rdx,%r11
+
+# qhasm: mulrax = mulr4
+# asm 1: mov <mulr4=int64#5,>mulrax=int64#7
+# asm 2: mov <mulr4=%r8,>mulrax=%rax
+mov %r8,%rax
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: mulr4 = mulrax
+# asm 1: mov <mulrax=int64#7,>mulr4=int64#2
+# asm 2: mov <mulrax=%rax,>mulr4=%rsi
+mov %rax,%rsi
+
+# qhasm: mulrax = mulr5
+# asm 1: mov <mulr5=int64#6,>mulrax=int64#7
+# asm 2: mov <mulr5=%r9,>mulrax=%rax
+mov %r9,%rax
+
+# qhasm: mulr5 = mulrdx
+# asm 1: mov <mulrdx=int64#3,>mulr5=int64#4
+# asm 2: mov <mulrdx=%rdx,>mulr5=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr5 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr5=int64#4
+# asm 2: add <mulrax=%rax,<mulr5=%rcx
+add %rax,%rcx
+
+# qhasm: mulrax = mulr6
+# asm 1: mov <mulr6=int64#8,>mulrax=int64#7
+# asm 2: mov <mulr6=%r10,>mulrax=%rax
+mov %r10,%rax
+
+# qhasm: mulr6 = 0
+# asm 1: mov $0,>mulr6=int64#5
+# asm 2: mov $0,>mulr6=%r8
+mov $0,%r8
+
+# qhasm: mulr6 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr6=int64#5
+# asm 2: adc <mulrdx=%rdx,<mulr6=%r8
+adc %rdx,%r8
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr6 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr6=int64#5
+# asm 2: add <mulrax=%rax,<mulr6=%r8
+add %rax,%r8
+
+# qhasm: mulrax = mulr7
+# asm 1: mov <mulr7=int64#9,>mulrax=int64#7
+# asm 2: mov <mulr7=%r11,>mulrax=%rax
+mov %r11,%rax
+
+# qhasm: mulr7 = 0
+# asm 1: mov $0,>mulr7=int64#6
+# asm 2: mov $0,>mulr7=%r9
+mov $0,%r9
+
+# qhasm: mulr7 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr7=int64#6
+# asm 2: adc <mulrdx=%rdx,<mulr7=%r9
+adc %rdx,%r9
+
+# qhasm: (uint128) mulrdx mulrax = mulrax * *(uint64 *)&crypto_sign_ed25519_amd64_64_38
+mulq crypto_sign_ed25519_amd64_64_38(%rip)
+
+# qhasm: carry? mulr7 += mulrax
+# asm 1: add <mulrax=int64#7,<mulr7=int64#6
+# asm 2: add <mulrax=%rax,<mulr7=%r9
+add %rax,%r9
+
+# qhasm: mulr8 = 0
+# asm 1: mov $0,>mulr8=int64#7
+# asm 2: mov $0,>mulr8=%rax
+mov $0,%rax
+
+# qhasm: mulr8 += mulrdx + carry
+# asm 1: adc <mulrdx=int64#3,<mulr8=int64#7
+# asm 2: adc <mulrdx=%rdx,<mulr8=%rax
+adc %rdx,%rax
+
+# qhasm: carry? rt0 += mulr4
+# asm 1: add <mulr4=int64#2,<rt0=int64#11
+# asm 2: add <mulr4=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt1 += mulr5 + carry
+# asm 1: adc <mulr5=int64#4,<rt1=int64#12
+# asm 2: adc <mulr5=%rcx,<rt1=%r14
+adc %rcx,%r14
+
+# qhasm: carry? rt2 += mulr6 + carry
+# asm 1: adc <mulr6=int64#5,<rt2=int64#13
+# asm 2: adc <mulr6=%r8,<rt2=%r15
+adc %r8,%r15
+
+# qhasm: carry? rt3 += mulr7 + carry
+# asm 1: adc <mulr7=int64#6,<rt3=int64#14
+# asm 2: adc <mulr7=%r9,<rt3=%rbx
+adc %r9,%rbx
+
+# qhasm: mulzero = 0
+# asm 1: mov $0,>mulzero=int64#2
+# asm 2: mov $0,>mulzero=%rsi
+mov $0,%rsi
+
+# qhasm: mulr8 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulr8=int64#7
+# asm 2: adc <mulzero=%rsi,<mulr8=%rax
+adc %rsi,%rax
+
+# qhasm: mulr8 *= 38
+# asm 1: imulq $38,<mulr8=int64#7,>mulr8=int64#3
+# asm 2: imulq $38,<mulr8=%rax,>mulr8=%rdx
+imulq $38,%rax,%rdx
+
+# qhasm: carry? rt0 += mulr8
+# asm 1: add <mulr8=int64#3,<rt0=int64#11
+# asm 2: add <mulr8=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt1=int64#12
+# asm 2: adc <mulzero=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt2=int64#13
+# asm 2: adc <mulzero=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<rt3=int64#14
+# asm 2: adc <mulzero=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: mulzero += mulzero + carry
+# asm 1: adc <mulzero=int64#2,<mulzero=int64#2
+# asm 2: adc <mulzero=%rsi,<mulzero=%rsi
+adc %rsi,%rsi
+
+# qhasm: mulzero *= 38
+# asm 1: imulq $38,<mulzero=int64#2,>mulzero=int64#2
+# asm 2: imulq $38,<mulzero=%rsi,>mulzero=%rsi
+imulq $38,%rsi,%rsi
+
+# qhasm: rt0 += mulzero
+# asm 1: add <mulzero=int64#2,<rt0=int64#11
+# asm 2: add <mulzero=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: carry? rt0 += rt0
+# asm 1: add <rt0=int64#11,<rt0=int64#11
+# asm 2: add <rt0=%r13,<rt0=%r13
+add %r13,%r13
+
+# qhasm: carry? rt1 += rt1 + carry
+# asm 1: adc <rt1=int64#12,<rt1=int64#12
+# asm 2: adc <rt1=%r14,<rt1=%r14
+adc %r14,%r14
+
+# qhasm: carry? rt2 += rt2 + carry
+# asm 1: adc <rt2=int64#13,<rt2=int64#13
+# asm 2: adc <rt2=%r15,<rt2=%r15
+adc %r15,%r15
+
+# qhasm: carry? rt3 += rt3 + carry
+# asm 1: adc <rt3=int64#14,<rt3=int64#14
+# asm 2: adc <rt3=%rbx,<rt3=%rbx
+adc %rbx,%rbx
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#2
+# asm 2: mov $0,>addt0=%rsi
+mov $0,%rsi
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#3
+# asm 2: mov $38,>addt1=%rdx
+mov $38,%rdx
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#2,<addt1=int64#3
+# asm 2: cmovae <addt0=%rsi,<addt1=%rdx
+cmovae %rsi,%rdx
+
+# qhasm: carry? rt0 += addt1
+# asm 1: add <addt1=int64#3,<rt0=int64#11
+# asm 2: add <addt1=%rdx,<rt0=%r13
+add %rdx,%r13
+
+# qhasm: carry? rt1 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt1=int64#12
+# asm 2: adc <addt0=%rsi,<rt1=%r14
+adc %rsi,%r14
+
+# qhasm: carry? rt2 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt2=int64#13
+# asm 2: adc <addt0=%rsi,<rt2=%r15
+adc %rsi,%r15
+
+# qhasm: carry? rt3 += addt0 + carry
+# asm 1: adc <addt0=int64#2,<rt3=int64#14
+# asm 2: adc <addt0=%rsi,<rt3=%rbx
+adc %rsi,%rbx
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#3,<addt0=int64#2
+# asm 2: cmovc <addt1=%rdx,<addt0=%rsi
+cmovc %rdx,%rsi
+
+# qhasm: rt0 += addt0
+# asm 1: add <addt0=int64#2,<rt0=int64#11
+# asm 2: add <addt0=%rsi,<rt0=%r13
+add %rsi,%r13
+
+# qhasm: rz0 = rt0
+# asm 1: mov <rt0=int64#11,>rz0=int64#2
+# asm 2: mov <rt0=%r13,>rz0=%rsi
+mov %r13,%rsi
+
+# qhasm: rz1 = rt1
+# asm 1: mov <rt1=int64#12,>rz1=int64#3
+# asm 2: mov <rt1=%r14,>rz1=%rdx
+mov %r14,%rdx
+
+# qhasm: rz2 = rt2
+# asm 1: mov <rt2=int64#13,>rz2=int64#4
+# asm 2: mov <rt2=%r15,>rz2=%rcx
+mov %r15,%rcx
+
+# qhasm: rz3 = rt3
+# asm 1: mov <rt3=int64#14,>rz3=int64#5
+# asm 2: mov <rt3=%rbx,>rz3=%r8
+mov %rbx,%r8
+
+# qhasm: carry? rz0 += c0_stack
+# asm 1: addq <c0_stack=stack64#8,<rz0=int64#2
+# asm 2: addq <c0_stack=56(%rsp),<rz0=%rsi
+addq 56(%rsp),%rsi
+
+# qhasm: carry? rz1 += c1_stack + carry
+# asm 1: adcq <c1_stack=stack64#9,<rz1=int64#3
+# asm 2: adcq <c1_stack=64(%rsp),<rz1=%rdx
+adcq 64(%rsp),%rdx
+
+# qhasm: carry? rz2 += c2_stack + carry
+# asm 1: adcq <c2_stack=stack64#10,<rz2=int64#4
+# asm 2: adcq <c2_stack=72(%rsp),<rz2=%rcx
+adcq 72(%rsp),%rcx
+
+# qhasm: carry? rz3 += c3_stack + carry
+# asm 1: adcq <c3_stack=stack64#11,<rz3=int64#5
+# asm 2: adcq <c3_stack=80(%rsp),<rz3=%r8
+adcq 80(%rsp),%r8
+
+# qhasm: addt0 = 0
+# asm 1: mov $0,>addt0=int64#6
+# asm 2: mov $0,>addt0=%r9
+mov $0,%r9
+
+# qhasm: addt1 = 38
+# asm 1: mov $38,>addt1=int64#7
+# asm 2: mov $38,>addt1=%rax
+mov $38,%rax
+
+# qhasm: addt1 = addt0 if !carry
+# asm 1: cmovae <addt0=int64#6,<addt1=int64#7
+# asm 2: cmovae <addt0=%r9,<addt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rz0 += addt1
+# asm 1: add <addt1=int64#7,<rz0=int64#2
+# asm 2: add <addt1=%rax,<rz0=%rsi
+add %rax,%rsi
+
+# qhasm: carry? rz1 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz1=int64#3
+# asm 2: adc <addt0=%r9,<rz1=%rdx
+adc %r9,%rdx
+
+# qhasm: carry? rz2 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz2=int64#4
+# asm 2: adc <addt0=%r9,<rz2=%rcx
+adc %r9,%rcx
+
+# qhasm: carry? rz3 += addt0 + carry
+# asm 1: adc <addt0=int64#6,<rz3=int64#5
+# asm 2: adc <addt0=%r9,<rz3=%r8
+adc %r9,%r8
+
+# qhasm: addt0 = addt1 if carry
+# asm 1: cmovc <addt1=int64#7,<addt0=int64#6
+# asm 2: cmovc <addt1=%rax,<addt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rz0 += addt0
+# asm 1: add <addt0=int64#6,<rz0=int64#2
+# asm 2: add <addt0=%r9,<rz0=%rsi
+add %r9,%rsi
+
+# qhasm: carry? rt0 -= c0_stack
+# asm 1: subq <c0_stack=stack64#8,<rt0=int64#11
+# asm 2: subq <c0_stack=56(%rsp),<rt0=%r13
+subq 56(%rsp),%r13
+
+# qhasm: carry? rt1 -= c1_stack - carry
+# asm 1: sbbq <c1_stack=stack64#9,<rt1=int64#12
+# asm 2: sbbq <c1_stack=64(%rsp),<rt1=%r14
+sbbq 64(%rsp),%r14
+
+# qhasm: carry? rt2 -= c2_stack - carry
+# asm 1: sbbq <c2_stack=stack64#10,<rt2=int64#13
+# asm 2: sbbq <c2_stack=72(%rsp),<rt2=%r15
+sbbq 72(%rsp),%r15
+
+# qhasm: carry? rt3 -= c3_stack - carry
+# asm 1: sbbq <c3_stack=stack64#11,<rt3=int64#14
+# asm 2: sbbq <c3_stack=80(%rsp),<rt3=%rbx
+sbbq 80(%rsp),%rbx
+
+# qhasm: subt0 = 0
+# asm 1: mov $0,>subt0=int64#6
+# asm 2: mov $0,>subt0=%r9
+mov $0,%r9
+
+# qhasm: subt1 = 38
+# asm 1: mov $38,>subt1=int64#7
+# asm 2: mov $38,>subt1=%rax
+mov $38,%rax
+
+# qhasm: subt1 = subt0 if !carry
+# asm 1: cmovae <subt0=int64#6,<subt1=int64#7
+# asm 2: cmovae <subt0=%r9,<subt1=%rax
+cmovae %r9,%rax
+
+# qhasm: carry? rt0 -= subt1
+# asm 1: sub <subt1=int64#7,<rt0=int64#11
+# asm 2: sub <subt1=%rax,<rt0=%r13
+sub %rax,%r13
+
+# qhasm: carry? rt1 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt1=int64#12
+# asm 2: sbb <subt0=%r9,<rt1=%r14
+sbb %r9,%r14
+
+# qhasm: carry? rt2 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt2=int64#13
+# asm 2: sbb <subt0=%r9,<rt2=%r15
+sbb %r9,%r15
+
+# qhasm: carry? rt3 -= subt0 - carry
+# asm 1: sbb <subt0=int64#6,<rt3=int64#14
+# asm 2: sbb <subt0=%r9,<rt3=%rbx
+sbb %r9,%rbx
+
+# qhasm: subt0 = subt1 if carry
+# asm 1: cmovc <subt1=int64#7,<subt0=int64#6
+# asm 2: cmovc <subt1=%rax,<subt0=%r9
+cmovc %rax,%r9
+
+# qhasm: rt0 -= subt0
+# asm 1: sub <subt0=int64#6,<rt0=int64#11
+# asm 2: sub <subt0=%r9,<rt0=%r13
+sub %r9,%r13
+
+# qhasm: *(uint64 *)(rp + 32) = rz0
+# asm 1: movq <rz0=int64#2,32(<rp=int64#1)
+# asm 2: movq <rz0=%rsi,32(<rp=%rdi)
+movq %rsi,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = rz1
+# asm 1: movq <rz1=int64#3,40(<rp=int64#1)
+# asm 2: movq <rz1=%rdx,40(<rp=%rdi)
+movq %rdx,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = rz2
+# asm 1: movq <rz2=int64#4,48(<rp=int64#1)
+# asm 2: movq <rz2=%rcx,48(<rp=%rdi)
+movq %rcx,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = rz3
+# asm 1: movq <rz3=int64#5,56(<rp=int64#1)
+# asm 2: movq <rz3=%r8,56(<rp=%rdi)
+movq %r8,56(%rdi)
+
+# qhasm: *(uint64 *)(rp + 96) = rt0
+# asm 1: movq <rt0=int64#11,96(<rp=int64#1)
+# asm 2: movq <rt0=%r13,96(<rp=%rdi)
+movq %r13,96(%rdi)
+
+# qhasm: *(uint64 *)(rp + 104) = rt1
+# asm 1: movq <rt1=int64#12,104(<rp=int64#1)
+# asm 2: movq <rt1=%r14,104(<rp=%rdi)
+movq %r14,104(%rdi)
+
+# qhasm: *(uint64 *)(rp + 112) = rt2
+# asm 1: movq <rt2=int64#13,112(<rp=int64#1)
+# asm 2: movq <rt2=%r15,112(<rp=%rdi)
+movq %r15,112(%rdi)
+
+# qhasm: *(uint64 *)(rp + 120) = rt3
+# asm 1: movq <rt3=int64#14,120(<rp=int64#1)
+# asm 2: movq <rt3=%rbx,120(<rp=%rdi)
+movq %rbx,120(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/ge25519_scalarmult_base.c b/ext/ed25519-amd64-asm/ge25519_scalarmult_base.c
new file mode 100644
index 00000000..986abaf6
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_scalarmult_base.c
@@ -0,0 +1,68 @@
+#include "fe25519.h"
+#include "sc25519.h"
+#include "ge25519.h"
+
+/* Multiples of the base point in Niels' representation */
+static const ge25519_niels ge25519_base_multiples_niels[] = {
+#ifdef SMALLTABLES
+#include "ge25519_base_niels_smalltables.data"
+#else
+#include "ge25519_base_niels.data"
+#endif
+};
+
+/* d */
+static const fe25519 ecd = {{0x75EB4DCA135978A3, 0x00700A4D4141D8AB, 0x8CC740797779E898, 0x52036CEE2B6FFE73}};
+
+void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
+{
+ signed char b[64];
+ int i;
+ ge25519_niels t;
+ fe25519 d;
+
+ sc25519_window4(b,s);
+
+#ifdef SMALLTABLES
+ ge25519_p1p1 tp1p1;
+ choose_t((ge25519_niels *)r, 0, (signed long long) b[1], ge25519_base_multiples_niels);
+ fe25519_sub(&d, &r->y, &r->x);
+ fe25519_add(&r->y, &r->y, &r->x);
+ r->x = d;
+ r->t = r->z;
+ fe25519_setint(&r->z,2);
+ for(i=3;i<64;i+=2)
+ {
+ choose_t(&t, (unsigned long long) i/2, (signed long long) b[i], ge25519_base_multiples_niels);
+ ge25519_nielsadd2(r, &t);
+ }
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ ge25519_dbl_p1p1(&tp1p1,(ge25519_p2 *)r);
+ ge25519_p1p1_to_p3(r, &tp1p1);
+ choose_t(&t, (unsigned long long) 0, (signed long long) b[0], ge25519_base_multiples_niels);
+ fe25519_mul(&t.t2d, &t.t2d, &ecd);
+ ge25519_nielsadd2(r, &t);
+ for(i=2;i<64;i+=2)
+ {
+ choose_t(&t, (unsigned long long) i/2, (signed long long) b[i], ge25519_base_multiples_niels);
+ ge25519_nielsadd2(r, &t);
+ }
+#else
+ choose_t((ge25519_niels *)r, 0, (signed long long) b[0], ge25519_base_multiples_niels);
+ fe25519_sub(&d, &r->y, &r->x);
+ fe25519_add(&r->y, &r->y, &r->x);
+ r->x = d;
+ r->t = r->z;
+ fe25519_setint(&r->z,2);
+ for(i=1;i<64;i++)
+ {
+ choose_t(&t, (unsigned long long) i, (signed long long) b[i], ge25519_base_multiples_niels);
+ ge25519_nielsadd2(r, &t);
+ }
+#endif
+}
diff --git a/ext/ed25519-amd64-asm/ge25519_unpackneg.c b/ext/ed25519-amd64-asm/ge25519_unpackneg.c
new file mode 100644
index 00000000..ff16fd20
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ge25519_unpackneg.c
@@ -0,0 +1,60 @@
+#include "fe25519.h"
+#include "ge25519.h"
+
+/* d */
+static const fe25519 ecd = {{0x75EB4DCA135978A3, 0x00700A4D4141D8AB, 0x8CC740797779E898, 0x52036CEE2B6FFE73}};
+/* sqrt(-1) */
+static const fe25519 sqrtm1 = {{0xC4EE1B274A0EA0B0, 0x2F431806AD2FE478, 0x2B4D00993DFBD7A7, 0x2B8324804FC1DF0B}};
+
+/* return 0 on success, -1 otherwise */
+int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
+{
+ fe25519 t, chk, num, den, den2, den4, den6;
+ unsigned char par = p[31] >> 7;
+
+ fe25519_setint(&r->z,1);
+ fe25519_unpack(&r->y, p);
+ fe25519_square(&num, &r->y); /* x = y^2 */
+ fe25519_mul(&den, &num, &ecd); /* den = dy^2 */
+ fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
+ fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
+
+ /* Computation of sqrt(num/den)
+ 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8)
+ */
+ fe25519_square(&den2, &den);
+ fe25519_square(&den4, &den2);
+ fe25519_mul(&den6, &den4, &den2);
+ fe25519_mul(&t, &den6, &num);
+ fe25519_mul(&t, &t, &den);
+
+ fe25519_pow2523(&t, &t);
+ /* 2. computation of r->x = t * num * den^3
+ */
+ fe25519_mul(&t, &t, &num);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&r->x, &t, &den);
+
+ /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not:
+ */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ fe25519_mul(&r->x, &r->x, &sqrtm1);
+
+ /* 4. Now we have one of the two square roots, except if input was not a square
+ */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ return -1;
+
+ /* 5. Choose the desired square root according to parity:
+ */
+ if(fe25519_getparity(&r->x) != (1-par))
+ fe25519_neg(&r->x, &r->x);
+
+ fe25519_mul(&r->t, &r->x, &r->y);
+ return 0;
+}
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced.s b/ext/ed25519-amd64-asm/heap_rootreplaced.s
new file mode 100644
index 00000000..8fe385b4
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced.s
@@ -0,0 +1,476 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#8),>c1=int64#10
+# asm 2: movq 8(<spc=%r10),>c1=%r12
+movq 8(%r10),%r12
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#8),>c2=int64#11
+# asm 2: movq 16(<spc=%r10),>c2=%r13
+movq 16(%r10),%r13
+
+# qhasm: c3 = *(uint64 *)(spc + 24)
+# asm 1: movq 24(<spc=int64#8),>c3=int64#12
+# asm 2: movq 24(<spc=%r10),>c3=%r14
+movq 24(%r10),%r14
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: carry? c1 -= *(uint64 *)(sprc + 8) - carry
+# asm 1: sbbq 8(<sprc=int64#7),<c1=int64#10
+# asm 2: sbbq 8(<sprc=%rax),<c1=%r12
+sbbq 8(%rax),%r12
+
+# qhasm: carry? c2 -= *(uint64 *)(sprc + 16) - carry
+# asm 1: sbbq 16(<sprc=int64#7),<c2=int64#11
+# asm 2: sbbq 16(<sprc=%rax),<c2=%r13
+sbbq 16(%rax),%r13
+
+# qhasm: carry? c3 -= *(uint64 *)(sprc + 24) - carry
+# asm 1: sbbq 24(<sprc=int64#7),<c3=int64#12
+# asm 2: sbbq 24(<sprc=%rax),<c3=%r14
+sbbq 24(%rax),%r14
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#6),>c1=int64#8
+# asm 2: movq 8(<spc=%r9),>c1=%r10
+movq 8(%r9),%r10
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#6),>c2=int64#9
+# asm 2: movq 16(<spc=%r9),>c2=%r11
+movq 16(%r9),%r11
+
+# qhasm: c3 = *(uint64 *)(spc + 24)
+# asm 1: movq 24(<spc=int64#6),>c3=int64#10
+# asm 2: movq 24(<spc=%r9),>c3=%r12
+movq 24(%r9),%r12
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+
+# qhasm: carry? c1 -= *(uint64 *)(spp + 8) - carry
+# asm 1: sbbq 8(<spp=int64#5),<c1=int64#8
+# asm 2: sbbq 8(<spp=%r8),<c1=%r10
+sbbq 8(%r8),%r10
+
+# qhasm: carry? c2 -= *(uint64 *)(spp + 16) - carry
+# asm 1: sbbq 16(<spp=int64#5),<c2=int64#9
+# asm 2: sbbq 16(<spp=%r8),<c2=%r11
+sbbq 16(%r8),%r11
+
+# qhasm: carry? c3 -= *(uint64 *)(spp + 24) - carry
+# asm 1: sbbq 24(<spp=int64#5),<c3=int64#10
+# asm 2: sbbq 24(<spp=%r8),<c3=%r12
+sbbq 24(%r8),%r12
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s b/ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s
new file mode 100644
index 00000000..488e9c52
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced_1limb.s
@@ -0,0 +1,416 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s b/ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s
new file mode 100644
index 00000000..f9259184
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.s
@@ -0,0 +1,436 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#8),>c1=int64#10
+# asm 2: movq 8(<spc=%r10),>c1=%r12
+movq 8(%r10),%r12
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: carry? c1 -= *(uint64 *)(sprc + 8) - carry
+# asm 1: sbbq 8(<sprc=int64#7),<c1=int64#10
+# asm 2: sbbq 8(<sprc=%rax),<c1=%r12
+sbbq 8(%rax),%r12
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#6),>c1=int64#8
+# asm 2: movq 8(<spc=%r9),>c1=%r10
+movq 8(%r9),%r10
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+
+# qhasm: carry? c1 -= *(uint64 *)(spp + 8) - carry
+# asm 1: sbbq 8(<spp=int64#5),<c1=int64#8
+# asm 2: sbbq 8(<spp=%r8),<c1=%r10
+sbbq 8(%r8),%r10
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s b/ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s
new file mode 100644
index 00000000..dcf890ea
--- /dev/null
+++ b/ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.s
@@ -0,0 +1,456 @@
+
+# qhasm: int64 hp
+
+# qhasm: int64 hlen
+
+# qhasm: int64 sp
+
+# qhasm: int64 pp
+
+# qhasm: input hp
+
+# qhasm: input hlen
+
+# qhasm: input sp
+
+# qhasm: int64 prc
+
+# qhasm: int64 plc
+
+# qhasm: int64 pc
+
+# qhasm: int64 d
+
+# qhasm: int64 spp
+
+# qhasm: int64 sprc
+
+# qhasm: int64 spc
+
+# qhasm: int64 c0
+
+# qhasm: int64 c1
+
+# qhasm: int64 c2
+
+# qhasm: int64 c3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 p0
+
+# qhasm: int64 p1
+
+# qhasm: int64 p2
+
+# qhasm: int64 p3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+.globl crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+_crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs:
+crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: pp = 0
+# asm 1: mov $0,>pp=int64#4
+# asm 2: mov $0,>pp=%rcx
+mov $0,%rcx
+
+# qhasm: siftdownloop:
+._siftdownloop:
+
+# qhasm: prc = pp
+# asm 1: mov <pp=int64#4,>prc=int64#5
+# asm 2: mov <pp=%rcx,>prc=%r8
+mov %rcx,%r8
+
+# qhasm: prc *= 2
+# asm 1: imulq $2,<prc=int64#5,>prc=int64#5
+# asm 2: imulq $2,<prc=%r8,>prc=%r8
+imulq $2,%r8,%r8
+
+# qhasm: pc = prc
+# asm 1: mov <prc=int64#5,>pc=int64#6
+# asm 2: mov <prc=%r8,>pc=%r9
+mov %r8,%r9
+
+# qhasm: prc += 2
+# asm 1: add $2,<prc=int64#5
+# asm 2: add $2,<prc=%r8
+add $2,%r8
+
+# qhasm: pc += 1
+# asm 1: add $1,<pc=int64#6
+# asm 2: add $1,<pc=%r9
+add $1,%r9
+
+# qhasm: unsigned>? hlen - prc
+# asm 1: cmp <prc=int64#5,<hlen=int64#2
+# asm 2: cmp <prc=%r8,<hlen=%rsi
+cmp %r8,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop if !unsigned>
+jbe ._siftuploop
+
+# qhasm: sprc = *(uint64 *)(hp + prc * 8)
+# asm 1: movq (<hp=int64#1,<prc=int64#5,8),>sprc=int64#7
+# asm 2: movq (<hp=%rdi,<prc=%r8,8),>sprc=%rax
+movq (%rdi,%r8,8),%rax
+
+# qhasm: sprc <<= 5
+# asm 1: shl $5,<sprc=int64#7
+# asm 2: shl $5,<sprc=%rax
+shl $5,%rax
+
+# qhasm: sprc += sp
+# asm 1: add <sp=int64#3,<sprc=int64#7
+# asm 2: add <sp=%rdx,<sprc=%rax
+add %rdx,%rax
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#6,8),>spc=int64#8
+# asm 2: movq (<hp=%rdi,<pc=%r9,8),>spc=%r10
+movq (%rdi,%r9,8),%r10
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#8
+# asm 2: shl $5,<spc=%r10
+shl $5,%r10
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#8
+# asm 2: add <sp=%rdx,<spc=%r10
+add %rdx,%r10
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#8),>c0=int64#9
+# asm 2: movq 0(<spc=%r10),>c0=%r11
+movq 0(%r10),%r11
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#8),>c1=int64#10
+# asm 2: movq 8(<spc=%r10),>c1=%r12
+movq 8(%r10),%r12
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#8),>c2=int64#11
+# asm 2: movq 16(<spc=%r10),>c2=%r13
+movq 16(%r10),%r13
+
+# qhasm: carry? c0 -= *(uint64 *)(sprc + 0)
+# asm 1: subq 0(<sprc=int64#7),<c0=int64#9
+# asm 2: subq 0(<sprc=%rax),<c0=%r11
+subq 0(%rax),%r11
+
+# qhasm: carry? c1 -= *(uint64 *)(sprc + 8) - carry
+# asm 1: sbbq 8(<sprc=int64#7),<c1=int64#10
+# asm 2: sbbq 8(<sprc=%rax),<c1=%r12
+sbbq 8(%rax),%r12
+
+# qhasm: carry? c2 -= *(uint64 *)(sprc + 16) - carry
+# asm 1: sbbq 16(<sprc=int64#7),<c2=int64#11
+# asm 2: sbbq 16(<sprc=%rax),<c2=%r13
+sbbq 16(%rax),%r13
+
+# qhasm: pc = prc if carry
+# asm 1: cmovc <prc=int64#5,<pc=int64#6
+# asm 2: cmovc <prc=%r8,<pc=%r9
+cmovc %r8,%r9
+
+# qhasm: spc = sprc if carry
+# asm 1: cmovc <sprc=int64#7,<spc=int64#8
+# asm 2: cmovc <sprc=%rax,<spc=%r10
+cmovc %rax,%r10
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#8
+# asm 2: sub <sp=%rdx,<spc=%r10
+sub %rdx,%r10
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#8
+# asm 2: shr $5,<spc=%r10
+shr $5,%r10
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#8,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r10,(<hp=%rdi,<pp=%rcx,8)
+movq %r10,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#6,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%r9,8)
+movq %r8,(%rdi,%r9,8)
+
+# qhasm: pp = pc
+# asm 1: mov <pc=int64#6,>pp=int64#4
+# asm 2: mov <pc=%r9,>pp=%rcx
+mov %r9,%rcx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftdownloop
+jmp ._siftdownloop
+
+# qhasm: siftuploop:
+._siftuploop:
+
+# qhasm: pc = pp
+# asm 1: mov <pp=int64#4,>pc=int64#2
+# asm 2: mov <pp=%rcx,>pc=%rsi
+mov %rcx,%rsi
+
+# qhasm: pp -= 1
+# asm 1: sub $1,<pp=int64#4
+# asm 2: sub $1,<pp=%rcx
+sub $1,%rcx
+
+# qhasm: (uint64) pp >>= 1
+# asm 1: shr $1,<pp=int64#4
+# asm 2: shr $1,<pp=%rcx
+shr $1,%rcx
+
+# qhasm: unsigned>? pc - 0
+# asm 1: cmp $0,<pc=int64#2
+# asm 2: cmp $0,<pc=%rsi
+cmp $0,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if !unsigned>
+jbe ._end
+
+# qhasm: spp = *(uint64 *)(hp + pp * 8)
+# asm 1: movq (<hp=int64#1,<pp=int64#4,8),>spp=int64#5
+# asm 2: movq (<hp=%rdi,<pp=%rcx,8),>spp=%r8
+movq (%rdi,%rcx,8),%r8
+
+# qhasm: spc = *(uint64 *)(hp + pc * 8)
+# asm 1: movq (<hp=int64#1,<pc=int64#2,8),>spc=int64#6
+# asm 2: movq (<hp=%rdi,<pc=%rsi,8),>spc=%r9
+movq (%rdi,%rsi,8),%r9
+
+# qhasm: spp <<= 5
+# asm 1: shl $5,<spp=int64#5
+# asm 2: shl $5,<spp=%r8
+shl $5,%r8
+
+# qhasm: spc <<= 5
+# asm 1: shl $5,<spc=int64#6
+# asm 2: shl $5,<spc=%r9
+shl $5,%r9
+
+# qhasm: spc += sp
+# asm 1: add <sp=int64#3,<spc=int64#6
+# asm 2: add <sp=%rdx,<spc=%r9
+add %rdx,%r9
+
+# qhasm: spp += sp
+# asm 1: add <sp=int64#3,<spp=int64#5
+# asm 2: add <sp=%rdx,<spp=%r8
+add %rdx,%r8
+
+# qhasm: c0 = *(uint64 *)(spc + 0)
+# asm 1: movq 0(<spc=int64#6),>c0=int64#7
+# asm 2: movq 0(<spc=%r9),>c0=%rax
+movq 0(%r9),%rax
+
+# qhasm: c1 = *(uint64 *)(spc + 8)
+# asm 1: movq 8(<spc=int64#6),>c1=int64#8
+# asm 2: movq 8(<spc=%r9),>c1=%r10
+movq 8(%r9),%r10
+
+# qhasm: c2 = *(uint64 *)(spc + 16)
+# asm 1: movq 16(<spc=int64#6),>c2=int64#9
+# asm 2: movq 16(<spc=%r9),>c2=%r11
+movq 16(%r9),%r11
+
+# qhasm: carry? c0 -= *(uint64 *)(spp + 0)
+# asm 1: subq 0(<spp=int64#5),<c0=int64#7
+# asm 2: subq 0(<spp=%r8),<c0=%rax
+subq 0(%r8),%rax
+
+# qhasm: carry? c1 -= *(uint64 *)(spp + 8) - carry
+# asm 1: sbbq 8(<spp=int64#5),<c1=int64#8
+# asm 2: sbbq 8(<spp=%r8),<c1=%r10
+sbbq 8(%r8),%r10
+
+# qhasm: carry? c2 -= *(uint64 *)(spp + 16) - carry
+# asm 1: sbbq 16(<spp=int64#5),<c2=int64#9
+# asm 2: sbbq 16(<spp=%r8),<c2=%r11
+sbbq 16(%r8),%r11
+# comment:fp stack unchanged by jump
+
+# qhasm: goto end if carry
+jc ._end
+
+# qhasm: spc -= sp
+# asm 1: sub <sp=int64#3,<spc=int64#6
+# asm 2: sub <sp=%rdx,<spc=%r9
+sub %rdx,%r9
+
+# qhasm: (uint64) spc >>= 5
+# asm 1: shr $5,<spc=int64#6
+# asm 2: shr $5,<spc=%r9
+shr $5,%r9
+
+# qhasm: spp -= sp
+# asm 1: sub <sp=int64#3,<spp=int64#5
+# asm 2: sub <sp=%rdx,<spp=%r8
+sub %rdx,%r8
+
+# qhasm: (uint64) spp >>= 5
+# asm 1: shr $5,<spp=int64#5
+# asm 2: shr $5,<spp=%r8
+shr $5,%r8
+
+# qhasm: *(uint64 *)(hp + pp * 8) = spc
+# asm 1: movq <spc=int64#6,(<hp=int64#1,<pp=int64#4,8)
+# asm 2: movq <spc=%r9,(<hp=%rdi,<pp=%rcx,8)
+movq %r9,(%rdi,%rcx,8)
+
+# qhasm: *(uint64 *)(hp + pc * 8) = spp
+# asm 1: movq <spp=int64#5,(<hp=int64#1,<pc=int64#2,8)
+# asm 2: movq <spp=%r8,(<hp=%rdi,<pc=%rsi,8)
+movq %r8,(%rdi,%rsi,8)
+# comment:fp stack unchanged by jump
+
+# qhasm: goto siftuploop
+jmp ._siftuploop
+
+# qhasm: end:
+._end:
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/hram.c b/ext/ed25519-amd64-asm/hram.c
new file mode 100644
index 00000000..6f99fc62
--- /dev/null
+++ b/ext/ed25519-amd64-asm/hram.c
@@ -0,0 +1,16 @@
+/*#include "crypto_hash_sha512.h"*/
+#include "hram.h"
+
+extern void ZT_sha512internal(void *digest,const void *data,unsigned int len);
+
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
+{
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ /*crypto_hash_sha512(hram,playground,smlen);*/
+ ZT_sha512internal(hram,playground,smlen);
+}
diff --git a/ext/ed25519-amd64-asm/hram.h b/ext/ed25519-amd64-asm/hram.h
new file mode 100644
index 00000000..1740c78a
--- /dev/null
+++ b/ext/ed25519-amd64-asm/hram.h
@@ -0,0 +1,8 @@
+#ifndef HRAM_H
+#define HRAM_H
+
+#define get_hram crypto_sign_ed25519_amd64_64_get_hram
+
+extern void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/implementors b/ext/ed25519-amd64-asm/implementors
new file mode 100644
index 00000000..9b5399a3
--- /dev/null
+++ b/ext/ed25519-amd64-asm/implementors
@@ -0,0 +1,5 @@
+Daniel J. Bernstein
+Niels Duif
+Tanja Lange
+lead: Peter Schwabe
+Bo-Yin Yang
diff --git a/ext/ed25519-amd64-asm/index_heap.c b/ext/ed25519-amd64-asm/index_heap.c
new file mode 100644
index 00000000..f29f7a28
--- /dev/null
+++ b/ext/ed25519-amd64-asm/index_heap.c
@@ -0,0 +1,58 @@
+#include "sc25519.h"
+#include "index_heap.h"
+
+/* caller's responsibility to ensure hlen>=3 */
+void heap_init(unsigned long long *h, unsigned long long hlen, sc25519 *scalars)
+{
+ h[0] = 0;
+ unsigned long long i=1;
+ while(i<hlen)
+ heap_push(h, &i, i, scalars);
+}
+
+void heap_extend(unsigned long long *h, unsigned long long oldlen, unsigned long long newlen, sc25519 *scalars)
+{
+ unsigned long long i=oldlen;
+ while(i<newlen)
+ heap_push(h, &i, i, scalars);
+}
+
+
+void heap_push(unsigned long long *h, unsigned long long *hlen, unsigned long long elem, sc25519 *scalars)
+{
+ /* Move up towards the root */
+ /* XXX: Check size of hlen, whether cast to signed value is ok */
+ signed long long pos = *hlen;
+ signed long long ppos = (pos-1)/2;
+ unsigned long long t;
+ h[*hlen] = elem;
+ while(pos > 0)
+ {
+ /* if(sc25519_lt_vartime(&scalars[h[ppos]], &scalars[h[pos]])) */
+ if(sc25519_lt(&scalars[h[ppos]], &scalars[h[pos]]))
+ {
+ t = h[ppos];
+ h[ppos] = h[pos];
+ h[pos] = t;
+ pos = ppos;
+ ppos = (pos-1)/2;
+ }
+ else break;
+ }
+ (*hlen)++;
+}
+
+/* Put the largest value in the heap in max1, the second largest in max2 */
+void heap_get2max(unsigned long long *h, unsigned long long *max1, unsigned long long *max2, sc25519 *scalars)
+{
+ *max1 = h[0];
+ *max2 = h[1];
+ if(sc25519_lt(&scalars[h[1]],&scalars[h[2]]))
+ *max2 = h[2];
+}
+
+/* After the root has been replaced, restore heap property */
+/* extern void heap_rootreplaced(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+*/
+/* extern void heap_rootreplaced_shortscalars(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+*/
diff --git a/ext/ed25519-amd64-asm/index_heap.h b/ext/ed25519-amd64-asm/index_heap.h
new file mode 100644
index 00000000..7dee9161
--- /dev/null
+++ b/ext/ed25519-amd64-asm/index_heap.h
@@ -0,0 +1,31 @@
+#ifndef INDEX_HEAP_H
+#define INDEX_HEAP_H
+
+#include "sc25519.h"
+
+#define heap_init crypto_sign_ed25519_amd64_64_heap_init
+#define heap_extend crypto_sign_ed25519_amd64_64_heap_extend
+#define heap_pop crypto_sign_ed25519_amd64_64_heap_pop
+#define heap_push crypto_sign_ed25519_amd64_64_heap_push
+#define heap_get2max crypto_sign_ed25519_amd64_64_heap_get2max
+#define heap_rootreplaced crypto_sign_ed25519_amd64_64_heap_rootreplaced
+#define heap_rootreplaced_3limbs crypto_sign_ed25519_amd64_64_heap_rootreplaced_3limbs
+#define heap_rootreplaced_2limbs crypto_sign_ed25519_amd64_64_heap_rootreplaced_2limbs
+#define heap_rootreplaced_1limb crypto_sign_ed25519_amd64_64_heap_rootreplaced_1limb
+
+void heap_init(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+
+void heap_extend(unsigned long long *h, unsigned long long oldlen, unsigned long long newlen, sc25519 *scalars);
+
+unsigned long long heap_pop(unsigned long long *h, unsigned long long *hlen, sc25519 *scalars);
+
+void heap_push(unsigned long long *h, unsigned long long *hlen, unsigned long long elem, sc25519 *scalars);
+
+void heap_get2max(unsigned long long *h, unsigned long long *max1, unsigned long long *max2, sc25519 *scalars);
+
+void heap_rootreplaced(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+void heap_rootreplaced_3limbs(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+void heap_rootreplaced_2limbs(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+void heap_rootreplaced_1limb(unsigned long long *h, unsigned long long hlen, sc25519 *scalars);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/keypair.c b/ext/ed25519-amd64-asm/keypair.c
new file mode 100644
index 00000000..7e094710
--- /dev/null
+++ b/ext/ed25519-amd64-asm/keypair.c
@@ -0,0 +1,25 @@
+#include <string.h>
+#include "crypto_sign.h"
+#include "crypto_hash_sha512.h"
+#include "randombytes.h"
+#include "ge25519.h"
+
+int crypto_sign_keypair(unsigned char *pk,unsigned char *sk)
+{
+ unsigned char az[64];
+ sc25519 scsk;
+ ge25519 gepk;
+
+ randombytes(sk,32);
+ crypto_hash_sha512(az,sk,32);
+ az[0] &= 248;
+ az[31] &= 127;
+ az[31] |= 64;
+
+ sc25519_from32bytes(&scsk,az);
+
+ ge25519_scalarmult_base(&gepk, &scsk);
+ ge25519_pack(pk, &gepk);
+ memmove(sk + 32,pk,32);
+ return 0;
+}
diff --git a/ext/ed25519-amd64-asm/open.c b/ext/ed25519-amd64-asm/open.c
new file mode 100644
index 00000000..104d48dc
--- /dev/null
+++ b/ext/ed25519-amd64-asm/open.c
@@ -0,0 +1,49 @@
+#include <string.h>
+#include "crypto_sign.h"
+#include "crypto_verify_32.h"
+#include "crypto_hash_sha512.h"
+#include "ge25519.h"
+
+int crypto_sign_open(
+ unsigned char *m,unsigned long long *mlen,
+ const unsigned char *sm,unsigned long long smlen,
+ const unsigned char *pk
+ )
+{
+ unsigned char pkcopy[32];
+ unsigned char rcopy[32];
+ unsigned char hram[64];
+ unsigned char rcheck[32];
+ ge25519 get1, get2;
+ sc25519 schram, scs;
+
+ if (smlen < 64) goto badsig;
+ if (sm[63] & 224) goto badsig;
+ if (ge25519_unpackneg_vartime(&get1,pk)) goto badsig;
+
+ memmove(pkcopy,pk,32);
+ memmove(rcopy,sm,32);
+
+ sc25519_from32bytes(&scs, sm+32);
+
+ memmove(m,sm,smlen);
+ memmove(m + 32,pkcopy,32);
+ crypto_hash_sha512(hram,m,smlen);
+
+ sc25519_from64bytes(&schram, hram);
+
+ ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &scs);
+ ge25519_pack(rcheck, &get2);
+
+ if (crypto_verify_32(rcopy,rcheck) == 0) {
+ memmove(m,m + 64,smlen - 64);
+ memset(m + smlen - 64,0,64);
+ *mlen = smlen - 64;
+ return 0;
+ }
+
+badsig:
+ *mlen = (unsigned long long) -1;
+ memset(m,0,smlen);
+ return -1;
+}
diff --git a/ext/ed25519-amd64-asm/sc25519.h b/ext/ed25519-amd64-asm/sc25519.h
new file mode 100644
index 00000000..8ff1b1ca
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519.h
@@ -0,0 +1,66 @@
+#ifndef SC25519_H
+#define SC25519_H
+
+#define sc25519 crypto_sign_ed25519_amd64_64_sc25519
+#define shortsc25519 crypto_sign_ed25519_amd64_64_shortsc25519
+#define sc25519_from32bytes crypto_sign_ed25519_amd64_64_sc25519_from32bytes
+#define shortsc25519_from16bytes crypto_sign_ed25519_amd64_64_shortsc25519_from16bytes
+#define sc25519_from64bytes crypto_sign_ed25519_amd64_64_sc25519_from64bytes
+#define sc25519_from_shortsc crypto_sign_ed25519_amd64_64_sc25519_from_shortsc
+#define sc25519_to32bytes crypto_sign_ed25519_amd64_64_sc25519_to32bytes
+#define sc25519_iszero_vartime crypto_sign_ed25519_amd64_64_sc25519_iszero_vartime
+#define sc25519_isshort_vartime crypto_sign_ed25519_amd64_64_sc25519_isshort_vartime
+#define sc25519_lt crypto_sign_ed25519_amd64_64_sc25519_lt
+#define sc25519_add crypto_sign_ed25519_amd64_64_sc25519_add
+#define sc25519_sub_nored crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+#define sc25519_mul crypto_sign_ed25519_amd64_64_sc25519_mul
+#define sc25519_mul_shortsc crypto_sign_ed25519_amd64_64_sc25519_mul_shortsc
+#define sc25519_window4 crypto_sign_ed25519_amd64_64_sc25519_window4
+#define sc25519_slide crypto_sign_ed25519_amd64_64_sc25519_slide
+#define sc25519_2interleave2 crypto_sign_ed25519_amd64_64_sc25519_2interleave2
+#define sc25519_barrett crypto_sign_ed25519_amd64_64_sc25519_barrett
+
+typedef struct
+{
+ unsigned long long v[4];
+}
+sc25519;
+
+typedef struct
+{
+ unsigned long long v[2];
+}
+shortsc25519;
+
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32]);
+
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]);
+
+void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x);
+
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x);
+
+int sc25519_iszero_vartime(const sc25519 *x);
+
+int sc25519_lt(const sc25519 *x, const sc25519 *y);
+
+void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y);
+
+void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y);
+
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y);
+
+void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y);
+
+/* Convert s into a representation of the form \sum_{i=0}^{63}r[i]2^(4*i)
+ * with r[i] in {-8,...,7}
+ */
+void sc25519_window4(signed char r[85], const sc25519 *s);
+
+void sc25519_slide(signed char r[256], const sc25519 *s, int swindowsize);
+
+void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2);
+
+void sc25519_barrett(sc25519 *r, unsigned long long x[8]);
+
+#endif
diff --git a/ext/ed25519-amd64-asm/sc25519_add.s b/ext/ed25519-amd64-asm/sc25519_add.s
new file mode 100644
index 00000000..d5b941cd
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_add.s
@@ -0,0 +1,232 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_add
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_add
+.globl crypto_sign_ed25519_amd64_64_sc25519_add
+_crypto_sign_ed25519_amd64_64_sc25519_add:
+crypto_sign_ed25519_amd64_64_sc25519_add:
+mov %rsp,%r11
+and $31,%r11
+add $32,%r11
+sub %r11,%rsp
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#1
+# asm 2: movq <caller4=%r14,>caller4_stack=0(%rsp)
+movq %r14,0(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#2
+# asm 2: movq <caller5=%r15,>caller5_stack=8(%rsp)
+movq %r15,8(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#3
+# asm 2: movq <caller6=%rbx,>caller6_stack=16(%rsp)
+movq %rbx,16(%rsp)
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 += *(uint64 *)(yp + 0)
+# asm 1: addq 0(<yp=int64#3),<r0=int64#4
+# asm 2: addq 0(<yp=%rdx),<r0=%rcx
+addq 0(%rdx),%rcx
+
+# qhasm: carry? r1 += *(uint64 *)(yp + 8) + carry
+# asm 1: adcq 8(<yp=int64#3),<r1=int64#5
+# asm 2: adcq 8(<yp=%rdx),<r1=%r8
+adcq 8(%rdx),%r8
+
+# qhasm: carry? r2 += *(uint64 *)(yp + 16) + carry
+# asm 1: adcq 16(<yp=int64#3),<r2=int64#6
+# asm 2: adcq 16(<yp=%rdx),<r2=%r9
+adcq 16(%rdx),%r9
+
+# qhasm: r3 += *(uint64 *)(yp + 24) + carry
+# asm 1: adcq 24(<yp=int64#3),<r3=int64#2
+# asm 2: adcq 24(<yp=%rdx),<r3=%rsi
+adcq 24(%rdx),%rsi
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#4,>t0=int64#3
+# asm 2: mov <r0=%rcx,>t0=%rdx
+mov %rcx,%rdx
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#5,>t1=int64#7
+# asm 2: mov <r1=%r8,>t1=%rax
+mov %r8,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#6,>t2=int64#8
+# asm 2: mov <r2=%r9,>t2=%r10
+mov %r9,%r10
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#2,>t3=int64#12
+# asm 2: mov <r3=%rsi,>t3=%r14
+mov %rsi,%r14
+
+# qhasm: carry? t0 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+# asm 1: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=int64#3
+# asm 2: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=%rdx
+sub crypto_sign_ed25519_amd64_64_ORDER0(%rip),%rdx
+
+# qhasm: carry? t1 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=int64#7
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=%rax
+sbb crypto_sign_ed25519_amd64_64_ORDER1(%rip),%rax
+
+# qhasm: carry? t2 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=int64#8
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=%r10
+sbb crypto_sign_ed25519_amd64_64_ORDER2(%rip),%r10
+
+# qhasm: unsigned<? t3 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=int64#12
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=%r14
+sbb crypto_sign_ed25519_amd64_64_ORDER3(%rip),%r14
+
+# qhasm: r0 = t0 if !unsigned<
+# asm 1: cmovae <t0=int64#3,<r0=int64#4
+# asm 2: cmovae <t0=%rdx,<r0=%rcx
+cmovae %rdx,%rcx
+
+# qhasm: r1 = t1 if !unsigned<
+# asm 1: cmovae <t1=int64#7,<r1=int64#5
+# asm 2: cmovae <t1=%rax,<r1=%r8
+cmovae %rax,%r8
+
+# qhasm: r2 = t2 if !unsigned<
+# asm 1: cmovae <t2=int64#8,<r2=int64#6
+# asm 2: cmovae <t2=%r10,<r2=%r9
+cmovae %r10,%r9
+
+# qhasm: r3 = t3 if !unsigned<
+# asm 1: cmovae <t3=int64#12,<r3=int64#2
+# asm 2: cmovae <t3=%r14,<r3=%rsi
+cmovae %r14,%rsi
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#1,>caller4=int64#12
+# asm 2: movq <caller4_stack=0(%rsp),>caller4=%r14
+movq 0(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#2,>caller5=int64#13
+# asm 2: movq <caller5_stack=8(%rsp),>caller5=%r15
+movq 8(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#3,>caller6=int64#14
+# asm 2: movq <caller6_stack=16(%rsp),>caller6=%rbx
+movq 16(%rsp),%rbx
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_barrett.s b/ext/ed25519-amd64-asm/sc25519_barrett.s
new file mode 100644
index 00000000..7eb56fad
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_barrett.s
@@ -0,0 +1,1188 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: int64 q23
+
+# qhasm: int64 q24
+
+# qhasm: int64 q30
+
+# qhasm: int64 q31
+
+# qhasm: int64 q32
+
+# qhasm: int64 q33
+
+# qhasm: int64 r20
+
+# qhasm: int64 r21
+
+# qhasm: int64 r22
+
+# qhasm: int64 r23
+
+# qhasm: int64 r24
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 rax
+
+# qhasm: int64 rdx
+
+# qhasm: int64 c
+
+# qhasm: int64 zero
+
+# qhasm: int64 mask
+
+# qhasm: int64 nmask
+
+# qhasm: stack64 q30_stack
+
+# qhasm: stack64 q31_stack
+
+# qhasm: stack64 q32_stack
+
+# qhasm: stack64 q33_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_barrett
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_barrett
+.globl crypto_sign_ed25519_amd64_64_sc25519_barrett
+_crypto_sign_ed25519_amd64_64_sc25519_barrett:
+crypto_sign_ed25519_amd64_64_sc25519_barrett:
+mov %rsp,%r11
+and $31,%r11
+add $96,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: zero ^= zero
+# asm 1: xor <zero=int64#4,<zero=int64#4
+# asm 2: xor <zero=%rcx,<zero=%rcx
+xor %rcx,%rcx
+
+# qhasm: q30 ^= q30
+# asm 1: xor <q30=int64#5,<q30=int64#5
+# asm 2: xor <q30=%r8,<q30=%r8
+xor %r8,%r8
+
+# qhasm: q31 ^= q31
+# asm 1: xor <q31=int64#6,<q31=int64#6
+# asm 2: xor <q31=%r9,<q31=%r9
+xor %r9,%r9
+
+# qhasm: q32 ^= q32
+# asm 1: xor <q32=int64#8,<q32=int64#8
+# asm 2: xor <q32=%r10,<q32=%r10
+xor %r10,%r10
+
+# qhasm: q33 ^= q33
+# asm 1: xor <q33=int64#9,<q33=int64#9
+# asm 2: xor <q33=%r11,<q33=%r11
+xor %r11,%r11
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3(%rip)
+
+# qhasm: q23 = rax
+# asm 1: mov <rax=int64#7,>q23=int64#10
+# asm 2: mov <rax=%rax,>q23=%r12
+mov %rax,%r12
+
+# qhasm: c = rdx
+# asm 1: mov <rdx=int64#3,>c=int64#11
+# asm 2: mov <rdx=%rdx,>c=%r13
+mov %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4(%rip)
+
+# qhasm: q24 = rax
+# asm 1: mov <rax=int64#7,>q24=int64#12
+# asm 2: mov <rax=%rax,>q24=%r14
+mov %rax,%r14
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#11,<q24=int64#12
+# asm 2: add <c=%r13,<q24=%r14
+add %r13,%r14
+
+# qhasm: q30 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q30=int64#5
+# asm 2: adc <rdx=%rdx,<q30=%r8
+adc %rdx,%r8
+
+# qhasm: rax = *(uint64 *)(xp + 32)
+# asm 1: movq 32(<xp=int64#2),>rax=int64#7
+# asm 2: movq 32(<xp=%rsi),>rax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2(%rip)
+
+# qhasm: carry? q23 += rax
+# asm 1: add <rax=int64#7,<q23=int64#10
+# asm 2: add <rax=%rax,<q23=%r12
+add %rax,%r12
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 32)
+# asm 1: movq 32(<xp=int64#2),>rax=int64#7
+# asm 2: movq 32(<xp=%rsi),>rax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3(%rip)
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#11,<q24=int64#12
+# asm 2: add <c=%r13,<q24=%r14
+add %r13,%r14
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 32)
+# asm 1: movq 32(<xp=int64#2),>rax=int64#7
+# asm 2: movq 32(<xp=%rsi),>rax=%rax
+movq 32(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4(%rip)
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#11,<q30=int64#5
+# asm 2: add <c=%r13,<q30=%r8
+add %r13,%r8
+
+# qhasm: q31 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q31=int64#6
+# asm 2: adc <rdx=%rdx,<q31=%r9
+adc %rdx,%r9
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU1
+mulq crypto_sign_ed25519_amd64_64_MU1(%rip)
+
+# qhasm: carry? q23 += rax
+# asm 1: add <rax=int64#7,<q23=int64#10
+# asm 2: add <rax=%rax,<q23=%r12
+add %rax,%r12
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2(%rip)
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#11,<q24=int64#12
+# asm 2: add <c=%r13,<q24=%r14
+add %r13,%r14
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3(%rip)
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#11,<q30=int64#5
+# asm 2: add <c=%r13,<q30=%r8
+add %r13,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#11
+# asm 2: mov $0,>c=%r13
+mov $0,%r13
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#11
+# asm 2: adc <rdx=%rdx,<c=%r13
+adc %rdx,%r13
+
+# qhasm: rax = *(uint64 *)(xp + 40)
+# asm 1: movq 40(<xp=int64#2),>rax=int64#7
+# asm 2: movq 40(<xp=%rsi),>rax=%rax
+movq 40(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4(%rip)
+
+# qhasm: carry? q31 += rax
+# asm 1: add <rax=int64#7,<q31=int64#6
+# asm 2: add <rax=%rax,<q31=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q31 += c
+# asm 1: add <c=int64#11,<q31=int64#6
+# asm 2: add <c=%r13,<q31=%r9
+add %r13,%r9
+
+# qhasm: q32 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q32=int64#8
+# asm 2: adc <rdx=%rdx,<q32=%r10
+adc %rdx,%r10
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU0
+mulq crypto_sign_ed25519_amd64_64_MU0(%rip)
+
+# qhasm: carry? q23 += rax
+# asm 1: add <rax=int64#7,<q23=int64#10
+# asm 2: add <rax=%rax,<q23=%r12
+add %rax,%r12
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU1
+mulq crypto_sign_ed25519_amd64_64_MU1(%rip)
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q24 += c
+# asm 1: add <c=int64#10,<q24=int64#12
+# asm 2: add <c=%r12,<q24=%r14
+add %r12,%r14
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2(%rip)
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#10,<q30=int64#5
+# asm 2: add <c=%r12,<q30=%r8
+add %r12,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3(%rip)
+
+# qhasm: carry? q31 += rax
+# asm 1: add <rax=int64#7,<q31=int64#6
+# asm 2: add <rax=%rax,<q31=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q31 += c
+# asm 1: add <c=int64#10,<q31=int64#6
+# asm 2: add <c=%r12,<q31=%r9
+add %r12,%r9
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 48)
+# asm 1: movq 48(<xp=int64#2),>rax=int64#7
+# asm 2: movq 48(<xp=%rsi),>rax=%rax
+movq 48(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4(%rip)
+
+# qhasm: carry? q32 += rax
+# asm 1: add <rax=int64#7,<q32=int64#8
+# asm 2: add <rax=%rax,<q32=%r10
+add %rax,%r10
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q32 += c
+# asm 1: add <c=int64#10,<q32=int64#8
+# asm 2: add <c=%r12,<q32=%r10
+add %r12,%r10
+
+# qhasm: q33 += rdx + carry
+# asm 1: adc <rdx=int64#3,<q33=int64#9
+# asm 2: adc <rdx=%rdx,<q33=%r11
+adc %rdx,%r11
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU0
+mulq crypto_sign_ed25519_amd64_64_MU0(%rip)
+
+# qhasm: carry? q24 += rax
+# asm 1: add <rax=int64#7,<q24=int64#12
+# asm 2: add <rax=%rax,<q24=%r14
+add %rax,%r14
+
+# qhasm: free q24
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU1
+mulq crypto_sign_ed25519_amd64_64_MU1(%rip)
+
+# qhasm: carry? q30 += rax
+# asm 1: add <rax=int64#7,<q30=int64#5
+# asm 2: add <rax=%rax,<q30=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q30 += c
+# asm 1: add <c=int64#10,<q30=int64#5
+# asm 2: add <c=%r12,<q30=%r8
+add %r12,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#10
+# asm 2: mov $0,>c=%r12
+mov $0,%r12
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#10
+# asm 2: adc <rdx=%rdx,<c=%r12
+adc %rdx,%r12
+
+# qhasm: q30_stack = q30
+# asm 1: movq <q30=int64#5,>q30_stack=stack64#8
+# asm 2: movq <q30=%r8,>q30_stack=56(%rsp)
+movq %r8,56(%rsp)
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU2
+mulq crypto_sign_ed25519_amd64_64_MU2(%rip)
+
+# qhasm: carry? q31 += rax
+# asm 1: add <rax=int64#7,<q31=int64#6
+# asm 2: add <rax=%rax,<q31=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q31 += c
+# asm 1: add <c=int64#10,<q31=int64#6
+# asm 2: add <c=%r12,<q31=%r9
+add %r12,%r9
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#5
+# asm 2: mov $0,>c=%r8
+mov $0,%r8
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#5
+# asm 2: adc <rdx=%rdx,<c=%r8
+adc %rdx,%r8
+
+# qhasm: q31_stack = q31
+# asm 1: movq <q31=int64#6,>q31_stack=stack64#9
+# asm 2: movq <q31=%r9,>q31_stack=64(%rsp)
+movq %r9,64(%rsp)
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU3
+mulq crypto_sign_ed25519_amd64_64_MU3(%rip)
+
+# qhasm: carry? q32 += rax
+# asm 1: add <rax=int64#7,<q32=int64#8
+# asm 2: add <rax=%rax,<q32=%r10
+add %rax,%r10
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? q32 += c
+# asm 1: add <c=int64#5,<q32=int64#8
+# asm 2: add <c=%r8,<q32=%r10
+add %r8,%r10
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#5
+# asm 2: mov $0,>c=%r8
+mov $0,%r8
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#5
+# asm 2: adc <rdx=%rdx,<c=%r8
+adc %rdx,%r8
+
+# qhasm: q32_stack = q32
+# asm 1: movq <q32=int64#8,>q32_stack=stack64#10
+# asm 2: movq <q32=%r10,>q32_stack=72(%rsp)
+movq %r10,72(%rsp)
+
+# qhasm: rax = *(uint64 *)(xp + 56)
+# asm 1: movq 56(<xp=int64#2),>rax=int64#7
+# asm 2: movq 56(<xp=%rsi),>rax=%rax
+movq 56(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_MU4
+mulq crypto_sign_ed25519_amd64_64_MU4(%rip)
+
+# qhasm: carry? q33 += rax
+# asm 1: add <rax=int64#7,<q33=int64#9
+# asm 2: add <rax=%rax,<q33=%r11
+add %rax,%r11
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: q33 += c
+# asm 1: add <c=int64#5,<q33=int64#9
+# asm 2: add <c=%r8,<q33=%r11
+add %r8,%r11
+
+# qhasm: q33_stack = q33
+# asm 1: movq <q33=int64#9,>q33_stack=stack64#11
+# asm 2: movq <q33=%r11,>q33_stack=80(%rsp)
+movq %r11,80(%rsp)
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0(%rip)
+
+# qhasm: r20 = rax
+# asm 1: mov <rax=int64#7,>r20=int64#5
+# asm 2: mov <rax=%rax,>r20=%r8
+mov %rax,%r8
+
+# qhasm: c = rdx
+# asm 1: mov <rdx=int64#3,>c=int64#6
+# asm 2: mov <rdx=%rdx,>c=%r9
+mov %rdx,%r9
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1
+mulq crypto_sign_ed25519_amd64_64_ORDER1(%rip)
+
+# qhasm: r21 = rax
+# asm 1: mov <rax=int64#7,>r21=int64#8
+# asm 2: mov <rax=%rax,>r21=%r10
+mov %rax,%r10
+
+# qhasm: carry? r21 += c
+# asm 1: add <c=int64#6,<r21=int64#8
+# asm 2: add <c=%r9,<r21=%r10
+add %r9,%r10
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#6
+# asm 2: mov $0,>c=%r9
+mov $0,%r9
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#6
+# asm 2: adc <rdx=%rdx,<c=%r9
+adc %rdx,%r9
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2
+mulq crypto_sign_ed25519_amd64_64_ORDER2(%rip)
+
+# qhasm: r22 = rax
+# asm 1: mov <rax=int64#7,>r22=int64#9
+# asm 2: mov <rax=%rax,>r22=%r11
+mov %rax,%r11
+
+# qhasm: carry? r22 += c
+# asm 1: add <c=int64#6,<r22=int64#9
+# asm 2: add <c=%r9,<r22=%r11
+add %r9,%r11
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#6
+# asm 2: mov $0,>c=%r9
+mov $0,%r9
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#6
+# asm 2: adc <rdx=%rdx,<c=%r9
+adc %rdx,%r9
+
+# qhasm: rax = q30_stack
+# asm 1: movq <q30_stack=stack64#8,>rax=int64#7
+# asm 2: movq <q30_stack=56(%rsp),>rax=%rax
+movq 56(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3
+mulq crypto_sign_ed25519_amd64_64_ORDER3(%rip)
+
+# qhasm: free rdx
+
+# qhasm: r23 = rax
+# asm 1: mov <rax=int64#7,>r23=int64#10
+# asm 2: mov <rax=%rax,>r23=%r12
+mov %rax,%r12
+
+# qhasm: r23 += c
+# asm 1: add <c=int64#6,<r23=int64#10
+# asm 2: add <c=%r9,<r23=%r12
+add %r9,%r12
+
+# qhasm: rax = q31_stack
+# asm 1: movq <q31_stack=stack64#9,>rax=int64#7
+# asm 2: movq <q31_stack=64(%rsp),>rax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0(%rip)
+
+# qhasm: carry? r21 += rax
+# asm 1: add <rax=int64#7,<r21=int64#8
+# asm 2: add <rax=%rax,<r21=%r10
+add %rax,%r10
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#6
+# asm 2: mov $0,>c=%r9
+mov $0,%r9
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#6
+# asm 2: adc <rdx=%rdx,<c=%r9
+adc %rdx,%r9
+
+# qhasm: rax = q31_stack
+# asm 1: movq <q31_stack=stack64#9,>rax=int64#7
+# asm 2: movq <q31_stack=64(%rsp),>rax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1
+mulq crypto_sign_ed25519_amd64_64_ORDER1(%rip)
+
+# qhasm: carry? r22 += rax
+# asm 1: add <rax=int64#7,<r22=int64#9
+# asm 2: add <rax=%rax,<r22=%r11
+add %rax,%r11
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#4,<rdx=int64#3
+# asm 2: adc <zero=%rcx,<rdx=%rdx
+adc %rcx,%rdx
+
+# qhasm: carry? r22 += c
+# asm 1: add <c=int64#6,<r22=int64#9
+# asm 2: add <c=%r9,<r22=%r11
+add %r9,%r11
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#4
+# asm 2: mov $0,>c=%rcx
+mov $0,%rcx
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#4
+# asm 2: adc <rdx=%rdx,<c=%rcx
+adc %rdx,%rcx
+
+# qhasm: rax = q31_stack
+# asm 1: movq <q31_stack=stack64#9,>rax=int64#7
+# asm 2: movq <q31_stack=64(%rsp),>rax=%rax
+movq 64(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2
+mulq crypto_sign_ed25519_amd64_64_ORDER2(%rip)
+
+# qhasm: free rdx
+
+# qhasm: r23 += rax
+# asm 1: add <rax=int64#7,<r23=int64#10
+# asm 2: add <rax=%rax,<r23=%r12
+add %rax,%r12
+
+# qhasm: r23 += c
+# asm 1: add <c=int64#4,<r23=int64#10
+# asm 2: add <c=%rcx,<r23=%r12
+add %rcx,%r12
+
+# qhasm: rax = q32_stack
+# asm 1: movq <q32_stack=stack64#10,>rax=int64#7
+# asm 2: movq <q32_stack=72(%rsp),>rax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0(%rip)
+
+# qhasm: carry? r22 += rax
+# asm 1: add <rax=int64#7,<r22=int64#9
+# asm 2: add <rax=%rax,<r22=%r11
+add %rax,%r11
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#4
+# asm 2: mov $0,>c=%rcx
+mov $0,%rcx
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#4
+# asm 2: adc <rdx=%rdx,<c=%rcx
+adc %rdx,%rcx
+
+# qhasm: rax = q32_stack
+# asm 1: movq <q32_stack=stack64#10,>rax=int64#7
+# asm 2: movq <q32_stack=72(%rsp),>rax=%rax
+movq 72(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1
+mulq crypto_sign_ed25519_amd64_64_ORDER1(%rip)
+
+# qhasm: free rdx
+
+# qhasm: r23 += rax
+# asm 1: add <rax=int64#7,<r23=int64#10
+# asm 2: add <rax=%rax,<r23=%r12
+add %rax,%r12
+
+# qhasm: r23 += c
+# asm 1: add <c=int64#4,<r23=int64#10
+# asm 2: add <c=%rcx,<r23=%r12
+add %rcx,%r12
+
+# qhasm: rax = q33_stack
+# asm 1: movq <q33_stack=stack64#11,>rax=int64#7
+# asm 2: movq <q33_stack=80(%rsp),>rax=%rax
+movq 80(%rsp),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+mulq crypto_sign_ed25519_amd64_64_ORDER0(%rip)
+
+# qhasm: free rdx
+
+# qhasm: r23 += rax
+# asm 1: add <rax=int64#7,<r23=int64#10
+# asm 2: add <rax=%rax,<r23=%r12
+add %rax,%r12
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#3
+# asm 2: movq 0(<xp=%rsi),>r0=%rdx
+movq 0(%rsi),%rdx
+
+# qhasm: carry? r0 -= r20
+# asm 1: sub <r20=int64#5,<r0=int64#3
+# asm 2: sub <r20=%r8,<r0=%rdx
+sub %r8,%rdx
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#3,>t0=int64#4
+# asm 2: mov <r0=%rdx,>t0=%rcx
+mov %rdx,%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: carry? r1 -= r21 - carry
+# asm 1: sbb <r21=int64#8,<r1=int64#5
+# asm 2: sbb <r21=%r10,<r1=%r8
+sbb %r10,%r8
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#5,>t1=int64#6
+# asm 2: mov <r1=%r8,>t1=%r9
+mov %r8,%r9
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#7
+# asm 2: movq 16(<xp=%rsi),>r2=%rax
+movq 16(%rsi),%rax
+
+# qhasm: carry? r2 -= r22 - carry
+# asm 1: sbb <r22=int64#9,<r2=int64#7
+# asm 2: sbb <r22=%r11,<r2=%rax
+sbb %r11,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#7,>t2=int64#8
+# asm 2: mov <r2=%rax,>t2=%r10
+mov %rax,%r10
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: r3 -= r23 - carry
+# asm 1: sbb <r23=int64#10,<r3=int64#2
+# asm 2: sbb <r23=%r12,<r3=%rsi
+sbb %r12,%rsi
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#2,>t3=int64#9
+# asm 2: mov <r3=%rsi,>t3=%r11
+mov %rsi,%r11
+
+# qhasm: carry? t0 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+# asm 1: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=int64#4
+# asm 2: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=%rcx
+sub crypto_sign_ed25519_amd64_64_ORDER0(%rip),%rcx
+
+# qhasm: carry? t1 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=int64#6
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=%r9
+sbb crypto_sign_ed25519_amd64_64_ORDER1(%rip),%r9
+
+# qhasm: carry? t2 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=int64#8
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=%r10
+sbb crypto_sign_ed25519_amd64_64_ORDER2(%rip),%r10
+
+# qhasm: unsigned<? t3 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=int64#9
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=%r11
+sbb crypto_sign_ed25519_amd64_64_ORDER3(%rip),%r11
+
+# qhasm: r0 = t0 if !unsigned<
+# asm 1: cmovae <t0=int64#4,<r0=int64#3
+# asm 2: cmovae <t0=%rcx,<r0=%rdx
+cmovae %rcx,%rdx
+
+# qhasm: t0 = r0
+# asm 1: mov <r0=int64#3,>t0=int64#4
+# asm 2: mov <r0=%rdx,>t0=%rcx
+mov %rdx,%rcx
+
+# qhasm: r1 = t1 if !unsigned<
+# asm 1: cmovae <t1=int64#6,<r1=int64#5
+# asm 2: cmovae <t1=%r9,<r1=%r8
+cmovae %r9,%r8
+
+# qhasm: t1 = r1
+# asm 1: mov <r1=int64#5,>t1=int64#6
+# asm 2: mov <r1=%r8,>t1=%r9
+mov %r8,%r9
+
+# qhasm: r2 = t2 if !unsigned<
+# asm 1: cmovae <t2=int64#8,<r2=int64#7
+# asm 2: cmovae <t2=%r10,<r2=%rax
+cmovae %r10,%rax
+
+# qhasm: t2 = r2
+# asm 1: mov <r2=int64#7,>t2=int64#8
+# asm 2: mov <r2=%rax,>t2=%r10
+mov %rax,%r10
+
+# qhasm: r3 = t3 if !unsigned<
+# asm 1: cmovae <t3=int64#9,<r3=int64#2
+# asm 2: cmovae <t3=%r11,<r3=%rsi
+cmovae %r11,%rsi
+
+# qhasm: t3 = r3
+# asm 1: mov <r3=int64#2,>t3=int64#9
+# asm 2: mov <r3=%rsi,>t3=%r11
+mov %rsi,%r11
+
+# qhasm: carry? t0 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER0
+# asm 1: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=int64#4
+# asm 2: sub crypto_sign_ed25519_amd64_64_ORDER0,<t0=%rcx
+sub crypto_sign_ed25519_amd64_64_ORDER0(%rip),%rcx
+
+# qhasm: carry? t1 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER1 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=int64#6
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER1,<t1=%r9
+sbb crypto_sign_ed25519_amd64_64_ORDER1(%rip),%r9
+
+# qhasm: carry? t2 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER2 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=int64#8
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER2,<t2=%r10
+sbb crypto_sign_ed25519_amd64_64_ORDER2(%rip),%r10
+
+# qhasm: unsigned<? t3 -= *(uint64 *) &crypto_sign_ed25519_amd64_64_ORDER3 - carry
+# asm 1: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=int64#9
+# asm 2: sbb crypto_sign_ed25519_amd64_64_ORDER3,<t3=%r11
+sbb crypto_sign_ed25519_amd64_64_ORDER3(%rip),%r11
+
+# qhasm: r0 = t0 if !unsigned<
+# asm 1: cmovae <t0=int64#4,<r0=int64#3
+# asm 2: cmovae <t0=%rcx,<r0=%rdx
+cmovae %rcx,%rdx
+
+# qhasm: r1 = t1 if !unsigned<
+# asm 1: cmovae <t1=int64#6,<r1=int64#5
+# asm 2: cmovae <t1=%r9,<r1=%r8
+cmovae %r9,%r8
+
+# qhasm: r2 = t2 if !unsigned<
+# asm 1: cmovae <t2=int64#8,<r2=int64#7
+# asm 2: cmovae <t2=%r10,<r2=%rax
+cmovae %r10,%rax
+
+# qhasm: r3 = t3 if !unsigned<
+# asm 1: cmovae <t3=int64#9,<r3=int64#2
+# asm 2: cmovae <t3=%r11,<r3=%rsi
+cmovae %r11,%rsi
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#3,0(<rp=int64#1)
+# asm 2: movq <r0=%rdx,0(<rp=%rdi)
+movq %rdx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#7,16(<rp=int64#1)
+# asm 2: movq <r2=%rax,16(<rp=%rdi)
+movq %rax,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_from32bytes.c b/ext/ed25519-amd64-asm/sc25519_from32bytes.c
new file mode 100644
index 00000000..7f21e686
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_from32bytes.c
@@ -0,0 +1,55 @@
+#include "sc25519.h"
+
+/*Arithmetic modulo the group order n = 2^252 + 27742317777372353535851937790883648493
+ * = 7237005577332262213973186563042994240857116359379907606001950938285454250989
+ */
+
+/* Contains order, 2*order, 4*order, 8*order, each represented in 4 consecutive unsigned long long */
+static const unsigned long long order[16] = {0x5812631A5CF5D3EDULL, 0x14DEF9DEA2F79CD6ULL,
+ 0x0000000000000000ULL, 0x1000000000000000ULL,
+ 0xB024C634B9EBA7DAULL, 0x29BDF3BD45EF39ACULL,
+ 0x0000000000000000ULL, 0x2000000000000000ULL,
+ 0x60498C6973D74FB4ULL, 0x537BE77A8BDE7359ULL,
+ 0x0000000000000000ULL, 0x4000000000000000ULL,
+ 0xC09318D2E7AE9F68ULL, 0xA6F7CEF517BCE6B2ULL,
+ 0x0000000000000000ULL, 0x8000000000000000ULL};
+
+static unsigned long long smaller(unsigned long long a,unsigned long long b)
+{
+ unsigned long long atop = a >> 32;
+ unsigned long long abot = a & 4294967295;
+ unsigned long long btop = b >> 32;
+ unsigned long long bbot = b & 4294967295;
+ unsigned long long atopbelowbtop = (atop - btop) >> 63;
+ unsigned long long atopeqbtop = ((atop ^ btop) - 1) >> 63;
+ unsigned long long abotbelowbbot = (abot - bbot) >> 63;
+ return atopbelowbtop | (atopeqbtop & abotbelowbbot);
+}
+
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
+{
+ unsigned long long t[4];
+ unsigned long long b;
+ unsigned long long mask;
+ int i, j;
+
+ /* assuming little-endian */
+ r->v[0] = *(unsigned long long *)x;
+ r->v[1] = *(((unsigned long long *)x)+1);
+ r->v[2] = *(((unsigned long long *)x)+2);
+ r->v[3] = *(((unsigned long long *)x)+3);
+
+ for(j=3;j>=0;j--)
+ {
+ b=0;
+ for(i=0;i<4;i++)
+ {
+ b += order[4*j+i]; /* no overflow for this particular order */
+ t[i] = r->v[i] - b;
+ b = smaller(r->v[i],b);
+ }
+ mask = b - 1;
+ for(i=0;i<4;i++)
+ r->v[i] ^= mask & (r->v[i] ^ t[i]);
+ }
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_from64bytes.c b/ext/ed25519-amd64-asm/sc25519_from64bytes.c
new file mode 100644
index 00000000..8e76a1b3
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_from64bytes.c
@@ -0,0 +1,7 @@
+#include "sc25519.h"
+
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
+{
+ /* assuming little-endian representation of unsigned long long */
+ sc25519_barrett(r, (unsigned long long *)x);
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_from_shortsc.c b/ext/ed25519-amd64-asm/sc25519_from_shortsc.c
new file mode 100644
index 00000000..3b8ff2fb
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_from_shortsc.c
@@ -0,0 +1,9 @@
+#include "sc25519.h"
+
+void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x)
+{
+ r->v[0] = x->v[0];
+ r->v[1] = x->v[1];
+ r->v[2] = 0;
+ r->v[3] = 0;
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_iszero.c b/ext/ed25519-amd64-asm/sc25519_iszero.c
new file mode 100644
index 00000000..21f593d7
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_iszero.c
@@ -0,0 +1,10 @@
+#include "sc25519.h"
+
+int sc25519_iszero_vartime(const sc25519 *x)
+{
+ if(x->v[0] != 0) return 0;
+ if(x->v[1] != 0) return 0;
+ if(x->v[2] != 0) return 0;
+ if(x->v[3] != 0) return 0;
+ return 1;
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_lt.s b/ext/ed25519-amd64-asm/sc25519_lt.s
new file mode 100644
index 00000000..3ba43178
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_lt.s
@@ -0,0 +1,131 @@
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: int64 ret
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: output ret
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 doof
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_lt
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_lt
+.globl crypto_sign_ed25519_amd64_64_sc25519_lt
+_crypto_sign_ed25519_amd64_64_sc25519_lt:
+crypto_sign_ed25519_amd64_64_sc25519_lt:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: t0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#1),>t0=int64#3
+# asm 2: movq 0(<xp=%rdi),>t0=%rdx
+movq 0(%rdi),%rdx
+
+# qhasm: t1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#1),>t1=int64#4
+# asm 2: movq 8(<xp=%rdi),>t1=%rcx
+movq 8(%rdi),%rcx
+
+# qhasm: t2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#1),>t2=int64#5
+# asm 2: movq 16(<xp=%rdi),>t2=%r8
+movq 16(%rdi),%r8
+
+# qhasm: t3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#1),>t3=int64#1
+# asm 2: movq 24(<xp=%rdi),>t3=%rdi
+movq 24(%rdi),%rdi
+
+# qhasm: carry? t0 -= *(uint64 *)(yp + 0)
+# asm 1: subq 0(<yp=int64#2),<t0=int64#3
+# asm 2: subq 0(<yp=%rsi),<t0=%rdx
+subq 0(%rsi),%rdx
+
+# qhasm: carry? t1 -= *(uint64 *)(yp + 8) - carry
+# asm 1: sbbq 8(<yp=int64#2),<t1=int64#4
+# asm 2: sbbq 8(<yp=%rsi),<t1=%rcx
+sbbq 8(%rsi),%rcx
+
+# qhasm: carry? t2 -= *(uint64 *)(yp + 16) - carry
+# asm 1: sbbq 16(<yp=int64#2),<t2=int64#5
+# asm 2: sbbq 16(<yp=%rsi),<t2=%r8
+sbbq 16(%rsi),%r8
+
+# qhasm: carry? t3 -= *(uint64 *)(yp + 24) - carry
+# asm 1: sbbq 24(<yp=int64#2),<t3=int64#1
+# asm 2: sbbq 24(<yp=%rsi),<t3=%rdi
+sbbq 24(%rsi),%rdi
+
+# qhasm: ret = 0
+# asm 1: mov $0,>ret=int64#1
+# asm 2: mov $0,>ret=%rdi
+mov $0,%rdi
+
+# qhasm: doof = 1
+# asm 1: mov $1,>doof=int64#2
+# asm 2: mov $1,>doof=%rsi
+mov $1,%rsi
+
+# qhasm: ret = doof if carry
+# asm 1: cmovc <doof=int64#2,<ret=int64#1
+# asm 2: cmovc <doof=%rsi,<ret=%rdi
+cmovc %rsi,%rdi
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_mul.c b/ext/ed25519-amd64-asm/sc25519_mul.c
new file mode 100644
index 00000000..ca4d5baa
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_mul.c
@@ -0,0 +1,12 @@
+#include "sc25519.h"
+
+#define ull4_mul crypto_sign_ed25519_amd64_64_ull4_mul
+
+extern void ull4_mul(unsigned long long r[8], const unsigned long long x[4], const unsigned long long y[4]);
+
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
+{
+ unsigned long long t[8];
+ ull4_mul(t, x->v, y->v);
+ sc25519_barrett(r, t);
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_mul_shortsc.c b/ext/ed25519-amd64-asm/sc25519_mul_shortsc.c
new file mode 100644
index 00000000..0c67250d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_mul_shortsc.c
@@ -0,0 +1,9 @@
+#include "sc25519.h"
+
+void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y)
+{
+ /* XXX: This wants to be faster */
+ sc25519 t;
+ sc25519_from_shortsc(&t, y);
+ sc25519_mul(r, x, &t);
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_slide.c b/ext/ed25519-amd64-asm/sc25519_slide.c
new file mode 100644
index 00000000..4e52010d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_slide.c
@@ -0,0 +1,49 @@
+#include "sc25519.h"
+
+void sc25519_slide(signed char r[256], const sc25519 *s, int swindowsize)
+{
+ int i,j,k,b,m=(1<<(swindowsize-1))-1, soplen=256;
+ unsigned long long sv0 = s->v[0];
+ unsigned long long sv1 = s->v[1];
+ unsigned long long sv2 = s->v[2];
+ unsigned long long sv3 = s->v[3];
+
+ /* first put the binary expansion into r */
+ for(i=0;i<64;i++) {
+ r[i] = sv0 & 1;
+ r[i+64] = sv1 & 1;
+ r[i+128] = sv2 & 1;
+ r[i+192] = sv3 & 1;
+ sv0 >>= 1;
+ sv1 >>= 1;
+ sv2 >>= 1;
+ sv3 >>= 1;
+ }
+
+ /* Making it sliding window */
+ for (j = 0;j < soplen;++j)
+ {
+ if (r[j]) {
+ for (b = 1;b < soplen - j && b <= 6;++b) {
+ if (r[j] + (r[j + b] << b) <= m)
+ {
+ r[j] += r[j + b] << b; r[j + b] = 0;
+ }
+ else if (r[j] - (r[j + b] << b) >= -m)
+ {
+ r[j] -= r[j + b] << b;
+ for (k = j + b;k < soplen;++k)
+ {
+ if (!r[k]) {
+ r[k] = 1;
+ break;
+ }
+ r[k] = 0;
+ }
+ }
+ else if (r[j + b])
+ break;
+ }
+ }
+ }
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_sub_nored.s b/ext/ed25519-amd64-asm/sc25519_sub_nored.s
new file mode 100644
index 00000000..a347e7d4
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_sub_nored.s
@@ -0,0 +1,142 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 t0
+
+# qhasm: int64 t1
+
+# qhasm: int64 t2
+
+# qhasm: int64 t3
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+.globl crypto_sign_ed25519_amd64_64_sc25519_sub_nored
+_crypto_sign_ed25519_amd64_64_sc25519_sub_nored:
+crypto_sign_ed25519_amd64_64_sc25519_sub_nored:
+mov %rsp,%r11
+and $31,%r11
+add $0,%r11
+sub %r11,%rsp
+
+# qhasm: r0 = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>r0=int64#4
+# asm 2: movq 0(<xp=%rsi),>r0=%rcx
+movq 0(%rsi),%rcx
+
+# qhasm: r1 = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>r1=int64#5
+# asm 2: movq 8(<xp=%rsi),>r1=%r8
+movq 8(%rsi),%r8
+
+# qhasm: r2 = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>r2=int64#6
+# asm 2: movq 16(<xp=%rsi),>r2=%r9
+movq 16(%rsi),%r9
+
+# qhasm: r3 = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>r3=int64#2
+# asm 2: movq 24(<xp=%rsi),>r3=%rsi
+movq 24(%rsi),%rsi
+
+# qhasm: carry? r0 -= *(uint64 *)(yp + 0)
+# asm 1: subq 0(<yp=int64#3),<r0=int64#4
+# asm 2: subq 0(<yp=%rdx),<r0=%rcx
+subq 0(%rdx),%rcx
+
+# qhasm: carry? r1 -= *(uint64 *)(yp + 8) - carry
+# asm 1: sbbq 8(<yp=int64#3),<r1=int64#5
+# asm 2: sbbq 8(<yp=%rdx),<r1=%r8
+sbbq 8(%rdx),%r8
+
+# qhasm: carry? r2 -= *(uint64 *)(yp + 16) - carry
+# asm 1: sbbq 16(<yp=int64#3),<r2=int64#6
+# asm 2: sbbq 16(<yp=%rdx),<r2=%r9
+sbbq 16(%rdx),%r9
+
+# qhasm: r3 -= *(uint64 *)(yp + 24) - carry
+# asm 1: sbbq 24(<yp=int64#3),<r3=int64#2
+# asm 2: sbbq 24(<yp=%rdx),<r3=%rsi
+sbbq 24(%rdx),%rsi
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#4,0(<rp=int64#1)
+# asm 2: movq <r0=%rcx,0(<rp=%rdi)
+movq %rcx,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#5,8(<rp=int64#1)
+# asm 2: movq <r1=%r8,8(<rp=%rdi)
+movq %r8,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#6,16(<rp=int64#1)
+# asm 2: movq <r2=%r9,16(<rp=%rdi)
+movq %r9,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#2,24(<rp=int64#1)
+# asm 2: movq <r3=%rsi,24(<rp=%rdi)
+movq %rsi,24(%rdi)
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/ed25519-amd64-asm/sc25519_to32bytes.c b/ext/ed25519-amd64-asm/sc25519_to32bytes.c
new file mode 100644
index 00000000..eddb235d
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_to32bytes.c
@@ -0,0 +1,8 @@
+#include "sc25519.h"
+
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
+{
+ /* assuming little-endian */
+ int i;
+ for(i=0;i<32;i++) r[i] = i[(unsigned char *)x->v];
+}
diff --git a/ext/ed25519-amd64-asm/sc25519_window4.c b/ext/ed25519-amd64-asm/sc25519_window4.c
new file mode 100644
index 00000000..683a1d4b
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sc25519_window4.c
@@ -0,0 +1,27 @@
+#include "sc25519.h"
+
+void sc25519_window4(signed char r[64], const sc25519 *s)
+{
+ char carry;
+ int i;
+ for(i=0;i<16;i++)
+ r[i] = (s->v[0] >> (4*i)) & 15;
+ for(i=0;i<16;i++)
+ r[i+16] = (s->v[1] >> (4*i)) & 15;
+ for(i=0;i<16;i++)
+ r[i+32] = (s->v[2] >> (4*i)) & 15;
+ for(i=0;i<16;i++)
+ r[i+48] = (s->v[3] >> (4*i)) & 15;
+
+ /* Making it signed */
+ carry = 0;
+ for(i=0;i<63;i++)
+ {
+ r[i] += carry;
+ r[i+1] += r[i] >> 4;
+ r[i] &= 15;
+ carry = r[i] >> 3;
+ r[i] -= carry << 4;
+ }
+ r[63] += carry;
+}
diff --git a/ext/ed25519-amd64-asm/sign.c b/ext/ed25519-amd64-asm/sign.c
new file mode 100644
index 00000000..958e4a14
--- /dev/null
+++ b/ext/ed25519-amd64-asm/sign.c
@@ -0,0 +1,165 @@
+#include <stdlib.h>
+#include <string.h>
+/*#include "crypto_sign.h"
+#include "crypto_hash_sha512.h"*/
+#include "ge25519.h"
+
+/* Original */
+#if 0
+int crypto_sign(
+ unsigned char *sm,unsigned long long *smlen,
+ const unsigned char *m,unsigned long long mlen,
+ const unsigned char *sk
+ )
+{
+ unsigned char pk[32];
+ unsigned char az[64];
+ unsigned char nonce[64];
+ unsigned char hram[64];
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+
+ memmove(pk,sk + 32,32);
+ /* pk: 32-byte public key A */
+
+ crypto_hash_sha512(az,sk,32);
+ az[0] &= 248;
+ az[31] &= 127;
+ az[31] |= 64;
+ /* az: 32-byte scalar a, 32-byte randomizer z */
+
+ *smlen = mlen + 64;
+ memmove(sm + 64,m,mlen);
+ memmove(sm + 32,az + 32,32);
+ /* sm: 32-byte uninit, 32-byte z, mlen-byte m */
+
+ crypto_hash_sha512(nonce, sm+32, mlen+32);
+ /* nonce: 64-byte H(z,m) */
+
+ sc25519_from64bytes(&sck, nonce);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(sm, &ger);
+ /* sm: 32-byte R, 32-byte z, mlen-byte m */
+
+ memmove(sm + 32,pk,32);
+ /* sm: 32-byte R, 32-byte A, mlen-byte m */
+
+ crypto_hash_sha512(hram,sm,mlen + 64);
+ /* hram: 64-byte H(R,A,m) */
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, az);
+ sc25519_mul(&scs, &scs, &scsk);
+ sc25519_add(&scs, &scs, &sck);
+ /* scs: S = nonce + H(R,A,m)a */
+
+ sc25519_to32bytes(sm + 32,&scs);
+ /* sm: 32-byte R, 32-byte S, mlen-byte m */
+
+ return 0;
+}
+#endif
+
+#if 0
+void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
+{
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char r[32];
+ unsigned char s[32];
+ unsigned char extsk[64];
+ unsigned char hmg[crypto_hash_sha512_BYTES];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char *sig = (unsigned char *)signature;
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+
+ SHA512::hash(digest,msg,len);
+
+ SHA512::hash(extsk,myPrivate.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = extsk[32 + i];
+ for(unsigned int i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ SHA512::hash(hmg,sig + 32,64);
+
+ /* Computation of R */
+ sc25519_from64bytes(&sck, hmg);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(r, &ger);
+
+ /* Computation of s */
+ for(unsigned int i=0;i<32;i++)
+ sig[i] = r[i];
+
+ get_hram(hram,sig,myPublic.data + 32,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, extsk);
+ sc25519_mul(&scs, &scs, &scsk);
+
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(s,&scs); /* cat s */
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = s[i];
+}
+
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
+{
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ //crypto_hash_sha512(hram,playground,smlen);
+ ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
+}
+#endif
+
+extern void ZT_sha512internal(void *digest,const void *data,unsigned int len);
+
+extern void ed25519_amd64_asm_sign(const unsigned char *sk,const unsigned char *pk,const unsigned char *m,const unsigned int mlen,unsigned char *sig)
+{
+ unsigned char az[64];
+ unsigned char nonce[64];
+ unsigned char hram[64];
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char digest[64];
+ unsigned int i;
+
+ ZT_sha512internal(digest,m,mlen);
+
+ ZT_sha512internal(az,sk,32);
+ az[0] &= 248;
+ az[31] &= 127;
+ az[31] |= 64;
+
+ for(i=0;i<32;i++)
+ sig[32 + i] = az[32 + i];
+ for(i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ ZT_sha512internal(nonce,sig + 32,64);
+
+ sc25519_from64bytes(&sck, nonce);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(sig, &ger);
+
+ memmove(sig + 32,pk,32);
+
+ ZT_sha512internal(hram,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, az);
+ sc25519_mul(&scs, &scs, &scsk);
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(sig + 32,&scs);
+}
diff --git a/ext/ed25519-amd64-asm/ull4_mul.s b/ext/ed25519-amd64-asm/ull4_mul.s
new file mode 100644
index 00000000..9f7b4fa2
--- /dev/null
+++ b/ext/ed25519-amd64-asm/ull4_mul.s
@@ -0,0 +1,716 @@
+
+# qhasm: int64 rp
+
+# qhasm: int64 xp
+
+# qhasm: int64 yp
+
+# qhasm: input rp
+
+# qhasm: input xp
+
+# qhasm: input yp
+
+# qhasm: int64 r0
+
+# qhasm: int64 r1
+
+# qhasm: int64 r2
+
+# qhasm: int64 r3
+
+# qhasm: int64 r4
+
+# qhasm: int64 r5
+
+# qhasm: int64 r6
+
+# qhasm: int64 r7
+
+# qhasm: int64 c
+
+# qhasm: int64 zero
+
+# qhasm: int64 rax
+
+# qhasm: int64 rdx
+
+# qhasm: int64 caller1
+
+# qhasm: int64 caller2
+
+# qhasm: int64 caller3
+
+# qhasm: int64 caller4
+
+# qhasm: int64 caller5
+
+# qhasm: int64 caller6
+
+# qhasm: int64 caller7
+
+# qhasm: caller caller1
+
+# qhasm: caller caller2
+
+# qhasm: caller caller3
+
+# qhasm: caller caller4
+
+# qhasm: caller caller5
+
+# qhasm: caller caller6
+
+# qhasm: caller caller7
+
+# qhasm: stack64 caller1_stack
+
+# qhasm: stack64 caller2_stack
+
+# qhasm: stack64 caller3_stack
+
+# qhasm: stack64 caller4_stack
+
+# qhasm: stack64 caller5_stack
+
+# qhasm: stack64 caller6_stack
+
+# qhasm: stack64 caller7_stack
+
+# qhasm: enter crypto_sign_ed25519_amd64_64_ull4_mul
+.text
+.p2align 5
+.globl _crypto_sign_ed25519_amd64_64_ull4_mul
+.globl crypto_sign_ed25519_amd64_64_ull4_mul
+_crypto_sign_ed25519_amd64_64_ull4_mul:
+crypto_sign_ed25519_amd64_64_ull4_mul:
+mov %rsp,%r11
+and $31,%r11
+add $64,%r11
+sub %r11,%rsp
+
+# qhasm: caller1_stack = caller1
+# asm 1: movq <caller1=int64#9,>caller1_stack=stack64#1
+# asm 2: movq <caller1=%r11,>caller1_stack=0(%rsp)
+movq %r11,0(%rsp)
+
+# qhasm: caller2_stack = caller2
+# asm 1: movq <caller2=int64#10,>caller2_stack=stack64#2
+# asm 2: movq <caller2=%r12,>caller2_stack=8(%rsp)
+movq %r12,8(%rsp)
+
+# qhasm: caller3_stack = caller3
+# asm 1: movq <caller3=int64#11,>caller3_stack=stack64#3
+# asm 2: movq <caller3=%r13,>caller3_stack=16(%rsp)
+movq %r13,16(%rsp)
+
+# qhasm: caller4_stack = caller4
+# asm 1: movq <caller4=int64#12,>caller4_stack=stack64#4
+# asm 2: movq <caller4=%r14,>caller4_stack=24(%rsp)
+movq %r14,24(%rsp)
+
+# qhasm: caller5_stack = caller5
+# asm 1: movq <caller5=int64#13,>caller5_stack=stack64#5
+# asm 2: movq <caller5=%r15,>caller5_stack=32(%rsp)
+movq %r15,32(%rsp)
+
+# qhasm: caller6_stack = caller6
+# asm 1: movq <caller6=int64#14,>caller6_stack=stack64#6
+# asm 2: movq <caller6=%rbx,>caller6_stack=40(%rsp)
+movq %rbx,40(%rsp)
+
+# qhasm: caller7_stack = caller7
+# asm 1: movq <caller7=int64#15,>caller7_stack=stack64#7
+# asm 2: movq <caller7=%rbp,>caller7_stack=48(%rsp)
+movq %rbp,48(%rsp)
+
+# qhasm: yp = yp
+# asm 1: mov <yp=int64#3,>yp=int64#4
+# asm 2: mov <yp=%rdx,>yp=%rcx
+mov %rdx,%rcx
+
+# qhasm: r4 = 0
+# asm 1: mov $0,>r4=int64#5
+# asm 2: mov $0,>r4=%r8
+mov $0,%r8
+
+# qhasm: r5 = 0
+# asm 1: mov $0,>r5=int64#6
+# asm 2: mov $0,>r5=%r9
+mov $0,%r9
+
+# qhasm: r6 = 0
+# asm 1: mov $0,>r6=int64#8
+# asm 2: mov $0,>r6=%r10
+mov $0,%r10
+
+# qhasm: r7 = 0
+# asm 1: mov $0,>r7=int64#9
+# asm 2: mov $0,>r7=%r11
+mov $0,%r11
+
+# qhasm: zero = 0
+# asm 1: mov $0,>zero=int64#10
+# asm 2: mov $0,>zero=%r12
+mov $0,%r12
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: r0 = rax
+# asm 1: mov <rax=int64#7,>r0=int64#11
+# asm 2: mov <rax=%rax,>r0=%r13
+mov %rax,%r13
+
+# qhasm: c = rdx
+# asm 1: mov <rdx=int64#3,>c=int64#12
+# asm 2: mov <rdx=%rdx,>c=%r14
+mov %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: r1 = rax
+# asm 1: mov <rax=int64#7,>r1=int64#13
+# asm 2: mov <rax=%rax,>r1=%r15
+mov %rax,%r15
+
+# qhasm: carry? r1 += c
+# asm 1: add <c=int64#12,<r1=int64#13
+# asm 2: add <c=%r14,<r1=%r15
+add %r14,%r15
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: r2 = rax
+# asm 1: mov <rax=int64#7,>r2=int64#14
+# asm 2: mov <rax=%rax,>r2=%rbx
+mov %rax,%rbx
+
+# qhasm: carry? r2 += c
+# asm 1: add <c=int64#12,<r2=int64#14
+# asm 2: add <c=%r14,<r2=%rbx
+add %r14,%rbx
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 0)
+# asm 1: movq 0(<xp=int64#2),>rax=int64#7
+# asm 2: movq 0(<xp=%rsi),>rax=%rax
+movq 0(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: r3 = rax
+# asm 1: mov <rax=int64#7,>r3=int64#15
+# asm 2: mov <rax=%rax,>r3=%rbp
+mov %rax,%rbp
+
+# qhasm: carry? r3 += c
+# asm 1: add <c=int64#12,<r3=int64#15
+# asm 2: add <c=%r14,<r3=%rbp
+add %r14,%rbp
+
+# qhasm: r4 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r4=int64#5
+# asm 2: adc <rdx=%rdx,<r4=%r8
+adc %rdx,%r8
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: carry? r1 += rax
+# asm 1: add <rax=int64#7,<r1=int64#13
+# asm 2: add <rax=%rax,<r1=%r15
+add %rax,%r15
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: carry? r2 += rax
+# asm 1: add <rax=int64#7,<r2=int64#14
+# asm 2: add <rax=%rax,<r2=%rbx
+add %rax,%rbx
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r2 += c
+# asm 1: add <c=int64#12,<r2=int64#14
+# asm 2: add <c=%r14,<r2=%rbx
+add %r14,%rbx
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: carry? r3 += rax
+# asm 1: add <rax=int64#7,<r3=int64#15
+# asm 2: add <rax=%rax,<r3=%rbp
+add %rax,%rbp
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r3 += c
+# asm 1: add <c=int64#12,<r3=int64#15
+# asm 2: add <c=%r14,<r3=%rbp
+add %r14,%rbp
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 8)
+# asm 1: movq 8(<xp=int64#2),>rax=int64#7
+# asm 2: movq 8(<xp=%rsi),>rax=%rax
+movq 8(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: carry? r4 += rax
+# asm 1: add <rax=int64#7,<r4=int64#5
+# asm 2: add <rax=%rax,<r4=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r4 += c
+# asm 1: add <c=int64#12,<r4=int64#5
+# asm 2: add <c=%r14,<r4=%r8
+add %r14,%r8
+
+# qhasm: r5 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r5=int64#6
+# asm 2: adc <rdx=%rdx,<r5=%r9
+adc %rdx,%r9
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: carry? r2 += rax
+# asm 1: add <rax=int64#7,<r2=int64#14
+# asm 2: add <rax=%rax,<r2=%rbx
+add %rax,%rbx
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: carry? r3 += rax
+# asm 1: add <rax=int64#7,<r3=int64#15
+# asm 2: add <rax=%rax,<r3=%rbp
+add %rax,%rbp
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r3 += c
+# asm 1: add <c=int64#12,<r3=int64#15
+# asm 2: add <c=%r14,<r3=%rbp
+add %r14,%rbp
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: carry? r4 += rax
+# asm 1: add <rax=int64#7,<r4=int64#5
+# asm 2: add <rax=%rax,<r4=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r4 += c
+# asm 1: add <c=int64#12,<r4=int64#5
+# asm 2: add <c=%r14,<r4=%r8
+add %r14,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 16)
+# asm 1: movq 16(<xp=int64#2),>rax=int64#7
+# asm 2: movq 16(<xp=%rsi),>rax=%rax
+movq 16(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: carry? r5 += rax
+# asm 1: add <rax=int64#7,<r5=int64#6
+# asm 2: add <rax=%rax,<r5=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r5 += c
+# asm 1: add <c=int64#12,<r5=int64#6
+# asm 2: add <c=%r14,<r5=%r9
+add %r14,%r9
+
+# qhasm: r6 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r6=int64#8
+# asm 2: adc <rdx=%rdx,<r6=%r10
+adc %rdx,%r10
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 0)
+# asm 1: mulq 0(<yp=int64#4)
+# asm 2: mulq 0(<yp=%rcx)
+mulq 0(%rcx)
+
+# qhasm: carry? r3 += rax
+# asm 1: add <rax=int64#7,<r3=int64#15
+# asm 2: add <rax=%rax,<r3=%rbp
+add %rax,%rbp
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 8)
+# asm 1: mulq 8(<yp=int64#4)
+# asm 2: mulq 8(<yp=%rcx)
+mulq 8(%rcx)
+
+# qhasm: carry? r4 += rax
+# asm 1: add <rax=int64#7,<r4=int64#5
+# asm 2: add <rax=%rax,<r4=%r8
+add %rax,%r8
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r4 += c
+# asm 1: add <c=int64#12,<r4=int64#5
+# asm 2: add <c=%r14,<r4=%r8
+add %r14,%r8
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 16)
+# asm 1: mulq 16(<yp=int64#4)
+# asm 2: mulq 16(<yp=%rcx)
+mulq 16(%rcx)
+
+# qhasm: carry? r5 += rax
+# asm 1: add <rax=int64#7,<r5=int64#6
+# asm 2: add <rax=%rax,<r5=%r9
+add %rax,%r9
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r5 += c
+# asm 1: add <c=int64#12,<r5=int64#6
+# asm 2: add <c=%r14,<r5=%r9
+add %r14,%r9
+
+# qhasm: c = 0
+# asm 1: mov $0,>c=int64#12
+# asm 2: mov $0,>c=%r14
+mov $0,%r14
+
+# qhasm: c += rdx + carry
+# asm 1: adc <rdx=int64#3,<c=int64#12
+# asm 2: adc <rdx=%rdx,<c=%r14
+adc %rdx,%r14
+
+# qhasm: rax = *(uint64 *)(xp + 24)
+# asm 1: movq 24(<xp=int64#2),>rax=int64#7
+# asm 2: movq 24(<xp=%rsi),>rax=%rax
+movq 24(%rsi),%rax
+
+# qhasm: (uint128) rdx rax = rax * *(uint64 *)(yp + 24)
+# asm 1: mulq 24(<yp=int64#4)
+# asm 2: mulq 24(<yp=%rcx)
+mulq 24(%rcx)
+
+# qhasm: carry? r6 += rax
+# asm 1: add <rax=int64#7,<r6=int64#8
+# asm 2: add <rax=%rax,<r6=%r10
+add %rax,%r10
+
+# qhasm: rdx += zero + carry
+# asm 1: adc <zero=int64#10,<rdx=int64#3
+# asm 2: adc <zero=%r12,<rdx=%rdx
+adc %r12,%rdx
+
+# qhasm: carry? r6 += c
+# asm 1: add <c=int64#12,<r6=int64#8
+# asm 2: add <c=%r14,<r6=%r10
+add %r14,%r10
+
+# qhasm: r7 += rdx + carry
+# asm 1: adc <rdx=int64#3,<r7=int64#9
+# asm 2: adc <rdx=%rdx,<r7=%r11
+adc %rdx,%r11
+
+# qhasm: *(uint64 *)(rp + 0) = r0
+# asm 1: movq <r0=int64#11,0(<rp=int64#1)
+# asm 2: movq <r0=%r13,0(<rp=%rdi)
+movq %r13,0(%rdi)
+
+# qhasm: *(uint64 *)(rp + 8) = r1
+# asm 1: movq <r1=int64#13,8(<rp=int64#1)
+# asm 2: movq <r1=%r15,8(<rp=%rdi)
+movq %r15,8(%rdi)
+
+# qhasm: *(uint64 *)(rp + 16) = r2
+# asm 1: movq <r2=int64#14,16(<rp=int64#1)
+# asm 2: movq <r2=%rbx,16(<rp=%rdi)
+movq %rbx,16(%rdi)
+
+# qhasm: *(uint64 *)(rp + 24) = r3
+# asm 1: movq <r3=int64#15,24(<rp=int64#1)
+# asm 2: movq <r3=%rbp,24(<rp=%rdi)
+movq %rbp,24(%rdi)
+
+# qhasm: *(uint64 *)(rp + 32) = r4
+# asm 1: movq <r4=int64#5,32(<rp=int64#1)
+# asm 2: movq <r4=%r8,32(<rp=%rdi)
+movq %r8,32(%rdi)
+
+# qhasm: *(uint64 *)(rp + 40) = r5
+# asm 1: movq <r5=int64#6,40(<rp=int64#1)
+# asm 2: movq <r5=%r9,40(<rp=%rdi)
+movq %r9,40(%rdi)
+
+# qhasm: *(uint64 *)(rp + 48) = r6
+# asm 1: movq <r6=int64#8,48(<rp=int64#1)
+# asm 2: movq <r6=%r10,48(<rp=%rdi)
+movq %r10,48(%rdi)
+
+# qhasm: *(uint64 *)(rp + 56) = r7
+# asm 1: movq <r7=int64#9,56(<rp=int64#1)
+# asm 2: movq <r7=%r11,56(<rp=%rdi)
+movq %r11,56(%rdi)
+
+# qhasm: caller1 = caller1_stack
+# asm 1: movq <caller1_stack=stack64#1,>caller1=int64#9
+# asm 2: movq <caller1_stack=0(%rsp),>caller1=%r11
+movq 0(%rsp),%r11
+
+# qhasm: caller2 = caller2_stack
+# asm 1: movq <caller2_stack=stack64#2,>caller2=int64#10
+# asm 2: movq <caller2_stack=8(%rsp),>caller2=%r12
+movq 8(%rsp),%r12
+
+# qhasm: caller3 = caller3_stack
+# asm 1: movq <caller3_stack=stack64#3,>caller3=int64#11
+# asm 2: movq <caller3_stack=16(%rsp),>caller3=%r13
+movq 16(%rsp),%r13
+
+# qhasm: caller4 = caller4_stack
+# asm 1: movq <caller4_stack=stack64#4,>caller4=int64#12
+# asm 2: movq <caller4_stack=24(%rsp),>caller4=%r14
+movq 24(%rsp),%r14
+
+# qhasm: caller5 = caller5_stack
+# asm 1: movq <caller5_stack=stack64#5,>caller5=int64#13
+# asm 2: movq <caller5_stack=32(%rsp),>caller5=%r15
+movq 32(%rsp),%r15
+
+# qhasm: caller6 = caller6_stack
+# asm 1: movq <caller6_stack=stack64#6,>caller6=int64#14
+# asm 2: movq <caller6_stack=40(%rsp),>caller6=%rbx
+movq 40(%rsp),%rbx
+
+# qhasm: caller7 = caller7_stack
+# asm 1: movq <caller7_stack=stack64#7,>caller7=int64#15
+# asm 2: movq <caller7_stack=48(%rsp),>caller7=%rbp
+movq 48(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+mov %rdi,%rax
+mov %rsi,%rdx
+ret
diff --git a/ext/http-parser/http_parser.c b/ext/http-parser/http_parser.c
index 3c896ffa..895bf0c7 100644
--- a/ext/http-parser/http_parser.c
+++ b/ext/http-parser/http_parser.c
@@ -1366,12 +1366,7 @@ reexecute:
|| c != CONTENT_LENGTH[parser->index]) {
parser->header_state = h_general;
} else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
- if (parser->flags & F_CONTENTLENGTH) {
- SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
- goto error;
- }
parser->header_state = h_content_length;
- parser->flags |= F_CONTENTLENGTH;
}
break;
@@ -1474,6 +1469,12 @@ reexecute:
goto error;
}
+ if (parser->flags & F_CONTENTLENGTH) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->flags |= F_CONTENTLENGTH;
parser->content_length = ch - '0';
break;
diff --git a/ext/http-parser/http_parser.h b/ext/http-parser/http_parser.h
index 105ae510..45c72a07 100644
--- a/ext/http-parser/http_parser.h
+++ b/ext/http-parser/http_parser.h
@@ -27,7 +27,7 @@ extern "C" {
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 7
-#define HTTP_PARSER_VERSION_PATCH 0
+#define HTTP_PARSER_VERSION_PATCH 1
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
@@ -90,6 +90,76 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
+/* Status Codes */
+#define HTTP_STATUS_MAP(XX) \
+ XX(100, CONTINUE, Continue) \
+ XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
+ XX(102, PROCESSING, Processing) \
+ XX(200, OK, OK) \
+ XX(201, CREATED, Created) \
+ XX(202, ACCEPTED, Accepted) \
+ XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
+ XX(204, NO_CONTENT, No Content) \
+ XX(205, RESET_CONTENT, Reset Content) \
+ XX(206, PARTIAL_CONTENT, Partial Content) \
+ XX(207, MULTI_STATUS, Multi-Status) \
+ XX(208, ALREADY_REPORTED, Already Reported) \
+ XX(226, IM_USED, IM Used) \
+ XX(300, MULTIPLE_CHOICES, Multiple Choices) \
+ XX(301, MOVED_PERMANENTLY, Moved Permanently) \
+ XX(302, FOUND, Found) \
+ XX(303, SEE_OTHER, See Other) \
+ XX(304, NOT_MODIFIED, Not Modified) \
+ XX(305, USE_PROXY, Use Proxy) \
+ XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
+ XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
+ XX(400, BAD_REQUEST, Bad Request) \
+ XX(401, UNAUTHORIZED, Unauthorized) \
+ XX(402, PAYMENT_REQUIRED, Payment Required) \
+ XX(403, FORBIDDEN, Forbidden) \
+ XX(404, NOT_FOUND, Not Found) \
+ XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
+ XX(406, NOT_ACCEPTABLE, Not Acceptable) \
+ XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
+ XX(408, REQUEST_TIMEOUT, Request Timeout) \
+ XX(409, CONFLICT, Conflict) \
+ XX(410, GONE, Gone) \
+ XX(411, LENGTH_REQUIRED, Length Required) \
+ XX(412, PRECONDITION_FAILED, Precondition Failed) \
+ XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
+ XX(414, URI_TOO_LONG, URI Too Long) \
+ XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
+ XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
+ XX(417, EXPECTATION_FAILED, Expectation Failed) \
+ XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
+ XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
+ XX(423, LOCKED, Locked) \
+ XX(424, FAILED_DEPENDENCY, Failed Dependency) \
+ XX(426, UPGRADE_REQUIRED, Upgrade Required) \
+ XX(428, PRECONDITION_REQUIRED, Precondition Required) \
+ XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
+ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
+ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
+ XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
+ XX(501, NOT_IMPLEMENTED, Not Implemented) \
+ XX(502, BAD_GATEWAY, Bad Gateway) \
+ XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
+ XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
+ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
+ XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
+ XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
+ XX(508, LOOP_DETECTED, Loop Detected) \
+ XX(510, NOT_EXTENDED, Not Extended) \
+ XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
+
+enum http_status
+ {
+#define XX(num, name, string) HTTP_STATUS_##name = num,
+ HTTP_STATUS_MAP(XX)
+#undef XX
+ };
+
+
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
diff --git a/ext/installfiles/linux/zerotier-containerized/Dockerfile b/ext/installfiles/linux/zerotier-containerized/Dockerfile
new file mode 100644
index 00000000..85faace0
--- /dev/null
+++ b/ext/installfiles/linux/zerotier-containerized/Dockerfile
@@ -0,0 +1,20 @@
+FROM alpine:latest
+MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
+
+LABEL version="1.2.4"
+LABEL description="Containerized ZeroTier One for use on CoreOS or other Docker-only Linux hosts."
+
+# Uncomment to build in container
+#RUN apk add --update alpine-sdk linux-headers
+
+RUN apk add --update libgcc libstdc++
+
+ADD zerotier-one /
+RUN chmod 0755 /zerotier-one
+RUN ln -sf /zerotier-one /zerotier-cli
+RUN mkdir -p /var/lib/zerotier-one
+
+ADD main.sh /
+RUN chmod 0755 /main.sh
+
+ENTRYPOINT /main.sh
diff --git a/ext/installfiles/linux/zerotier-containerized/main.sh b/ext/installfiles/linux/zerotier-containerized/main.sh
new file mode 100755
index 00000000..685a6891
--- /dev/null
+++ b/ext/installfiles/linux/zerotier-containerized/main.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
+
+if [ ! -e /dev/net/tun ]; then
+ echo 'FATAL: cannot start ZeroTier One in container: /dev/net/tun not present.'
+ exit 1
+fi
+
+exec /zerotier-one
diff --git a/ext/installfiles/mac-update/updater.tmpl.sh b/ext/installfiles/mac-update/updater.tmpl.sh
new file mode 100644
index 00000000..0b07f6d9
--- /dev/null
+++ b/ext/installfiles/mac-update/updater.tmpl.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+export PATH=/bin:/usr/bin:/sbin:/usr/sbin
+shopt -s expand_aliases
+
+if [ "$UID" -ne 0 ]; then
+ echo '*** Auto-updater must be run as root.'
+ exit 1
+fi
+
+scriptPath="`dirname "$0"`/`basename "$0"`"
+if [ ! -s "$scriptPath" ]; then
+ scriptPath="$0"
+ if [ ! -s "$scriptPath" ]; then
+ echo "*** Auto-updater cannot determine its own path; $scriptPath is not readable."
+ exit 2
+ fi
+fi
+
+endMarkerIndex=`grep -a -b -E '^################' "$scriptPath" | head -c 16 | cut -d : -f 1`
+if [ "$endMarkerIndex" -le 100 ]; then
+ echo 'Internal error: unable to find end of script / start of binary data marker.'
+ exit 2
+fi
+blobStart=`expr $endMarkerIndex + 17`
+if [ "$blobStart" -le "$endMarkerIndex" ]; then
+ echo 'Internal error: unable to find end of script / start of binary data marker.'
+ exit 2
+fi
+
+rm -f /tmp/ZeroTierOne-update.pkg
+tail -c +$blobStart "$scriptPath" >/tmp/ZeroTierOne-update.pkg
+chmod 0600 /tmp/ZeroTierOne-update.pkg
+
+if [ -s /tmp/ZeroTierOne-update.pkg ]; then
+ rm -f '/Library/Application Support/ZeroTier/One/latest-update.exe' '/Library/Application Support/ZeroTier/One/latest-update.json' /tmp/ZeroTierOne-update.log
+ installer -verbose -pkg /tmp/ZeroTierOne-update.pkg -target / >/tmp/ZeroTierOne-update.log 2>&1
+ rm -f /tmp/ZeroTierOne-update.pkg
+ exit 0
+else
+ echo '*** Error self-unpacking update!'
+ exit 3
+fi
+
+# Do not remove the last line or add a carriage return to it! The installer
+# looks for an unterminated line beginning with 16 #'s in itself to find
+# the binary blob data, which is appended after it.
+
+################ \ No newline at end of file
diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj
index d9730527..59c22dde 100755
--- a/ext/installfiles/mac/ZeroTier One.pkgproj
+++ b/ext/installfiles/mac/ZeroTier One.pkgproj
@@ -37,7 +37,7 @@
<key>GID</key>
<integer>80</integer>
<key>PATH</key>
- <string>mac-ui-macgap1-wrapper/bin/ZeroTier One.app</string>
+ <string>../../../macui/build/Release/ZeroTier One.app</string>
<key>PATH_TYPE</key>
<integer>1</integer>
<key>PERMISSIONS</key>
@@ -123,119 +123,6 @@
</dict>
<dict>
<key>CHILDREN</key>
- <array>
- <dict>
- <key>CHILDREN</key>
- <array/>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui/index.html</string>
- <key>PATH_TYPE</key>
- <integer>1</integer>
- <key>PERMISSIONS</key>
- <integer>420</integer>
- <key>TYPE</key>
- <integer>3</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- <dict>
- <key>CHILDREN</key>
- <array/>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui/main.js</string>
- <key>PATH_TYPE</key>
- <integer>1</integer>
- <key>PERMISSIONS</key>
- <integer>420</integer>
- <key>TYPE</key>
- <integer>3</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- <dict>
- <key>CHILDREN</key>
- <array/>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui/react.min.js</string>
- <key>PATH_TYPE</key>
- <integer>1</integer>
- <key>PERMISSIONS</key>
- <integer>420</integer>
- <key>TYPE</key>
- <integer>3</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- <dict>
- <key>CHILDREN</key>
- <array/>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui/simpleajax.min.js</string>
- <key>PATH_TYPE</key>
- <integer>1</integer>
- <key>PERMISSIONS</key>
- <integer>420</integer>
- <key>TYPE</key>
- <integer>3</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- <dict>
- <key>CHILDREN</key>
- <array/>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui/zerotier.css</string>
- <key>PATH_TYPE</key>
- <integer>1</integer>
- <key>PERMISSIONS</key>
- <integer>420</integer>
- <key>TYPE</key>
- <integer>3</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- <dict>
- <key>CHILDREN</key>
- <array/>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui/ztui.min.js</string>
- <key>PATH_TYPE</key>
- <integer>1</integer>
- <key>PERMISSIONS</key>
- <integer>420</integer>
- <key>TYPE</key>
- <integer>3</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- </array>
- <key>GID</key>
- <integer>0</integer>
- <key>PATH</key>
- <string>ui</string>
- <key>PATH_TYPE</key>
- <integer>0</integer>
- <key>PERMISSIONS</key>
- <integer>493</integer>
- <key>TYPE</key>
- <integer>2</integer>
- <key>UID</key>
- <integer>0</integer>
- </dict>
- <dict>
- <key>CHILDREN</key>
<array/>
<key>GID</key>
<integer>0</integer>
@@ -726,6 +613,12 @@
</dict>
<key>PAYLOAD_TYPE</key>
<integer>0</integer>
+ <key>SHOW_INVISIBLE</key>
+ <false/>
+ <key>SPLIT_FORKS</key>
+ <true/>
+ <key>TREAT_MISSING_FILES_AS_WARNING</key>
+ <false/>
<key>VERSION</key>
<integer>3</integer>
</dict>
@@ -754,12 +647,24 @@
<integer>1</integer>
<key>CONCLUSION_ACTION</key>
<integer>0</integer>
+ <key>FOLLOW_SYMBOLIC_LINKS</key>
+ <false/>
<key>IDENTIFIER</key>
<string>com.zerotier.pkg.ZeroTierOne</string>
+ <key>LOCATION</key>
+ <integer>0</integer>
+ <key>NAME</key>
+ <string></string>
<key>OVERWRITE_PERMISSIONS</key>
<false/>
+ <key>PAYLOAD_SIZE</key>
+ <integer>-1</integer>
+ <key>RELOCATABLE</key>
+ <false/>
+ <key>USE_HFS+_COMPRESSION</key>
+ <false/>
<key>VERSION</key>
- <string>1.1.14</string>
+ <string>1.2.6</string>
</dict>
<key>PROJECT_COMMENTS</key>
<dict>
@@ -773,7 +678,7 @@
ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp
dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u
dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD
- b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuNDciPgo8c3R5bGUg
+ b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE1MDQuNzYiPgo8c3R5bGUg
dHlwZT0idGV4dC9jc3MiPgpwLnAxIHttYXJnaW46IDAuMHB4IDAu
MHB4IDAuMHB4IDAuMHB4OyBsaW5lLWhlaWdodDogMTQuMHB4OyBm
b250OiAxMi4wcHggSGVsdmV0aWNhOyBjb2xvcjogIzAwMDAwMDsg
@@ -782,7 +687,7 @@
b2R5Pgo8cCBjbGFzcz0icDEiPjxzcGFuIGNsYXNzPSJzMSI+WmVy
b1RpZXIgT25lIC0gTmV0d29yayBWaXJ0dWFsaXphdGlvbiBFdmVy
eXdoZXJlPC9zcGFuPjwvcD4KPHAgY2xhc3M9InAxIj48c3BhbiBj
- bGFzcz0iczEiPihjKTIwMTEtMjAxNiBaZXJvVGllciwgSW5jLjwv
+ bGFzcz0iczEiPihjKTIwMTEtMjAxNyBaZXJvVGllciwgSW5jLjwv
c3Bhbj48L3A+CjxwIGNsYXNzPSJwMSI+PHNwYW4gY2xhc3M9InMx
Ij5jb250YWN0QHplcm90aWVyLmNvbTwvc3Bhbj48L3A+CjxwIGNs
YXNzPSJwMSI+PHNwYW4gY2xhc3M9InMxIj48YnI+Cjwvc3Bhbj48
@@ -975,6 +880,8 @@
</array>
<key>NAME</key>
<string>ZeroTier One</string>
+ <key>PAYLOAD_ONLY</key>
+ <false/>
</dict>
</dict>
<key>TYPE</key>
diff --git a/ext/installfiles/mac/launch.sh b/ext/installfiles/mac/launch.sh
index 41c4b9c8..b02a6670 100755
--- a/ext/installfiles/mac/launch.sh
+++ b/ext/installfiles/mac/launch.sh
@@ -1,7 +1,3 @@
#!/bin/bash
-
-zthome="/Library/Application Support/ZeroTier/One"
-export PATH="$zthome:/bin:/usr/bin:/sbin:/usr/sbin"
-
-# Launch ZeroTier One (not as daemon... launchd monitors it)
+export PATH="/Library/Application Support/ZeroTier/One:/bin:/usr/bin:/sbin:/usr/sbin"
exec zerotier-one
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Info.plist b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Info.plist
deleted file mode 100644
index c67923c7..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Info.plist
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>BuildMachineOSBuild</key>
- <string>15B42</string>
- <key>CFBundleDevelopmentRegion</key>
- <string>en</string>
- <key>CFBundleExecutable</key>
- <string>ZeroTier One</string>
- <key>CFBundleIconFile</key>
- <string>ZeroTierIcon</string>
- <key>CFBundleIdentifier</key>
- <string>com.zerotier.ZeroTier-One</string>
- <key>CFBundleInfoDictionaryVersion</key>
- <string>6.0</string>
- <key>CFBundleName</key>
- <string>ZeroTier One</string>
- <key>CFBundlePackageType</key>
- <string>APPL</string>
- <key>CFBundleShortVersionString</key>
- <string>1.0</string>
- <key>CFBundleSignature</key>
- <string>????</string>
- <key>CFBundleSupportedPlatforms</key>
- <array>
- <string>MacOSX</string>
- </array>
- <key>CFBundleVersion</key>
- <string>1</string>
- <key>DTCompiler</key>
- <string>com.apple.compilers.llvm.clang.1_0</string>
- <key>DTPlatformBuild</key>
- <string>7B1005</string>
- <key>DTPlatformVersion</key>
- <string>GM</string>
- <key>DTSDKBuild</key>
- <string>15A278</string>
- <key>DTSDKName</key>
- <string>macosx10.11</string>
- <key>DTXcode</key>
- <string>0711</string>
- <key>DTXcodeBuild</key>
- <string>7B1005</string>
- <key>LSApplicationCategoryType</key>
- <string>public.app-category.utilities</string>
- <key>LSMinimumSystemVersion</key>
- <string>10.7</string>
- <key>NSAppTransportSecurity</key>
- <dict>
- <key>NSAllowsArbitraryLoads</key>
- <true/>
- </dict>
- <key>NSMainNibFile</key>
- <string>MainMenu</string>
- <key>NSPrincipalClass</key>
- <string>NSApplication</string>
-</dict>
-</plist>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/MacOS/ZeroTier One b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/MacOS/ZeroTier One
deleted file mode 100755
index 8e38b861..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/MacOS/ZeroTier One
+++ /dev/null
Binary files differ
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/PkgInfo b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/PkgInfo
deleted file mode 100644
index bd04210f..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/PkgInfo
+++ /dev/null
@@ -1 +0,0 @@
-APPL???? \ No newline at end of file
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Credits.rtf b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Credits.rtf
deleted file mode 100644
index 6f388f66..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Credits.rtf
+++ /dev/null
@@ -1,13 +0,0 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1347\cocoasubrtf570
-{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
-{\colortbl;\red255\green255\blue255;}
-\vieww9600\viewh8400\viewkind0
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
-
-\f0\b\fs24 \cf0 (c)2011-2015 ZeroTier, Inc.\
-Licensed under the GNU GPLv3\
-\
-UI Wrapper MacGap (c) Twitter, Inc.\
-Licensed under the MIT License\
-http://macgap.com/\
-} \ No newline at end of file
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/InfoPlist.strings b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/InfoPlist.strings
deleted file mode 100644
index 5e45963c..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/InfoPlist.strings
+++ /dev/null
Binary files differ
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/MainMenu.nib b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/MainMenu.nib
deleted file mode 100644
index bac7faa7..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/MainMenu.nib
+++ /dev/null
Binary files differ
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Window.nib b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Window.nib
deleted file mode 100644
index e7b174a1..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/en.lproj/Window.nib
+++ /dev/null
Binary files differ
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/_CodeSignature/CodeResources b/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/_CodeSignature/CodeResources
deleted file mode 100644
index 5e334db0..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/_CodeSignature/CodeResources
+++ /dev/null
@@ -1,187 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>files</key>
- <dict>
- <key>Resources/ZeroTierIcon.icns</key>
- <data>
- 430Gd+4+jnim7WxXEEugp6G+Tgk=
- </data>
- <key>Resources/en.lproj/Credits.rtf</key>
- <dict>
- <key>hash</key>
- <data>
- ePttkAH2X1GJ6OL0UhDBAktxB3Y=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- <key>Resources/en.lproj/InfoPlist.strings</key>
- <dict>
- <key>hash</key>
- <data>
- MiLKDDnrUKr4EmuvhS5VQwxHGK8=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- <key>Resources/en.lproj/MainMenu.nib</key>
- <dict>
- <key>hash</key>
- <data>
- 8JZXf4/3df3LD+o74Y8WM0dV8io=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- <key>Resources/en.lproj/Window.nib</key>
- <dict>
- <key>hash</key>
- <data>
- aP0mIANPPnnTMmxYlELioz9ZO1I=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- </dict>
- <key>files2</key>
- <dict>
- <key>Resources/ZeroTierIcon.icns</key>
- <data>
- 430Gd+4+jnim7WxXEEugp6G+Tgk=
- </data>
- <key>Resources/en.lproj/Credits.rtf</key>
- <dict>
- <key>hash</key>
- <data>
- ePttkAH2X1GJ6OL0UhDBAktxB3Y=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- <key>Resources/en.lproj/InfoPlist.strings</key>
- <dict>
- <key>hash</key>
- <data>
- MiLKDDnrUKr4EmuvhS5VQwxHGK8=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- <key>Resources/en.lproj/MainMenu.nib</key>
- <dict>
- <key>hash</key>
- <data>
- 8JZXf4/3df3LD+o74Y8WM0dV8io=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- <key>Resources/en.lproj/Window.nib</key>
- <dict>
- <key>hash</key>
- <data>
- aP0mIANPPnnTMmxYlELioz9ZO1I=
- </data>
- <key>optional</key>
- <true/>
- </dict>
- </dict>
- <key>rules</key>
- <dict>
- <key>^Resources/</key>
- <true/>
- <key>^Resources/.*\.lproj/</key>
- <dict>
- <key>optional</key>
- <true/>
- <key>weight</key>
- <real>1000</real>
- </dict>
- <key>^Resources/.*\.lproj/locversion.plist$</key>
- <dict>
- <key>omit</key>
- <true/>
- <key>weight</key>
- <real>1100</real>
- </dict>
- <key>^version.plist$</key>
- <true/>
- </dict>
- <key>rules2</key>
- <dict>
- <key>.*\.dSYM($|/)</key>
- <dict>
- <key>weight</key>
- <real>11</real>
- </dict>
- <key>^(.*/)?\.DS_Store$</key>
- <dict>
- <key>omit</key>
- <true/>
- <key>weight</key>
- <real>2000</real>
- </dict>
- <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
- <dict>
- <key>nested</key>
- <true/>
- <key>weight</key>
- <real>10</real>
- </dict>
- <key>^.*</key>
- <true/>
- <key>^Info\.plist$</key>
- <dict>
- <key>omit</key>
- <true/>
- <key>weight</key>
- <real>20</real>
- </dict>
- <key>^PkgInfo$</key>
- <dict>
- <key>omit</key>
- <true/>
- <key>weight</key>
- <real>20</real>
- </dict>
- <key>^Resources/</key>
- <dict>
- <key>weight</key>
- <real>20</real>
- </dict>
- <key>^Resources/.*\.lproj/</key>
- <dict>
- <key>optional</key>
- <true/>
- <key>weight</key>
- <real>1000</real>
- </dict>
- <key>^Resources/.*\.lproj/locversion.plist$</key>
- <dict>
- <key>omit</key>
- <true/>
- <key>weight</key>
- <real>1100</real>
- </dict>
- <key>^[^/]+$</key>
- <dict>
- <key>nested</key>
- <true/>
- <key>weight</key>
- <real>10</real>
- </dict>
- <key>^embedded\.provisionprofile$</key>
- <dict>
- <key>weight</key>
- <real>20</real>
- </dict>
- <key>^version\.plist$</key>
- <dict>
- <key>weight</key>
- <real>20</real>
- </dict>
- </dict>
-</dict>
-</plist>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/LICENSE b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/LICENSE
deleted file mode 100644
index c7fd4a4a..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/LICENSE
+++ /dev/null
@@ -1,25 +0,0 @@
-MacGap was ported from phonegap-mac, and is under the same license (MIT)
-
-The MIT License
-*****************
-
-Copyright (c) <2012> <Nitobi Software Inc., et. al., >
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
-
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.pbxproj b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.pbxproj
deleted file mode 100644
index 775c5964..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,489 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 46;
- objects = {
-
-/* Begin PBXBuildFile section */
- 1495814F15C15CCC00E1CFE5 /* Notice.m in Sources */ = {isa = PBXBuildFile; fileRef = 1495814E15C15CCC00E1CFE5 /* Notice.m */; };
- 6F169DA718CC332E005EDDF3 /* Command.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F169DA618CC332E005EDDF3 /* Command.m */; };
- 6F169DAA18CC35FD005EDDF3 /* CallbackDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F169DA918CC35FD005EDDF3 /* CallbackDelegate.m */; };
- 6F169DAC18CD8A4A005EDDF3 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F169DAB18CD8A4A005EDDF3 /* JavaScriptCore.framework */; };
- 6F169DB118CD906F005EDDF3 /* MenuItemProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F169DAE18CD906F005EDDF3 /* MenuItemProxy.m */; };
- 6F169DB218CD906F005EDDF3 /* MenuProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F169DB018CD906F005EDDF3 /* MenuProxy.m */; };
- 6FD672B618FE618E00C0DAAD /* UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD672B518FE618E00C0DAAD /* UserDefaults.m */; };
- 6FD6E4ED18C2D48C00DFFBE6 /* fonts.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FD6E4EC18C2D48C00DFFBE6 /* fonts.m */; };
- 88746BEE14CCA435001E160E /* JSEventHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 88746BED14CCA435001E160E /* JSEventHelper.m */; };
- 88C0646014BDE10A00E4BCE2 /* Window.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C0645F14BDE10A00E4BCE2 /* Window.m */; };
- 88C0646614BDEC5800E4BCE2 /* Window.xib in Resources */ = {isa = PBXBuildFile; fileRef = 88C0646414BDEC5800E4BCE2 /* Window.xib */; };
- 88C0646D14BDF6A600E4BCE2 /* WindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 88C0646C14BDF6A600E4BCE2 /* WindowController.m */; };
- C14EFCA71B0986AF00894B5F /* ZeroTierIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = C14EFCA61B0986AF00894B5F /* ZeroTierIcon.icns */; };
- C1C2B9911AFB0CF10060D7C2 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1C2B9901AFB0CF10060D7C2 /* Security.framework */; };
- F2B80016179E0FC100B069A8 /* Clipboard.m in Sources */ = {isa = PBXBuildFile; fileRef = F2B80015179E0FC100B069A8 /* Clipboard.m */; };
- FA32509D14BA813600BF0781 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA32509C14BA813600BF0781 /* WebKit.framework */; };
- FA3250C314BA85E700BF0781 /* ContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250BC14BA85E700BF0781 /* ContentView.m */; };
- FA3250C514BA85E700BF0781 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250BE14BA85E700BF0781 /* Utils.m */; };
- FA3250C714BA85E700BF0781 /* WebViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250C014BA85E700BF0781 /* WebViewDelegate.m */; };
- FA3250D314BA860800BF0781 /* App.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250C914BA860800BF0781 /* App.m */; };
- FA3250D514BA860800BF0781 /* Dock.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250CB14BA860800BF0781 /* Dock.m */; };
- FA3250D914BA860800BF0781 /* Path.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250CF14BA860800BF0781 /* Path.m */; };
- FA3250DB14BA860800BF0781 /* Sound.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3250D114BA860800BF0781 /* Sound.m */; };
- FA3F7742168F70790027B324 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA3F7741168F70780027B324 /* Cocoa.framework */; };
- FAE451C914BA79C600190544 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FAE451C714BA79C600190544 /* InfoPlist.strings */; };
- FAE451CB14BA79C600190544 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FAE451CA14BA79C600190544 /* main.m */; };
- FAE451CF14BA79C600190544 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = FAE451CD14BA79C600190544 /* Credits.rtf */; };
- FAE451D214BA79C600190544 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FAE451D114BA79C600190544 /* AppDelegate.m */; };
- FAE451D514BA79C600190544 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = FAE451D314BA79C600190544 /* MainMenu.xib */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXCopyFilesBuildPhase section */
- FA3250DD14BA876F00BF0781 /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = "";
- dstSubfolderSpec = 10;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXCopyFilesBuildPhase section */
-
-/* Begin PBXFileReference section */
- 1495814D15C15CCC00E1CFE5 /* Notice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Notice.h; path = Classes/Commands/Notice.h; sourceTree = "<group>"; };
- 1495814E15C15CCC00E1CFE5 /* Notice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Notice.m; path = Classes/Commands/Notice.m; sourceTree = "<group>"; };
- 6F169DA518CC332E005EDDF3 /* Command.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Command.h; path = Classes/Commands/Command.h; sourceTree = "<group>"; };
- 6F169DA618CC332E005EDDF3 /* Command.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Command.m; path = Classes/Commands/Command.m; sourceTree = "<group>"; };
- 6F169DA818CC35FD005EDDF3 /* CallbackDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CallbackDelegate.h; path = Classes/CallbackDelegate.h; sourceTree = "<group>"; };
- 6F169DA918CC35FD005EDDF3 /* CallbackDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CallbackDelegate.m; path = Classes/CallbackDelegate.m; sourceTree = "<group>"; };
- 6F169DAB18CD8A4A005EDDF3 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
- 6F169DAD18CD906F005EDDF3 /* MenuItemProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MenuItemProxy.h; path = Classes/Commands/MenuItemProxy.h; sourceTree = "<group>"; };
- 6F169DAE18CD906F005EDDF3 /* MenuItemProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MenuItemProxy.m; path = Classes/Commands/MenuItemProxy.m; sourceTree = "<group>"; };
- 6F169DAF18CD906F005EDDF3 /* MenuProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MenuProxy.h; path = Classes/Commands/MenuProxy.h; sourceTree = "<group>"; };
- 6F169DB018CD906F005EDDF3 /* MenuProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MenuProxy.m; path = Classes/Commands/MenuProxy.m; sourceTree = "<group>"; };
- 6FD672B418FE618E00C0DAAD /* UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UserDefaults.h; path = Classes/Commands/UserDefaults.h; sourceTree = "<group>"; };
- 6FD672B518FE618E00C0DAAD /* UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = UserDefaults.m; path = Classes/Commands/UserDefaults.m; sourceTree = "<group>"; };
- 6FD6E4EB18C2D48200DFFBE6 /* fonts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = fonts.h; path = Classes/Commands/fonts.h; sourceTree = "<group>"; };
- 6FD6E4EC18C2D48C00DFFBE6 /* fonts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = fonts.m; path = Classes/Commands/fonts.m; sourceTree = "<group>"; };
- 88746BEC14CCA435001E160E /* JSEventHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSEventHelper.h; path = Classes/JSEventHelper.h; sourceTree = "<group>"; };
- 88746BED14CCA435001E160E /* JSEventHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JSEventHelper.m; path = Classes/JSEventHelper.m; sourceTree = "<group>"; };
- 88C0645E14BDE10A00E4BCE2 /* Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Window.h; path = Classes/Window.h; sourceTree = "<group>"; };
- 88C0645F14BDE10A00E4BCE2 /* Window.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Window.m; path = Classes/Window.m; sourceTree = "<group>"; };
- 88C0646514BDEC5800E4BCE2 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/Window.xib; sourceTree = "<group>"; };
- 88C0646B14BDF6A600E4BCE2 /* WindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowController.h; sourceTree = "<group>"; };
- 88C0646C14BDF6A600E4BCE2 /* WindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WindowController.m; sourceTree = "<group>"; };
- C14EFCA61B0986AF00894B5F /* ZeroTierIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = ZeroTierIcon.icns; path = ../../../../artwork/ZeroTierIcon.icns; sourceTree = "<group>"; };
- C1C2B9901AFB0CF10060D7C2 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
- F2B80014179E0FC100B069A8 /* Clipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Clipboard.h; sourceTree = "<group>"; };
- F2B80015179E0FC100B069A8 /* Clipboard.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Clipboard.m; sourceTree = "<group>"; };
- FA32509C14BA813600BF0781 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
- FA3250BA14BA85E700BF0781 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Constants.h; path = Classes/Constants.h; sourceTree = "<group>"; };
- FA3250BB14BA85E700BF0781 /* ContentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ContentView.h; path = Classes/ContentView.h; sourceTree = "<group>"; };
- FA3250BC14BA85E700BF0781 /* ContentView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ContentView.m; path = Classes/ContentView.m; sourceTree = "<group>"; };
- FA3250BD14BA85E700BF0781 /* Utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = Classes/Utils.h; sourceTree = "<group>"; };
- FA3250BE14BA85E700BF0781 /* Utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Utils.m; path = Classes/Utils.m; sourceTree = "<group>"; };
- FA3250BF14BA85E700BF0781 /* WebViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WebViewDelegate.h; path = Classes/WebViewDelegate.h; sourceTree = "<group>"; };
- FA3250C014BA85E700BF0781 /* WebViewDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = WebViewDelegate.m; path = Classes/WebViewDelegate.m; sourceTree = "<group>"; };
- FA3250C814BA860800BF0781 /* App.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = App.h; path = Classes/Commands/App.h; sourceTree = "<group>"; };
- FA3250C914BA860800BF0781 /* App.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = App.m; path = Classes/Commands/App.m; sourceTree = "<group>"; };
- FA3250CA14BA860800BF0781 /* Dock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Dock.h; path = Classes/Commands/Dock.h; sourceTree = "<group>"; };
- FA3250CB14BA860800BF0781 /* Dock.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Dock.m; path = Classes/Commands/Dock.m; sourceTree = "<group>"; };
- FA3250CE14BA860800BF0781 /* Path.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Path.h; path = Classes/Commands/Path.h; sourceTree = "<group>"; };
- FA3250CF14BA860800BF0781 /* Path.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Path.m; path = Classes/Commands/Path.m; sourceTree = "<group>"; };
- FA3250D014BA860800BF0781 /* Sound.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Sound.h; path = Classes/Commands/Sound.h; sourceTree = "<group>"; };
- FA3250D114BA860800BF0781 /* Sound.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Sound.m; path = Classes/Commands/Sound.m; sourceTree = "<group>"; };
- FA3F7741168F70780027B324 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
- FAE451BA14BA79C600190544 /* ZeroTier One.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZeroTier One.app"; sourceTree = BUILT_PRODUCTS_DIR; };
- FAE451BE14BA79C600190544 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
- FAE451C114BA79C600190544 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
- FAE451C214BA79C600190544 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
- FAE451C314BA79C600190544 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
- FAE451C614BA79C600190544 /* MacGap-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MacGap-Info.plist"; sourceTree = "<group>"; };
- FAE451C814BA79C600190544 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
- FAE451CA14BA79C600190544 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
- FAE451CC14BA79C600190544 /* MacGap-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MacGap-Prefix.pch"; sourceTree = "<group>"; };
- FAE451CE14BA79C600190544 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
- FAE451D014BA79C600190544 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
- FAE451D114BA79C600190544 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
- FAE451D414BA79C600190544 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- FAE451B714BA79C600190544 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- C1C2B9911AFB0CF10060D7C2 /* Security.framework in Frameworks */,
- 6F169DAC18CD8A4A005EDDF3 /* JavaScriptCore.framework in Frameworks */,
- FA3F7742168F70790027B324 /* Cocoa.framework in Frameworks */,
- FA32509D14BA813600BF0781 /* WebKit.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- FA3250E014BA87B800BF0781 /* Classes */ = {
- isa = PBXGroup;
- children = (
- FA3250E114BA87DD00BF0781 /* Commands */,
- FA3250BA14BA85E700BF0781 /* Constants.h */,
- 6F169DA818CC35FD005EDDF3 /* CallbackDelegate.h */,
- 6F169DA918CC35FD005EDDF3 /* CallbackDelegate.m */,
- FA3250BB14BA85E700BF0781 /* ContentView.h */,
- FA3250BC14BA85E700BF0781 /* ContentView.m */,
- FA3250BF14BA85E700BF0781 /* WebViewDelegate.h */,
- FA3250C014BA85E700BF0781 /* WebViewDelegate.m */,
- 88C0646B14BDF6A600E4BCE2 /* WindowController.h */,
- 88C0646C14BDF6A600E4BCE2 /* WindowController.m */,
- );
- name = Classes;
- sourceTree = "<group>";
- };
- FA3250E114BA87DD00BF0781 /* Commands */ = {
- isa = PBXGroup;
- children = (
- 6F169DA518CC332E005EDDF3 /* Command.h */,
- 6F169DA618CC332E005EDDF3 /* Command.m */,
- 1495814D15C15CCC00E1CFE5 /* Notice.h */,
- 1495814E15C15CCC00E1CFE5 /* Notice.m */,
- FA3250CA14BA860800BF0781 /* Dock.h */,
- FA3250CB14BA860800BF0781 /* Dock.m */,
- 6FD6E4EB18C2D48200DFFBE6 /* fonts.h */,
- 6FD6E4EC18C2D48C00DFFBE6 /* fonts.m */,
- FA3250BD14BA85E700BF0781 /* Utils.h */,
- FA3250BE14BA85E700BF0781 /* Utils.m */,
- 6FD672B418FE618E00C0DAAD /* UserDefaults.h */,
- 6FD672B518FE618E00C0DAAD /* UserDefaults.m */,
- FA3250CE14BA860800BF0781 /* Path.h */,
- FA3250CF14BA860800BF0781 /* Path.m */,
- FA3250D014BA860800BF0781 /* Sound.h */,
- FA3250D114BA860800BF0781 /* Sound.m */,
- FA3250C814BA860800BF0781 /* App.h */,
- FA3250C914BA860800BF0781 /* App.m */,
- 6F169DAD18CD906F005EDDF3 /* MenuItemProxy.h */,
- 6F169DAE18CD906F005EDDF3 /* MenuItemProxy.m */,
- 6F169DAF18CD906F005EDDF3 /* MenuProxy.h */,
- 6F169DB018CD906F005EDDF3 /* MenuProxy.m */,
- 88C0645E14BDE10A00E4BCE2 /* Window.h */,
- 88C0645F14BDE10A00E4BCE2 /* Window.m */,
- 88746BEC14CCA435001E160E /* JSEventHelper.h */,
- 88746BED14CCA435001E160E /* JSEventHelper.m */,
- F2B80014179E0FC100B069A8 /* Clipboard.h */,
- F2B80015179E0FC100B069A8 /* Clipboard.m */,
- );
- name = Commands;
- sourceTree = "<group>";
- };
- FAE451AF14BA79C600190544 = {
- isa = PBXGroup;
- children = (
- FA3F7741168F70780027B324 /* Cocoa.framework */,
- FAE451C414BA79C600190544 /* MacGap */,
- FAE451BD14BA79C600190544 /* Frameworks */,
- FAE451BB14BA79C600190544 /* Products */,
- );
- sourceTree = "<group>";
- };
- FAE451BB14BA79C600190544 /* Products */ = {
- isa = PBXGroup;
- children = (
- FAE451BA14BA79C600190544 /* ZeroTier One.app */,
- );
- name = Products;
- sourceTree = "<group>";
- };
- FAE451BD14BA79C600190544 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- C1C2B9901AFB0CF10060D7C2 /* Security.framework */,
- 6F169DAB18CD8A4A005EDDF3 /* JavaScriptCore.framework */,
- FA32509C14BA813600BF0781 /* WebKit.framework */,
- FAE451BE14BA79C600190544 /* Cocoa.framework */,
- FAE451C014BA79C600190544 /* Other Frameworks */,
- );
- name = Frameworks;
- sourceTree = "<group>";
- };
- FAE451C014BA79C600190544 /* Other Frameworks */ = {
- isa = PBXGroup;
- children = (
- FAE451C114BA79C600190544 /* AppKit.framework */,
- FAE451C214BA79C600190544 /* CoreData.framework */,
- FAE451C314BA79C600190544 /* Foundation.framework */,
- );
- name = "Other Frameworks";
- sourceTree = "<group>";
- };
- FAE451C414BA79C600190544 /* MacGap */ = {
- isa = PBXGroup;
- children = (
- FA3250E014BA87B800BF0781 /* Classes */,
- FAE451D014BA79C600190544 /* AppDelegate.h */,
- FAE451D114BA79C600190544 /* AppDelegate.m */,
- C14EFCA61B0986AF00894B5F /* ZeroTierIcon.icns */,
- FAE451D314BA79C600190544 /* MainMenu.xib */,
- 88C0646414BDEC5800E4BCE2 /* Window.xib */,
- FAE451C514BA79C600190544 /* Supporting Files */,
- );
- path = MacGap;
- sourceTree = "<group>";
- };
- FAE451C514BA79C600190544 /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- FAE451C614BA79C600190544 /* MacGap-Info.plist */,
- FAE451C714BA79C600190544 /* InfoPlist.strings */,
- FAE451CA14BA79C600190544 /* main.m */,
- FAE451CC14BA79C600190544 /* MacGap-Prefix.pch */,
- FAE451CD14BA79C600190544 /* Credits.rtf */,
- );
- name = "Supporting Files";
- sourceTree = "<group>";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- FAE451B914BA79C600190544 /* MacGap */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = FAE451D814BA79C600190544 /* Build configuration list for PBXNativeTarget "MacGap" */;
- buildPhases = (
- FAE451B814BA79C600190544 /* Resources */,
- FAE451B614BA79C600190544 /* Sources */,
- FAE451B714BA79C600190544 /* Frameworks */,
- FA3250DD14BA876F00BF0781 /* CopyFiles */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = MacGap;
- productName = MacGap;
- productReference = FAE451BA14BA79C600190544 /* ZeroTier One.app */;
- productType = "com.apple.product-type.application";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- FAE451B114BA79C600190544 /* Project object */ = {
- isa = PBXProject;
- attributes = {
- LastUpgradeCheck = 0710;
- ORGANIZATIONNAME = Twitter;
- };
- buildConfigurationList = FAE451B414BA79C600190544 /* Build configuration list for PBXProject "MacGap" */;
- compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
- hasScannedForEncodings = 0;
- knownRegions = (
- en,
- );
- mainGroup = FAE451AF14BA79C600190544;
- productRefGroup = FAE451BB14BA79C600190544 /* Products */;
- projectDirPath = "";
- projectRoot = "";
- targets = (
- FAE451B914BA79C600190544 /* MacGap */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
- FAE451B814BA79C600190544 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- C14EFCA71B0986AF00894B5F /* ZeroTierIcon.icns in Resources */,
- FAE451C914BA79C600190544 /* InfoPlist.strings in Resources */,
- FAE451CF14BA79C600190544 /* Credits.rtf in Resources */,
- FAE451D514BA79C600190544 /* MainMenu.xib in Resources */,
- 88C0646614BDEC5800E4BCE2 /* Window.xib in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
- FAE451B614BA79C600190544 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 6F169DAA18CC35FD005EDDF3 /* CallbackDelegate.m in Sources */,
- FA3250D314BA860800BF0781 /* App.m in Sources */,
- FA3250D514BA860800BF0781 /* Dock.m in Sources */,
- FA3250D914BA860800BF0781 /* Path.m in Sources */,
- FA3250DB14BA860800BF0781 /* Sound.m in Sources */,
- FA3250C314BA85E700BF0781 /* ContentView.m in Sources */,
- FA3250C514BA85E700BF0781 /* Utils.m in Sources */,
- FA3250C714BA85E700BF0781 /* WebViewDelegate.m in Sources */,
- FAE451CB14BA79C600190544 /* main.m in Sources */,
- 6F169DB118CD906F005EDDF3 /* MenuItemProxy.m in Sources */,
- FAE451D214BA79C600190544 /* AppDelegate.m in Sources */,
- 6F169DA718CC332E005EDDF3 /* Command.m in Sources */,
- 6FD672B618FE618E00C0DAAD /* UserDefaults.m in Sources */,
- 88C0646014BDE10A00E4BCE2 /* Window.m in Sources */,
- 6F169DB218CD906F005EDDF3 /* MenuProxy.m in Sources */,
- 88C0646D14BDF6A600E4BCE2 /* WindowController.m in Sources */,
- 6FD6E4ED18C2D48C00DFFBE6 /* fonts.m in Sources */,
- 88746BEE14CCA435001E160E /* JSEventHelper.m in Sources */,
- 1495814F15C15CCC00E1CFE5 /* Notice.m in Sources */,
- F2B80016179E0FC100B069A8 /* Clipboard.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXVariantGroup section */
- 88C0646414BDEC5800E4BCE2 /* Window.xib */ = {
- isa = PBXVariantGroup;
- children = (
- 88C0646514BDEC5800E4BCE2 /* en */,
- );
- name = Window.xib;
- sourceTree = "<group>";
- };
- FAE451C714BA79C600190544 /* InfoPlist.strings */ = {
- isa = PBXVariantGroup;
- children = (
- FAE451C814BA79C600190544 /* en */,
- );
- name = InfoPlist.strings;
- sourceTree = "<group>";
- };
- FAE451CD14BA79C600190544 /* Credits.rtf */ = {
- isa = PBXVariantGroup;
- children = (
- FAE451CE14BA79C600190544 /* en */,
- );
- name = Credits.rtf;
- sourceTree = "<group>";
- };
- FAE451D314BA79C600190544 /* MainMenu.xib */ = {
- isa = PBXVariantGroup;
- children = (
- FAE451D414BA79C600190544 /* en */,
- );
- name = MainMenu.xib;
- sourceTree = "<group>";
- };
-/* End PBXVariantGroup section */
-
-/* Begin XCBuildConfiguration section */
- FAE451D614BA79C600190544 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ENABLE_OBJC_ARC = YES;
- COPY_PHASE_STRIP = NO;
- ENABLE_TESTABILITY = YES;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_OBJC_EXCEPTIONS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- GCC_VERSION = "";
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.7;
- ONLY_ACTIVE_ARCH = YES;
- PRODUCT_NAME = "ZeroTier One";
- SDKROOT = "";
- };
- name = Debug;
- };
- FAE451D714BA79C600190544 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- CLANG_ENABLE_OBJC_ARC = YES;
- COPY_PHASE_STRIP = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_ENABLE_OBJC_EXCEPTIONS = YES;
- GCC_VERSION = "";
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.7;
- PRODUCT_NAME = "ZeroTier One";
- SDKROOT = "";
- };
- name = Release;
- };
- FAE451D914BA79C600190544 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "compiler-default";
- CLANG_CXX_LIBRARY = "compiler-default";
- COMBINE_HIDPI_IMAGES = YES;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "\"$(SRCROOT)/MacGap\"",
- );
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "MacGap/MacGap-Prefix.pch";
- GCC_VERSION = "";
- INFOPLIST_FILE = "MacGap/MacGap-Info.plist";
- MACOSX_DEPLOYMENT_TARGET = 10.7;
- PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.$(PRODUCT_NAME:rfc1034identifier)";
- PRODUCT_NAME = "ZeroTier One";
- SDKROOT = macosx;
- WRAPPER_EXTENSION = app;
- };
- name = Debug;
- };
- FAE451DA14BA79C600190544 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_CXX_LANGUAGE_STANDARD = "compiler-default";
- CLANG_CXX_LIBRARY = "compiler-default";
- COMBINE_HIDPI_IMAGES = YES;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "\"$(SRCROOT)/MacGap\"",
- );
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "MacGap/MacGap-Prefix.pch";
- GCC_VERSION = "";
- INFOPLIST_FILE = "MacGap/MacGap-Info.plist";
- MACOSX_DEPLOYMENT_TARGET = 10.7;
- PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.$(PRODUCT_NAME:rfc1034identifier)";
- PRODUCT_NAME = "ZeroTier One";
- SDKROOT = macosx;
- WRAPPER_EXTENSION = app;
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- FAE451B414BA79C600190544 /* Build configuration list for PBXProject "MacGap" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- FAE451D614BA79C600190544 /* Debug */,
- FAE451D714BA79C600190544 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- FAE451D814BA79C600190544 /* Build configuration list for PBXNativeTarget "MacGap" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- FAE451D914BA79C600190544 /* Debug */,
- FAE451DA14BA79C600190544 /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = FAE451B114BA79C600190544 /* Project object */;
-}
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcshareddata/MacGap.xccheckout b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcshareddata/MacGap.xccheckout
deleted file mode 100644
index 7fdde853..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcshareddata/MacGap.xccheckout
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>IDESourceControlProjectFavoriteDictionaryKey</key>
- <false/>
- <key>IDESourceControlProjectIdentifier</key>
- <string>4D486E78-E297-4CC3-AAAE-1A58EDAC87E6</string>
- <key>IDESourceControlProjectName</key>
- <string>MacGap</string>
- <key>IDESourceControlProjectOriginsDictionary</key>
- <dict>
- <key>ABA3617E9F0148F844A82502F0D808DE6591AA97</key>
- <string>http://adam.ierymenko@git.int.zerotier.com/zerotier/zerotierone</string>
- </dict>
- <key>IDESourceControlProjectPath</key>
- <string>ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj</string>
- <key>IDESourceControlProjectRelativeInstallPathDictionary</key>
- <dict>
- <key>ABA3617E9F0148F844A82502F0D808DE6591AA97</key>
- <string>../../../../..</string>
- </dict>
- <key>IDESourceControlProjectURL</key>
- <string>http://adam.ierymenko@git.int.zerotier.com/zerotier/zerotierone</string>
- <key>IDESourceControlProjectVersion</key>
- <integer>111</integer>
- <key>IDESourceControlProjectWCCIdentifier</key>
- <string>ABA3617E9F0148F844A82502F0D808DE6591AA97</string>
- <key>IDESourceControlProjectWCConfigurations</key>
- <array>
- <dict>
- <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
- <string>public.vcs.git</string>
- <key>IDESourceControlWCCIdentifierKey</key>
- <string>ABA3617E9F0148F844A82502F0D808DE6591AA97</string>
- <key>IDESourceControlWCCName</key>
- <string>ZeroTierOne</string>
- </dict>
- </array>
-</dict>
-</plist>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/Alex.xcuserdatad/UserInterfaceState.xcuserstate b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/Alex.xcuserdatad/UserInterfaceState.xcuserstate
deleted file mode 100644
index 20281812..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/Alex.xcuserdatad/UserInterfaceState.xcuserstate
+++ /dev/null
Binary files differ
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/api.xcuserdatad/WorkspaceSettings.xcsettings b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/api.xcuserdatad/WorkspaceSettings.xcsettings
deleted file mode 100644
index 659c8766..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/api.xcuserdatad/WorkspaceSettings.xcsettings
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
- <true/>
- <key>SnapshotAutomaticallyBeforeSignificantChanges</key>
- <true/>
-</dict>
-</plist>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/UserInterfaceState.xcuserstate b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/UserInterfaceState.xcuserstate
deleted file mode 100644
index 822ed3cb..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/UserInterfaceState.xcuserstate
+++ /dev/null
Binary files differ
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/WorkspaceSettings.xcsettings b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/WorkspaceSettings.xcsettings
deleted file mode 100644
index 6ff33e60..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/liamks.xcuserdatad/WorkspaceSettings.xcsettings
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>IDEWorkspaceUserSettings_HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges</key>
- <true/>
- <key>IDEWorkspaceUserSettings_SnapshotAutomaticallyBeforeSignificantChanges</key>
- <true/>
-</dict>
-</plist>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.h
deleted file mode 100644
index bf7370b5..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// AppDelegate.h
-// MacGap
-//
-// Created by Alex MacCaw on 08/01/2012.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import <Cocoa/Cocoa.h>
-#import "Classes/ContentView.h"
-
-#import "WindowController.h"
-
-@interface AppDelegate : NSObject <NSApplicationDelegate>
-
-@property (retain, nonatomic) WindowController *windowController;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.m
deleted file mode 100644
index 45923bb3..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/AppDelegate.m
+++ /dev/null
@@ -1,159 +0,0 @@
-//
-// AppDelegate.m
-// MacGap
-//
-// Created by Alex MacCaw on 08/01/2012.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "AppDelegate.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-
-@implementation AppDelegate
-
-@synthesize windowController;
-
-- (void) applicationWillFinishLaunching:(NSNotification *)aNotification
-{
-}
-
--(BOOL)applicationShouldHandleReopen:(NSApplication*)application
- hasVisibleWindows:(BOOL)visibleWindows{
- if(!visibleWindows){
- [self.windowController.window makeKeyAndOrderFront: nil];
- }
- return YES;
-}
-
-- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
- return YES;
-}
-
-- (void) applicationDidFinishLaunching:(NSNotification *)aNotification {
- char buf[16384],userAuthTokenPath[4096];
- struct stat systemAuthTokenStat,userAuthTokenStat;
-
- FILE *pf = fopen("/Library/Application Support/ZeroTier/One/zerotier-one.port","r");
- long port = 9993; // default
- if (pf) {
- long n = fread(buf,1,sizeof(buf)-1,pf);
- if (n > 0) {
- buf[n] = (char)0;
- port = strtol(buf,(char **)0,10);
- }
- fclose(pf);
- }
-
- char url[16384];
- memset(url,0,sizeof(url));
-
- const char *homeDir = getenv("HOME");
- if (homeDir) {
- snprintf(userAuthTokenPath,sizeof(userAuthTokenPath),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",homeDir);
-
- bool userAuthTokenOutOfDate = false;
- memset(&systemAuthTokenStat,0,sizeof(systemAuthTokenStat));
- memset(&userAuthTokenStat,0,sizeof(userAuthTokenStat));
- if (stat("/Library/Application Support/ZeroTier/One/authtoken.secret",&systemAuthTokenStat) == 0) {
- if (stat(userAuthTokenPath,&userAuthTokenStat) == 0) {
- if (userAuthTokenStat.st_mtimespec.tv_sec < systemAuthTokenStat.st_mtimespec.tv_sec)
- userAuthTokenOutOfDate = true;
- }
- }
-
- if (!userAuthTokenOutOfDate) {
- pf = fopen(userAuthTokenPath,"r");
- if (pf) {
- long n = fread(buf,1,sizeof(buf)-1,pf);
- if (n > 0) {
- buf[n] = (char)0;
- snprintf(url,sizeof(url),"http://127.0.0.1:%ld/index.html?authToken=%s",port,buf);
- }
- fclose(pf);
- }
- }
- }
-
- if (!url[0]) {
- // Create authorization reference
- OSStatus status;
- AuthorizationRef authorizationRef;
-
- // AuthorizationCreate and pass NULL as the initial
- // AuthorizationRights set so that the AuthorizationRef gets created
- // successfully, and then later call AuthorizationCopyRights to
- // determine or extend the allowable rights.
- // http://developer.apple.com/qa/qa2001/qa1172.html
- status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
- if (status != errAuthorizationSuccess)
- {
- NSLog(@"Error Creating Initial Authorization: %d", status);
- return;
- }
-
- // kAuthorizationRightExecute == "system.privilege.admin"
- AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0};
- AuthorizationRights rights = {1, &right};
- AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
- kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
-
- // Call AuthorizationCopyRights to determine or extend the allowable rights.
- status = AuthorizationCopyRights(authorizationRef, &rights, NULL, flags, NULL);
- if (status != errAuthorizationSuccess)
- {
- NSLog(@"Copy Rights Unsuccessful: %d", status);
- return;
- }
-
- // use rm tool with -rf
- char *tool = "/bin/cat";
- char *args[] = {"/Library/Application Support/ZeroTier/One/authtoken.secret", NULL};
- FILE *pipe = NULL;
-
- status = AuthorizationExecuteWithPrivileges(authorizationRef, tool, kAuthorizationFlagDefaults, args, &pipe);
- if (status != errAuthorizationSuccess)
- {
- NSLog(@"Error: %d", status);
- }
-
- if (pipe) {
- long n = (long)fread(buf,1,sizeof(buf)-1,pipe);
- if (n > 0) {
- buf[n] = (char)0;
- snprintf(url,sizeof(url),"http://127.0.0.1:%ld/index.html?authToken=%s",port,buf);
-
- if (homeDir) {
- snprintf(userAuthTokenPath,sizeof(userAuthTokenPath),"%s/Library/Application Support/ZeroTier",homeDir);
- mkdir(userAuthTokenPath,0755);
- snprintf(userAuthTokenPath,sizeof(userAuthTokenPath),"%s/Library/Application Support/ZeroTier/One",homeDir);
- mkdir(userAuthTokenPath,0755);
- snprintf(userAuthTokenPath,sizeof(userAuthTokenPath),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",homeDir);
- pf = fopen(userAuthTokenPath,"w");
- if (pf) {
- fwrite(buf,1,strlen(buf),pf);
- fclose(pf);
- chmod(userAuthTokenPath,0600);
- }
- }
- }
- fclose(pipe);
- }
-
- // The only way to guarantee that a credential acquired when you
- // request a right is not shared with other authorization instances is
- // to destroy the credential. To do so, call the AuthorizationFree
- // function with the flag kAuthorizationFlagDestroyRights.
- // http://developer.apple.com/documentation/Security/Conceptual/authorization_concepts/02authconcepts/chapter_2_section_7.html
- status = AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
- }
-
- NSString *urlStr = [[NSString alloc] initWithCString:url];
- self.windowController = [[WindowController alloc] initWithURL: urlStr];
- [self.windowController showWindow: [NSApplication sharedApplication].delegate];
- self.windowController.contentView.webView.alphaValue = 1.0;
- self.windowController.contentView.alphaValue = 1.0;
- [self.windowController showWindow:self];
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.h
deleted file mode 100755
index 0f31ee41..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.h
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// CallbackDelegate.h
-// MacGap
-//
-// Created by Joe Hildebrand on 1/10/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "Command.h"
-
-@interface CallbackDelegate : Command {
-}
-
-@property JSObjectRef callback;
-
-- (id) initWithContext:(JSContextRef)aContext forCallback:(WebScriptObject*)aCallback;
-- (id) call;
-- (id) callWithParams:(id)firstOrNil, ... NS_REQUIRES_NIL_TERMINATION;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.m
deleted file mode 100755
index 5ce8fbe3..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/CallbackDelegate.m
+++ /dev/null
@@ -1,168 +0,0 @@
-//
-// CallbackDelegate.m
-// MacGap
-//
-// Created by Joe Hildebrand on 1/10/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "CallbackDelegate.h"
-#import <JavaScriptCore/JavaScript.h>
-
-@implementation CallbackDelegate
-
-@synthesize callback;
-
-- (id) initWithContext:(JSContextRef)aContext forCallback:(WebScriptObject*)aCallback
-{
- if (!aCallback)
- return nil;
- if ([aCallback isKindOfClass:[WebUndefined class]])
- return nil;
-
- self = [super initWithContext:aContext];
- if (!self)
- return nil;
-
- callback = [aCallback JSObject];
- JSValueProtect(context, callback);
- return self;
-}
-
-- (void) dealloc
-{
- if (callback)
- {
- JSValueUnprotect(context, callback);
- callback = nil;
- }
-}
-
-- (id) objectFromValue:(JSValueRef)val
-{
- JSStringRef jstr;
- NSString *rets;
-
- switch(JSValueGetType(context, val))
- {
- case kJSTypeUndefined:
- case kJSTypeNull:
- return nil;
- case kJSTypeBoolean:
- return [NSNumber numberWithBool:JSValueToBoolean(context, val)];
- case kJSTypeNumber:
- return [NSNumber numberWithDouble:JSValueToNumber(context, val, NULL)];
- case kJSTypeString:
- jstr = JSValueToStringCopy(context, val, NULL);
- size_t sz = JSStringGetMaximumUTF8CStringSize(jstr);
- char *buf = (char*)malloc(sz);
- JSStringGetUTF8CString(jstr, buf, sz);
- rets = [NSString stringWithUTF8String:buf];
- free(buf);
- return rets;
- case kJSTypeObject:
- // TODO: dictionary or something
- return nil;
- default:
- NSAssert(false, @"Invalid JavaScript type");
- return nil;
- }
-}
-
-- (JSValueRef) valueFromObject:(id)obj
-{
- JSValueRef val = nil;
- if (!obj)
- {
- val = JSValueMakeNull(context);
- }
- else if ([obj isKindOfClass:[NSString class]])
- {
- JSStringRef jstr = JSStringCreateWithUTF8CString([obj UTF8String]);
- val = JSValueMakeString(context, jstr);
- JSStringRelease(jstr);
- }
- else if ([obj isKindOfClass:[NSNumber class]])
- {
- val = JSValueMakeNumber(context, [obj doubleValue]);
- }
- else if ([obj isKindOfClass:[NSDictionary class]])
- {
- JSObjectRef o = JSObjectMake(context, NULL, NULL);
- for (NSString *key in obj)
- {
- JSStringRef kstr = JSStringCreateWithUTF8CString([key UTF8String]);
- JSValueRef v = [self valueFromObject:[obj objectForKey:key]];
-
- JSObjectSetProperty(context, o, kstr, v, kJSPropertyAttributeNone, NULL);
- JSStringRelease(kstr);
- }
- val = o;
- }
- else if ([obj isKindOfClass:[NSArray class]])
- {
- NSUInteger pcount = [obj count];
- JSValueRef jsArgs[pcount];
- NSUInteger i=0;
- for (id v in obj)
- {
- jsArgs[i++] = [self valueFromObject:v];
- }
- val = JSObjectMakeArray(context, pcount, jsArgs, NULL);
- }
- else if ([obj isKindOfClass:[NSDate class]])
- {
- NSTimeInterval secs = [obj timeIntervalSince1970];
- JSValueRef jsArgs[1];
- // call the Date(milliseconds) constructor in JS
- jsArgs[0] = JSValueMakeNumber(context, secs * 1000.0);
- val = JSObjectMakeDate(context, 1, jsArgs, NULL);
- }
- else
- {
- NSLog(@"Warning: unknown object type for: %@", obj);
- val = JSValueMakeUndefined(context);
- }
- return val;
-}
-
-- (id) call
-{
- NSAssert(callback, @"Callback required");
- if (!JSObjectIsFunction(context, callback))
- return nil;
-
- JSValueRef jsArgs[0];
- JSValueRef ret = JSObjectCallAsFunction(context, callback, NULL, 0, jsArgs, NULL);
- return [self objectFromValue:ret];
-}
-
-- (id) callWithParams:(id)firstOrNil, ...
-{
- NSAssert(callback, @"Callback required");
- if (!JSObjectIsFunction(context, callback))
- return nil;
- NSUInteger pcount = 0;
- id p;
- va_list args;
- va_start(args, firstOrNil);
- for (p=firstOrNil; p; p=va_arg(args, id))
- {
- pcount++;
- }
- va_end(args);
-
- JSValueRef jsArgs[pcount];
- NSUInteger j = 0;
- va_start(args, firstOrNil);
- for (p=firstOrNil; p; p=va_arg(args, id))
- {
- jsArgs[j++] = [self valueFromObject:p];
- }
- va_end(args);
-
- JSValueRef ret = JSObjectCallAsFunction(context, callback, NULL, j, jsArgs, NULL);
- return [self objectFromValue:ret];
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h
deleted file mode 100644
index f65ba61e..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "WindowController.h"
-
-@interface App : NSObject {
-
-}
-
-@property (nonatomic, retain) WebView *webView;
-
-- (id) initWithWebView:(WebView *)view;
-
-- (void) terminate;
-- (void) activate;
-- (void) hide;
-- (void) unhide;
-- (void) beep;
-- (void) bounce;
-- (void) setCustomUserAgent:(NSString *)userAgentString;
-- (NSNumber*) systemIdleTime;
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m
deleted file mode 100644
index 6d47a17e..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m
+++ /dev/null
@@ -1,128 +0,0 @@
-#import "App.h"
-
-#import "JSEventHelper.h"
-
-@implementation App
-
-@synthesize webView;
-
-- (id) initWithWebView:(WebView *) view{
- self = [super init];
-
- if (self) {
- self.webView = view;
- [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
- selector: @selector(receiveSleepNotification:)
- name: NSWorkspaceWillSleepNotification object: NULL];
- [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
- selector: @selector(receiveWakeNotification:)
- name: NSWorkspaceDidWakeNotification object: NULL];
- [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
- selector: @selector(receiveActivateNotification:)
- name: NSWorkspaceDidActivateApplicationNotification object: NULL];
- }
-
- return self;
-}
-
-- (void) terminate {
- [NSApp terminate:nil];
-}
-
-- (void) activate {
- [NSApp activateIgnoringOtherApps:YES];
-}
-
-- (void) hide {
- [NSApp hide:nil];
-}
-
-- (void) unhide {
- [NSApp unhide:nil];
-}
-
-- (void)beep {
- NSBeep();
-}
-
-- (void) bounce {
- [NSApp requestUserAttention:NSInformationalRequest];
-}
-
-- (void)setCustomUserAgent:(NSString *)userAgentString {
- [self.webView setCustomUserAgent: userAgentString];
-}
-
-- (void) open:(NSString*)url {
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
-}
-
-- (void) launch:(NSString *)name {
- [[NSWorkspace sharedWorkspace] launchApplication:name];
-}
-
-- (void)receiveSleepNotification:(NSNotification*)note{
- [JSEventHelper triggerEvent:@"sleep" forWebView:self.webView];
-}
-
-- (void) receiveWakeNotification:(NSNotification*)note{
- [JSEventHelper triggerEvent:@"wake" forWebView:self.webView];
-}
-
-- (void) receiveActivateNotification:(NSNotification*)notification{
- NSDictionary* userInfo = [notification userInfo];
- NSRunningApplication* runningApplication = [userInfo objectForKey:NSWorkspaceApplicationKey];
- if (runningApplication) {
- NSMutableDictionary* applicationDidGetFocusDict = [[NSMutableDictionary alloc] initWithCapacity:2];
- [applicationDidGetFocusDict setObject:runningApplication.localizedName
- forKey:@"localizedName"];
- [applicationDidGetFocusDict setObject:[runningApplication.bundleURL absoluteString]
- forKey:@"bundleURL"];
-
- [JSEventHelper triggerEvent:@"appActivated" withArgs:applicationDidGetFocusDict forWebView:self.webView];
- }
-}
-
-
-
-
-/*
- To get the elapsed time since the previous input event—keyboard, mouse, or tablet—specify kCGAnyInputEventType.
- */
-- (NSNumber*)systemIdleTime {
- CFTimeInterval timeSinceLastEvent = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType);
-
- return [NSNumber numberWithDouble:timeSinceLastEvent];
-}
-
-
-
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector
-{
- id result = nil;
-
- if (selector == @selector(open:)) {
- result = @"open";
- } else if (selector == @selector(launch:)) {
- result = @"launch";
- } else if (selector == @selector(setCustomUserAgent:)) {
- result = @"setCustomUserAgent";
- } else if (selector == @selector(systemIdleTime)) {
- result = @"systemIdleTime";
- }
-
- return result;
-}
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return NO;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return YES;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h
deleted file mode 100755
index 65d6b6d4..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// Command.h
-// MacGap
-//
-// Created by Joe Hildebrand on 1/10/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-#import <Webkit/WebScriptObject.h>
-
-@interface Command : NSObject {
- JSContextRef context;
-}
-
-- (id) initWithContext:(JSContextRef)aContext;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m
deleted file mode 100755
index 39b85630..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// Command.m
-// MacGap
-//
-// Created by Joe Hildebrand on 1/10/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "Command.h"
-#import <JavaScriptCore/JSContextRef.h>
-
-@implementation Command
-
-- (id) initWithContext:(JSContextRef)aContext {
- self = [super init];
- if (!self)
- return nil;
- context = aContext;
- JSGlobalContextRetain((JSGlobalContextRef)context);
- return self;
-}
-
-- (void)dealloc
-{
- if (context)
- JSGlobalContextRelease((JSGlobalContextRef)context);
-}
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h
deleted file mode 100644
index b3c533d7..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#import <Foundation/Foundation.h>
-
-@interface Dock : NSObject {
-
-}
-- (void) setBadge:(NSString*)value;
-- (NSString *) badge;
-
-@property (readwrite, copy) NSString *badge;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m
deleted file mode 100644
index a4494d16..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m
+++ /dev/null
@@ -1,31 +0,0 @@
-#import "Dock.h"
-
-@implementation Dock
-
-@synthesize badge;
-
-- (void) setBadge:(NSString *)value
-{
- NSDockTile *tile = [[NSApplication sharedApplication] dockTile];
- [tile setBadgeLabel:value];
-}
-
-- (NSString *) badge
-{
- NSDockTile *tile = [[NSApplication sharedApplication] dockTile];
- return [tile badgeLabel];
-}
-
-#pragma mark WebScripting Protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return NO;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return NO;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h
deleted file mode 100755
index d765978f..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// MenuItemProxy.h
-// MacGap
-//
-// Created by Joe Hildebrand on 1/15/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "Command.h"
-#import "CallbackDelegate.h"
-
-@class MenuProxy;
-
-@interface MenuItemProxy : Command {
- NSMenuItem *item;
- CallbackDelegate *callback;
-}
-
-+ (MenuItemProxy*) proxyWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem;
-
-- (MenuProxy*)addSubmenu;
-
-- (void) remove;
-- (void) setCallback:(WebScriptObject*)aCallback;
-- (void) setKey:(NSString*)keyCommand;
-- (void) setTitle:(NSString*)title;
-- (void) enable;
-- (void) disable;
-- (MenuProxy*)submenu;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m
deleted file mode 100755
index 7b9702cc..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m
+++ /dev/null
@@ -1,150 +0,0 @@
-//
-// MenuItemProxy.m
-// MacGap
-//
-// Created by Joe Hildebrand on 1/15/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "MenuItemProxy.h"
-#import "MenuProxy.h"
-
-@implementation MenuItemProxy
-
-- (id) initWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem
-{
- NSAssert(anItem, @"anItem required");
- self = [super initWithContext:aContext];
- if (!self)
- return nil;
- item = anItem;
- item.representedObject = self;
-
- return self;
-}
-
-+ (MenuItemProxy*) proxyWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem
-{
- MenuItemProxy *proxy = [anItem representedObject];
- if (proxy)
- {
- NSLog(@"MIP Cache hit");
- NSAssert([proxy class] == [MenuItemProxy class], @"Bad proxy");
- return proxy;
- }
- return [[MenuItemProxy alloc] initWithContext:aContext andMenuItem:anItem];
-}
-
-- (NSString*) description
-{
- return [item description];
-}
-
-- (MenuProxy*)addSubmenu
-{
- NSMenu *s = [item submenu];
- if (!s)
- {
- s = [[NSMenu alloc] initWithTitle:@"FFFFFFOOOOO"];
- [item setSubmenu:s];
- }
- return [MenuProxy proxyWithContext:context andMenu:s];
-}
-
-- (void) remove
-{
- NSMenu *menu = [item menu];
- [menu removeItem:item];
-}
-
-- (void)callCallback:(id)sender
-{
- [callback callWithParams:[sender title], nil];
-}
-
-- (void) setCallback:(WebScriptObject*)aCallback
-{
- NSAssert(item, @"item required");
- callback = [[CallbackDelegate alloc] initWithContext:context forCallback:aCallback];
- [item setAction:@selector(callCallback:)];
- [item setTarget:self];
-}
-
-- (void)setKey:(NSString*)keyCommand
-{
- NSString *aKey = [MenuProxy getKeyFromString:keyCommand];
- [item setKeyEquivalent:aKey];
-
- NSUInteger modifiers = [MenuProxy getModifiersFromString:keyCommand];
- [item setKeyEquivalentModifierMask:modifiers];
-}
-
-- (void) setTitle:(NSString*)title
-{
- [item setTitle:title];
-}
-
-- (MenuProxy*)submenu;
-{
- // TODO: make this work as a property
- NSMenu *s = [item submenu];
- if (!s)
- return nil;
- return [MenuProxy proxyWithContext:context andMenu:s];
-}
-
-- (void) enable
-{
- [item setEnabled:YES];
-}
-
-- (void) disable
-{
- [item setEnabled:NO];
-}
-
-#pragma mark WebScripting protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return [self webScriptNameForSelector:selector] == nil;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return YES;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector
-{
- id result = nil;
-
- if (selector == @selector(addSubmenu)) {
- result = @"addSubmenu";
- }
- else if (selector == @selector(remove)) {
- result = @"remove";
- }
- else if (selector == @selector(setCallback:)) {
- result = @"setCallback";
- }
- else if (selector == @selector(setKey:)) {
- result = @"setKey";
- }
- else if (selector == @selector(setTitle:)) {
- result = @"setTitle";
- }
- else if (selector == @selector(submenu)) {
- result = @"submenu";
- }
- else if (selector == @selector(enable)) {
- result = @"enable";
- }
- else if (selector == @selector(disable)) {
- result = @"disable";
- }
-
- return result;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h
deleted file mode 100755
index afd6c6ed..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// MenuProxy.h
-// MacGap
-//
-// Created by Joe Hildebrand on 1/14/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "Command.h"
-
-@class MenuItemProxy;
-
-@interface MenuProxy : Command {
- NSMenu *menu;
-}
-
-+ (MenuProxy*)proxyWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu;
-
-- (MenuItemProxy*)addItemWithTitle:(NSString*)title
- keyEquivalent:(NSString*)aKey
- callback:(WebScriptObject*)aCallback
- atIndex:(NSInteger)index;
-
-- (MenuItemProxy*)addSeparator;
-- (MenuItemProxy*)itemForKey:(id)key;
-- (MenuProxy*)removeItem:(id)key;
-
-+ (NSString*)getKeyFromString:(NSString*)keyCommand;
-+ (NSUInteger*)getModifiersFromString:(NSString*)keyCommand;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m
deleted file mode 100755
index 5bc10a76..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m
+++ /dev/null
@@ -1,233 +0,0 @@
-//
-// MenuProxy.m
-// MacGap
-//
-// Created by Joe Hildebrand on 1/14/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import <objc/runtime.h>
-#import <JavaScriptCore/JavaScript.h>
-
-#import "MenuProxy.h"
-#import "MenuItemProxy.h"
-
-static char REPRESENTED_OBJECT;
-
-@interface NSMenu (represented)
-@property (strong) id representedObject;
-@end
-
-@implementation NSMenu (represented)
-
-- (id) representedObject
-{
- return objc_getAssociatedObject(self, &REPRESENTED_OBJECT);
-}
-
-- (void) setRepresentedObject:(id)representedObject
-{
- objc_setAssociatedObject(self,
- &REPRESENTED_OBJECT,
- representedObject,
- OBJC_ASSOCIATION_RETAIN);
-}
-
-@end
-
-@implementation MenuProxy
-
-- (id) initWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu
-{
- self = [super initWithContext:aContext];
- if (!self)
- return nil;
- menu = aMenu;
- menu.representedObject = self;
- return self;
-}
-
-+ (MenuProxy*)proxyWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu
-{
- // singleton-ish.
- MenuProxy *ret = [aMenu representedObject];
- if (ret)
- {
- NSLog(@"MP cache hit");
- return ret;
- }
- return [[MenuProxy alloc] initWithContext:aContext andMenu:aMenu];
-}
-
-- (void) dealloc
-{
- menu.representedObject = nil;
-}
-
-- (NSString*) description
-{
- return [menu description];
-}
-
-static BOOL isNullish(id o)
-{
- if (!o)
- return YES;
- if ([o isKindOfClass:[WebUndefined class]])
- return YES;
- return NO;
-}
-
-- (MenuItemProxy*)addItemWithTitle:(NSString*)title
- keyEquivalent:(NSString*)keyCommand
- callback:(WebScriptObject*)aCallback
- atIndex:(NSInteger)index
-{
- if (isNullish(title))
- title = @"";
-
- NSString *aKey = [MenuProxy getKeyFromString:keyCommand];
- NSMenuItem *item = nil;
-
- if(index) {
- item = [menu insertItemWithTitle:title action:nil keyEquivalent:aKey atIndex:index ];
- } else {
- item = [menu addItemWithTitle:title action:nil keyEquivalent:aKey ];
-
- }
-
- // Set the modifiers.
- NSUInteger modifiers = [MenuProxy getModifiersFromString:keyCommand];
- [item setKeyEquivalentModifierMask:modifiers];
-
- if(!menu.supermenu) {
- NSMenu *s = [[NSMenu alloc] initWithTitle:title];
- [item setSubmenu:s];
- }
-
- MenuItemProxy *mip = [MenuItemProxy proxyWithContext:context andMenuItem:item];
- if (!isNullish(aCallback))
- [mip setCallback:aCallback];
-
-
- return mip;
-}
-
-+ (NSString*)getKeyFromString:(NSString*)keyCommand {
- if (isNullish(keyCommand))
- keyCommand = @"";
-
- // Obtain the key (if there are modifiers, it will be the last character).
- NSString *aKey = @"";
- if ([keyCommand length] > 0) {
- aKey = [keyCommand substringFromIndex:[keyCommand length] - 1];
- }
-
- return aKey;
-}
-
-+ (NSUInteger*)getModifiersFromString:(NSString*)keyCommand {
- // aKeys may optionally specify one or more modifiers.
- NSUInteger modifiers = 0;
-
- if ([keyCommand rangeOfString:@"caps"].location != NSNotFound) modifiers += NSAlphaShiftKeyMask;
- if ([keyCommand rangeOfString:@"shift"].location != NSNotFound) modifiers += NSShiftKeyMask;
- if ([keyCommand rangeOfString:@"cmd"].location != NSNotFound) modifiers += NSCommandKeyMask;
- if ([keyCommand rangeOfString:@"ctrl"].location != NSNotFound) modifiers += NSControlKeyMask;
- if ([keyCommand rangeOfString:@"opt"].location != NSNotFound) modifiers += NSAlternateKeyMask;
- if ([keyCommand rangeOfString:@"alt"].location != NSNotFound) modifiers += NSAlternateKeyMask;
-
- return modifiers;
-}
-
-- (MenuItemProxy*)addSeparator
-{
- NSMenuItem *sep = [NSMenuItem separatorItem];
- [menu addItem:sep];
- return [MenuItemProxy proxyWithContext:context andMenuItem:sep];
-}
-
-- (MenuItemProxy*)itemForKey:(id)key
-{
- if (isNullish(key))
- return nil;
- NSMenuItem *item = nil;
- if ([key isKindOfClass:[NSNumber class]])
- {
- item = [menu itemAtIndex:[key intValue]];
- }
- else if ([key isKindOfClass:[NSString class]])
- {
- item = [menu itemWithTitle:key];
- if (!item)
- {
- // Try again, with ... appended. e.g. "Save..."
- item = [menu itemWithTitle:
- [key stringByAppendingString:@"\u2026"]];
- }
- }
- if (!item)
- return nil;
-
- return [MenuItemProxy proxyWithContext:context andMenuItem:item];
-}
-
-- (MenuProxy*)removeItem:(id)key
-{
- if (isNullish(key))
- return nil;
-
- NSMenuItem *item = nil;
- if ([key isKindOfClass:[NSNumber class]])
- {
- item = [menu itemAtIndex:[key intValue]];
- }
- else if ([key isKindOfClass:[NSString class]])
- {
- item = [menu itemWithTitle:key];
- if (!item)
- {
- // Try again, with ... appended. e.g. "Save..."
- item = [menu itemWithTitle:
- [key stringByAppendingString:@"\u2026"]];
- }
- }
- if (!item)
- return nil;
-
- [menu removeItem:item];
- return [MenuProxy proxyWithContext:context andMenu:menu];
-}
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return [self webScriptNameForSelector:selector] == nil;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return YES;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector
-{
- id result = nil;
-
- if (selector == @selector(addItemWithTitle:keyEquivalent:callback:atIndex:)) {
- result = @"addItem";
- }
- else if (selector == @selector(addSeparator)) {
- result = @"addSeparator";
- }
- else if (selector == @selector(itemForKey:)) {
- result = @"getItem";
- }
- else if (selector == @selector(removeItem:)) {
- result = @"removeMenu";
- }
-
- return result;
-}
-
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h
deleted file mode 100644
index 51077a43..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// Notice.h
-// MacGap
-//
-// Created by Christian Sullivan on 7/26/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-#import "WindowController.h"
-
-#define APP_NOTICE_NOTIFICATION @"Notice"
-
-@interface Notice : NSObject <NSUserNotificationCenterDelegate> {
-
-}
-
-@property (nonatomic, retain) WebView *webView;
-
-- (id) initWithWebView:(WebView *)view;
-- (void) notify:(NSDictionary*)message;
-- (void) close:(NSString*)notificationId;
-+ (BOOL) available;
-
-@end
-
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m
deleted file mode 100644
index a4095f9f..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m
+++ /dev/null
@@ -1,108 +0,0 @@
-//
-// Notice.m
-// MacGap
-//
-// Created by Christian Sullivan on 7/26/12.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "Notice.h"
-
-#import "JSEventHelper.h"
-
-@implementation Notice
-
-- (id) initWithWebView:(WebView*)view
-{
- if(self = [super init]) {
- self.webView = view;
- [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
- }
- return self;
-}
-
-- (void) notify:(NSDictionary *)message {
- NSUserNotification *notification = [[NSUserNotification alloc] init];
- [notification setTitle:[message valueForKey:@"title"]];
- [notification setInformativeText:[message valueForKey:@"content"]];
- [notification setDeliveryDate:[NSDate dateWithTimeInterval:0 sinceDate:[NSDate date]]];
- BOOL playSound = true; // optional parameter, false only when {sound: false}
- @try {
- NSNumber *s = [message valueForKey:@"sound"];
- if ([[s className] isEqual: @"__NSCFBoolean"]) {
- playSound = [s boolValue];
- }
- }
- @catch (NSException *exception) {
- }
- if (playSound) {
- [notification setSoundName:NSUserNotificationDefaultSoundName];
- }
- NSString *id = @""; // optional, needed for close
- @try {
- id = [message valueForKey:@"id"];
- }
- @catch (NSException *exception) {
- }
- [notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:id, @"id", nil]];
- NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
- [center scheduleNotification:notification];
-}
-
-// close all notifications with id == notificationId or close all notifications if notificationId == "*"
-- (void) close:(NSString*)notificationId {
- NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
- for(NSUserNotification * deliveredNote in center.deliveredNotifications) {
- if ([notificationId isEqualToString:@"*"] || [deliveredNote.userInfo[@"id"] isEqualToString:notificationId]) {
- [center removeDeliveredNotification: deliveredNote];
- }
- }
-}
-
-+ (BOOL) available {
- if ([NSUserNotificationCenter respondsToSelector:@selector(defaultUserNotificationCenter)])
- return YES;
-
- return NO;
-}
-
-- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
-{
- NSString *notificationId = [notification.userInfo valueForKey:@"id"];
- [JSEventHelper triggerEvent:@"macgap.notify.activated" forDetail:notificationId forWebView:self.webView];
-}
-
-#pragma mark WebScripting Protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- BOOL result = YES;
- if (selector == @selector(notify:))
- result = NO;
- if (selector == @selector(close:))
- result = NO;
-
- return result;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector
-{
- id result = nil;
-
- if (selector == @selector(notify:)) {
- result = @"notify";
- }
- if (selector == @selector(close:)) {
- result = @"close";
- }
-
- return result;
-}
-
-// right now exclude all properties (eg keys)
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return YES;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h
deleted file mode 100644
index f931340d..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#import <Foundation/Foundation.h>
-
-@interface Path : NSObject {
-
-}
-
-- (NSString *) application;
-- (NSString *) resource;
-- (NSString *) documents;
-- (NSString *) library;
-- (NSString *) home;
-- (NSString *) temp;
-
-@property (readonly,copy) NSString* application;
-@property (readonly,copy) NSString* resource;
-@property (readonly,copy) NSString* documents;
-@property (readonly,copy) NSString* library;
-@property (readonly,copy) NSString* home;
-@property (readonly,copy) NSString* temp;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m
deleted file mode 100644
index 8c54100f..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m
+++ /dev/null
@@ -1,53 +0,0 @@
-#import "Path.h"
-
-@implementation Path
-
-@synthesize application;
-@synthesize resource;
-@synthesize documents;
-@synthesize library;
-@synthesize home;
-@synthesize temp;
-
-- (NSString *)application {
- return [[NSBundle mainBundle] bundlePath];
-}
-
-- (NSString *)resource {
- return [[NSBundle mainBundle] resourcePath];
-}
-
-- (NSString *)documents {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- return [paths objectAtIndex:0];
-}
-
-- (NSString *)library {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
- NSLog( @"%@", paths );
- return [paths objectAtIndex:0];
-}
-
-- (NSString *)home {
- return NSHomeDirectory();
-}
-
-- (NSString *)temp {
- return NSTemporaryDirectory();
-}
-
-#pragma mark WebScripting Protocol
-
-/* checks whether a selector is acceptable to be called from JavaScript */
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return NO;
-}
-
-// right now exclude all properties (eg keys)
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return NO;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h
deleted file mode 100644
index 06707643..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#import <Cocoa/Cocoa.h>
-#import "Command.h"
-#import "CallbackDelegate.h"
-
-
-@interface Sound : Command {
-
-}
-
-// pending callbacks for sounds being played, to keep
-// ARC from freeing them too early
-@property (nonatomic, strong) NSMutableSet *pending;
-
-- (void) play:(NSString*)file onComplete:(WebScriptObject*)callback;
-- (void) playSystem:(NSString*)name onComplete:(WebScriptObject*)callback;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m
deleted file mode 100644
index 9f4a44db..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m
+++ /dev/null
@@ -1,97 +0,0 @@
-#import "Sound.h"
-
-
-@interface PlayDelegate : CallbackDelegate <NSSoundDelegate> {
-}
-
-@property (nonatomic, weak) Sound *sound;
-
-- (id) initWithContext:(JSContextRef)aContext
- forCallback:(WebScriptObject*)aCallback
- withSound:(Sound*)aSound;
-@end
-
-@implementation PlayDelegate
-
-@synthesize sound;
-
-- (id) initWithContext:(JSContextRef)aContext
- forCallback:(WebScriptObject*)aCallback
- withSound:(Sound*)aSound
-{
- self = [super initWithContext:aContext forCallback:aCallback];
- if (!self)
- return nil;
- sound = aSound;
- return self;
-}
-
-- (void)sound:(NSSound *)aSound didFinishPlaying:(BOOL)finishedPlaying {
- [self callWithParams:[aSound name], nil];
- [sound.pending removeObject:self];
-}
-
-@end
-
-@implementation Sound
-
-@synthesize pending;
-
-- (id) initWithContext:(JSContextRef)aContext {
- self = [super initWithContext:aContext];
- if (!self) {
- return nil;
- }
-
- pending = [NSMutableSet new];
- return self;
-}
-
-- (void) playSound:(NSSound*)sound onComplete:(WebScriptObject*)callback {
- if (callback != (id)[WebUndefined undefined]) {
- PlayDelegate *d = [[PlayDelegate alloc] initWithContext:context
- forCallback:callback
- withSound:self];
- [pending addObject:d];
- [sound setDelegate:d];
- }
- [sound play];
-}
-
-- (void) play:(NSString*)file onComplete:(WebScriptObject*)callback {
- NSURL* fileUrl = [NSURL fileURLWithPath:[[Utils sharedInstance] pathForResource:file]];
- DebugNSLog(@"Sound file:%@", [fileUrl description]);
-
- NSSound* sound = [[NSSound alloc] initWithContentsOfURL:fileUrl byReference:YES];
- [self playSound:sound onComplete:callback];
-}
-
-- (void) playSystem:(NSString*)name onComplete:(WebScriptObject*)callback {
- NSSound *systemSound = [NSSound soundNamed:name];
- [self playSound:systemSound onComplete:callback];
-}
-
-#pragma mark WebScripting Protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
- return [self webScriptNameForSelector:selector] == nil;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name {
- return YES;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector {
- id result = nil;
-
- if (selector == @selector(play:onComplete:)) {
- result = @"play";
- }
- else if (selector == @selector(playSystem:onComplete:)) {
- result = @"playSystem";
- }
-
- return result;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h
deleted file mode 100644
index 269191b3..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// UserDefaults.h
-// MacGap
-//
-// Created by Jeff Hanbury on 16/04/2014.
-// Copyright (c) 2014 Twitter. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-
-#import "WindowController.h"
-
-@interface UserDefaults : NSObject
-
-@property (nonatomic, retain) WebView *webView;
-
-- (id) initWithWebView:(WebView *)view;
-- (NSString*) getMyDefaults;
-- (NSDictionary*) myDefaultsDictionary;
-- (void) removeObjectForKey:(NSString*)key;
-- (NSArray*) getUserDefaultsKeys;
-
-- (NSString*) addPrefix:(NSString*)key;
-
-- (void) setString:(NSString*)key withValue:(NSString*)value;
-- (NSString*) getString:(NSString*)key;
-
-- (void) setInteger:(NSString*)key withValue:(NSString*)value;
-- (NSNumber*) getInteger:(NSString*)key;
-
-- (void) setBool:(NSString*)key withValue:(NSString*)value;
-- (NSNumber*) getBool:(NSString*)key;
-
-- (void) setFloat:(NSString*)key withValue:(NSString*)value;
-- (NSNumber*) getFloat:(NSString*)key;
-
-// Could also be implemented:
-//– setObject:forKey:
-//– setDouble:forKey:
-//– setURL:forKey:
-
-@end
-
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m
deleted file mode 100644
index 48568710..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m
+++ /dev/null
@@ -1,211 +0,0 @@
-//
-// UserDefaults.m
-// MacGap
-//
-// Created by Jeff Hanbury on 16/04/2014.
-// Copyright (c) 2014 Twitter. All rights reserved.
-//
-
-#import "UserDefaults.h"
-#import "JSEventHelper.h"
-
-@interface UserDefaults() {
-
-}
-
--(void) setupNotificationCenter;
-
-@end
-
-
-@implementation UserDefaults
-
-- (id) initWithWebView:(WebView *) view{
- self = [super init];
-
- if (self) {
- self.webView = view;
- [self setupNotificationCenter];
- }
-
- return self;
-}
-
-
--(void) setupNotificationCenter{
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(defaultsChanged:)
- name:NSUserDefaultsDidChangeNotification
- object:nil];
-}
-
-- (void)defaultsChanged:(NSNotification *)notification {
- NSDictionary* returnDict = [self myDefaultsDictionary];
- [JSEventHelper triggerEvent:@"userDefaultsChanged" withArgs:returnDict forWebView:self.webView];
-}
-
-- (NSString*) getMyDefaults {
- NSDictionary* myDefaults = [self myDefaultsDictionary];
-
- return [[Utils sharedInstance] convertDictionaryToJSON:myDefaults];
-}
-
-- (NSDictionary*) myDefaultsDictionary {
- NSString* prefix = [kWebScriptNamespace stringByAppendingString:@"_"];
- NSMutableDictionary* returnDict = [[NSMutableDictionary alloc] init];
-
- // Get the user defaults.
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
-
- // Build up a dictionary containing just the items beginning with our
- // prefix.
- for (NSString* key in [self getUserDefaultsKeys]) {
- if ([key hasPrefix:prefix]) {
- id val = [defaults valueForKey:key];
- [returnDict setObject:val forKey:key];
- }
- }
-
- return returnDict;
-}
-
-- (NSArray*) getUserDefaultsKeys {
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- return [[prefs dictionaryRepresentation] allKeys];
-}
-
-- (void) removeObjectForKey:(NSString*)key {
- NSString* prefixedKey;
- prefixedKey = [self addPrefix:key];
-
- [[NSUserDefaults standardUserDefaults] removeObjectForKey:prefixedKey];
- [[NSUserDefaults standardUserDefaults] synchronize];
-}
-
-// Check we have a standard prefix for JS-modified keys, for security purposes.
-// If not, add it. This stops JavaScript from ever being able to modify keys
-// it did not create.
-- (NSString*) addPrefix:(NSString*)key {
- NSString* prefix;
- prefix = [kWebScriptNamespace stringByAppendingString:@"_"];
-
- if (![key hasPrefix:prefix]) {
- key = [prefix stringByAppendingString:key];
- }
- return key;
-}
-
-// String
-
-- (void) setString:(NSString*)key withValue:(NSString*)value {
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- NSString* prefixedKey;
- prefixedKey = [self addPrefix:key];
- [prefs setObject:value forKey:prefixedKey];
-}
-
-- (NSString*) getString:(NSString *)key {
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- return [prefs stringForKey:key];
-}
-
-// All the following must convert their type to NSNumber for JavaScript.
-
-// Integer
-
-- (void) setInteger:(NSString*)key withValue:(NSString*)value {
- NSString* prefixedKey;
- prefixedKey = [self addPrefix:key];
-
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- NSInteger myInt = [value intValue];
- [prefs setInteger:myInt forKey:prefixedKey];
-}
-
-- (NSNumber*) getInteger:(NSString *)key {
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- return [NSNumber numberWithInteger:[prefs integerForKey:key]];
-}
-
-// Boolean
-
-- (void) setBool:(NSString*)key withValue:(NSString*)value {
- NSString* prefixedKey;
- prefixedKey = [self addPrefix:key];
-
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- BOOL myBool = [value boolValue];
- [prefs setBool:myBool forKey:prefixedKey];
-}
-
-- (NSNumber*) getBool:(NSString *)key {
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- return [NSNumber numberWithBool:[prefs boolForKey:key]];
-}
-
-// Float
-
-- (void) setFloat:(NSString*)key withValue:(NSString*)value {
- NSString* prefixedKey;
- prefixedKey = [self addPrefix:key];
-
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- float myFloat = [value floatValue];
- [prefs setFloat:myFloat forKey:prefixedKey];
-}
-
-- (NSNumber*) getFloat:(NSString *)key {
- NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
- return [NSNumber numberWithFloat:[prefs floatForKey:key]];
-}
-
-
-#pragma mark WebScripting Protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
- return NO;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector {
- id result = nil;
-
- if (selector == @selector(getMyDefaults)) {
- result = @"getMyDefaults";
- }
-
- if (selector == @selector(removeObjectForKey:)) {
- result = @"removeObjectForKey";
- }
-
- else if (selector == @selector(setString:withValue:)) {
- result = @"setString";
- } else if (selector == @selector(getString:)) {
- result = @"getString";
- }
-
- else if (selector == @selector(setInteger:withValue:)) {
- result = @"setInteger";
- } else if (selector == @selector(getInteger:)) {
- result = @"getInteger";
- }
-
- else if (selector == @selector(setBool:withValue:)) {
- result = @"setBool";
- } else if (selector == @selector(getBool:)) {
- result = @"getBool";
- }
-
- else if (selector == @selector(setFloat:withValue:)) {
- result = @"setFloat";
- } else if (selector == @selector(getFloat:)) {
- result = @"getFloat";
- }
-
- return result;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name {
- return NO;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h
deleted file mode 100644
index 62c7b7e8..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h
+++ /dev/null
@@ -1,9 +0,0 @@
-@interface Fonts : NSObject {
-}
-
-- (NSArray*) availableFonts;
-- (NSArray*) availableFontFamilies;
-- (NSArray*) availableMembersOfFontFamily:(NSString*)fontFamily;
-- (CGFloat) defaultLineHeightForFont:(NSString *)theFontName ofSize:(CGFloat)theFontSize;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m
deleted file mode 100644
index b17818a5..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m
+++ /dev/null
@@ -1,48 +0,0 @@
-#import "fonts.h"
-
-@implementation Fonts
-
-
-- (NSArray*) availableFonts {
- return [[NSFontManager sharedFontManager] availableFonts];
-}
-
-- (NSArray*) availableFontFamilies {
- return [[NSFontManager sharedFontManager] availableFontFamilies];
-}
-
-- (NSArray*) availableMembersOfFontFamily:(NSString *)fontFamily {
- return [[NSFontManager sharedFontManager] availableMembersOfFontFamily:fontFamily];
-}
-
-- (CGFloat) defaultLineHeightForFont:(NSString*)theFontName ofSize:(CGFloat)theFontSize {
- NSFont *theFont = [NSFont fontWithName:theFontName size:theFontSize];
- NSLayoutManager *lm = [[NSLayoutManager alloc] init];
-
- return [lm defaultLineHeightForFont:theFont];
-}
-
-
-#pragma mark WebScripting Protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
- return NO;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector {
- id result = nil;
-
- if (selector == @selector(availableMembersOfFontFamily:)) {
- result = @"availableMembersOfFontFamily";
- } else if (selector == @selector(defaultLineHeightForFont:ofSize:)) {
- result = @"defaultLineHeightForFont";
- }
-
- return result;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name {
- return NO;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Constants.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Constants.h
deleted file mode 100644
index 1fe59d6c..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Constants.h
+++ /dev/null
@@ -1,7 +0,0 @@
-// Application constants
-
-#define kStartPage @"http://127.0.0.1:9993/"
-
-#define kStartFolder @"."
-
-#define kWebScriptNamespace @"macgap" \ No newline at end of file
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.h
deleted file mode 100644
index 65890a5e..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#import <Cocoa/Cocoa.h>
-#import <WebKit/WebKit.h>
-
-@class WebViewDelegate;
-
-@interface ContentView : NSView {
- IBOutlet WebView* webView;
- WebViewDelegate* delegate;
-}
-
-@property (retain) WebView* webView;
-@property (retain) WebViewDelegate* delegate;
-@property (strong) IBOutlet NSMenu *mainMenu;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.m
deleted file mode 100644
index 6558a191..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/ContentView.m
+++ /dev/null
@@ -1,68 +0,0 @@
-#import "ContentView.h"
-#import "WebViewDelegate.h"
-#import "AppDelegate.h"
-#import "JSEventHelper.h"
-
-@interface WebPreferences (WebPreferencesPrivate)
- - (void)_setLocalStorageDatabasePath:(NSString *)path;
- - (void) setLocalStorageEnabled: (BOOL) localStorageEnabled;
- - (void) setDatabasesEnabled:(BOOL)databasesEnabled;
- - (void) setDeveloperExtrasEnabled:(BOOL)developerExtrasEnabled;
- - (void) setWebGLEnabled:(BOOL)webGLEnabled;
- - (void) setOfflineWebApplicationCacheEnabled:(BOOL)offlineWebApplicationCacheEnabled;
-@end
-
-@implementation ContentView
-
-@synthesize webView, delegate, mainMenu;
-
-- (void) awakeFromNib
-{
- WebPreferences *webPrefs = [WebPreferences standardPreferences];
-
- NSString *cappBundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- NSString *applicationSupportFile = [@"~/Library/Application Support/" stringByExpandingTildeInPath];
- NSString *savePath = [NSString pathWithComponents:[NSArray arrayWithObjects:applicationSupportFile, cappBundleName, @"LocalStorage", nil]];
- [webPrefs _setLocalStorageDatabasePath:savePath];
- [webPrefs setLocalStorageEnabled:YES];
- [webPrefs setDatabasesEnabled:YES];
- [webPrefs setDeveloperExtrasEnabled:[[NSUserDefaults standardUserDefaults] boolForKey: @"developer"]];
- [webPrefs setOfflineWebApplicationCacheEnabled:YES];
- [webPrefs setWebGLEnabled:YES];
-
- [self.webView setPreferences:webPrefs];
-
- NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage
- sharedHTTPCookieStorage];
- [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
-
- [self.webView setApplicationNameForUserAgent: @"MacGap"];
-
- self.delegate = [[WebViewDelegate alloc] initWithMenu:[NSApp mainMenu]];
-// [self.webView setFrameLoadDelegate:self.delegate];
-// [self.webView setUIDelegate:self.delegate];
-// [self.webView setResourceLoadDelegate:self.delegate];
-// [self.webView setDownloadDelegate:self.delegate];
-// [self.webView setPolicyDelegate:self.delegate];
- [self.webView setDrawsBackground:NO];
- [self.webView setShouldCloseWithWindow:NO];
-
- [self.webView setGroupName:@"MacGap"];
-
-}
-
-- (void) windowResized:(NSNotification*)notification;
-{
- NSWindow* window = (NSWindow*)notification.object;
- NSSize size = [window frame].size;
-
- DebugNSLog(@"window width = %f, window height = %f", size.width, size.height);
-
- bool isFullScreen = (window.styleMask & NSFullScreenWindowMask) == NSFullScreenWindowMask;
- int titleBarHeight = isFullScreen ? 0 : [[Utils sharedInstance] titleBarHeight:window];
-
- [self.webView setFrame:NSMakeRect(0, 0, size.width, size.height - titleBarHeight)];
- [JSEventHelper triggerEvent:@"orientationchange" forWebView:self.webView];
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.h
deleted file mode 100644
index 401f3e39..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.h
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// Helper.h
-// MacGap
-//
-// Created by Liam Kaufman Simpkins on 12-01-22.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-#import "WindowController.h"
-
-@interface JSEventHelper : NSObject
-
-+ (void) triggerEvent:(NSString *)event forWebView:(WebView *)webView;
-+ (void) triggerEvent:(NSString *)event withArgs:(NSDictionary *)args forWebView:(WebView *)webView;
-+ (void) triggerEvent:(NSString *)event withArgs:(NSDictionary *)args forObject:(NSString *)objName forWebView:(WebView *)webView;
-+ (void) triggerEvent:(NSString *)event forDetail:(NSString *)detail forWebView:(WebView *)webView;
-+ (void) triggerEvent:(NSString *)event forDetail:(NSString *)detail forObject:(NSString *)objName forWebView:(WebView *)webView;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.m
deleted file mode 100644
index 65406b3c..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/JSEventHelper.m
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// Helper.m
-// MacGap
-//
-// Created by Liam Kaufman Simpkins on 12-01-22.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import "JSEventHelper.h"
-
-@implementation JSEventHelper
-
-+ (void) triggerEvent:(NSString *)event forWebView:(WebView *)webView {
- [self triggerEvent:event withArgs:[NSMutableDictionary dictionary] forObject:@"document" forWebView:webView];
-}
-
-+ (void) triggerEvent:(NSString *)event withArgs:(NSDictionary *)args forWebView:(WebView *)webView {
- [self triggerEvent:event withArgs:args forObject:@"document" forWebView:webView];
-}
-
-+ (void) triggerEvent:(NSString *)event withArgs:(NSDictionary *)args forObject:(NSString *)objName forWebView:(WebView *)webView {
-
- // Convert args Dictionary to JSON.
- NSString* jsonString = [[Utils sharedInstance] convertDictionaryToJSON:args];
-
- // Create the event JavaScript and run it.
- NSString * str = [NSString stringWithFormat:@"var e = document.createEvent('Events'); e.initEvent('%@', true, false); e.data=%@; %@.dispatchEvent(e); ", event, jsonString, objName];
- [webView stringByEvaluatingJavaScriptFromString:str];
-}
-
-+ (void) triggerEvent:(NSString *)event forDetail:(NSString *)detail forWebView:(WebView *)webView {
- [self triggerEvent:event forDetail:detail forObject:@"document" forWebView:webView];
-}
-
-+ (void) triggerEvent:(NSString *)event forDetail:(NSString *)detail forObject:(NSString *)objName forWebView:(WebView *)webView {
- NSString *detailEscaped = [detail stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
- NSString *str = [NSString stringWithFormat:@"var e = new CustomEvent('%@', { 'detail': decodeURIComponent(\"%@\") }); %@.dispatchEvent(e); ", event, detailEscaped, objName];
- [webView stringByEvaluatingJavaScriptFromString:str];
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.h
deleted file mode 100644
index f573d881..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <Webkit/WebScriptObject.h>
-
-#define DEG_EPS 0.001
-#define fequal(a,b) (fabs((a) - (b)) < DEG_EPS)
-#define fequalzero(a) (fabs(a) < DEG_EPS)
-
-@class LoadingView;
-
-@interface Utils : NSObject {
-}
-
-- (float) titleBarHeight:(NSWindow*)aWindow;
-- (NSString*) pathForResource:(NSString*)resourcepath;
-- (NSString*) convertDictionaryToJSON:(NSDictionary*)dict;
-- (NSArray*) convertJSarrayToNSArray:(WebScriptObject*)jsArray;
-
-+ (Utils*) sharedInstance;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.m
deleted file mode 100644
index 8d85c294..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Utils.m
+++ /dev/null
@@ -1,93 +0,0 @@
-#import "Utils.h"
-#import <Webkit/WebScriptObject.h>
-
-static Utils* sharedInstance = nil;
-
-@implementation Utils
-
-- (float) titleBarHeight:(NSWindow*)aWindow
-{
- NSRect frame = [aWindow frame];
- NSRect contentRect = [NSWindow contentRectForFrameRect: frame
- styleMask: NSTitledWindowMask];
-
- return (frame.size.height - contentRect.size.height);
-}
-
-- (NSString*) pathForResource:(NSString*)resourcepath
-{
- NSBundle * mainBundle = [NSBundle mainBundle];
- NSMutableArray *directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]];
- NSString *filename = [directoryParts lastObject];
- [directoryParts removeLastObject];
-
- NSString *directoryStr = [NSString stringWithFormat:@"%@/%@", kStartFolder, [directoryParts componentsJoinedByString:@"/"]];
- return [mainBundle pathForResource:filename
- ofType:@""
- inDirectory:directoryStr];
-}
-
-- (NSString*) convertDictionaryToJSON:(NSDictionary*)dict {
- // Convert defaults Dictionary to JSON.
- NSError *error;
- NSData *jsonData = [NSJSONSerialization
- dataWithJSONObject:dict
- options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
- error:&error];
-
- NSString *jsonString;
- if (! jsonData) {
- NSLog(@"Got an error converting to JSON: %@", error);
- }
- else {
- jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
- }
-
- return jsonString;
-}
-
-// Convert JavaScript array (arrives as a WebScriptObject) into an NSArray of strings.
-- (NSArray*) convertJSarrayToNSArray:(WebScriptObject*)jsArray {
- NSInteger count = [[jsArray valueForKey:@"length"] integerValue];
-
- NSMutableArray *args = [NSMutableArray array];
- for (int i = 0; i < count; i++) {
- NSString *item = [jsArray webScriptValueAtIndex:i];
- if ([item isKindOfClass:[NSString class]]) {
- [args addObject:item];
- }
- }
-
- return args;
-}
-
-#pragma mark -
-#pragma mark Singleton methods
-
-+ (Utils*) sharedInstance
-{
- @synchronized(self)
- {
- if (sharedInstance == nil){
- sharedInstance = [[Utils alloc] init];
- }
- }
- return sharedInstance;
-}
-
-+ (id) allocWithZone:(NSZone *)zone {
- @synchronized(self) {
- if (sharedInstance == nil) {
- sharedInstance = [super allocWithZone:zone];
- return sharedInstance; // assignment and return on first allocation
- }
- }
- return nil; // on subsequent allocation attempts return nil
-}
-
-- (id) copyWithZone:(NSZone *)zone
-{
- return self;
-}
-
-@end \ No newline at end of file
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.h
deleted file mode 100644
index 49c6da6b..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#import <Cocoa/Cocoa.h>
-#import <WebKit/WebKit.h>
-
-@class Sound;
-@class Dock;
-@class Growl;
-@class Notice;
-@class Path;
-@class App;
-@class Window;
-@class Clipboard;
-@class Fonts;
-@class MenuProxy;
-@class UserDefaults;
-
-@class WindowController;
-
-@interface WebViewDelegate : NSObject {
- Sound* sound;
- Dock* dock;
- Growl* growl;
- Notice* notice;
- Path* path;
- App* app;
- Window* window;
- Clipboard* clipboard;
- Fonts* fonts;
- NSMenu *mainMenu;
- UserDefaults* userDefaults;
-}
-
-
-
-@property (nonatomic, retain) Sound* sound;
-@property (nonatomic, retain) Dock* dock;
-@property (nonatomic, retain) Growl* growl;
-@property (nonatomic, retain) Notice* notice;
-@property (nonatomic, retain) Path* path;
-@property (nonatomic, retain) App* app;
-@property (nonatomic, retain) Window* window;
-@property (nonatomic, retain) Clipboard* clipboard;
-@property (nonatomic, retain) Fonts* fonts;
-@property (nonatomic, retain) MenuProxy* menu;
-@property (nonatomic, retain) UserDefaults* userDefaults;
-
-@property (nonatomic, retain) WindowController *requestedWindow;
-
-- (id) initWithMenu:(NSMenu*)menu;
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.m
deleted file mode 100644
index 50578018..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/WebViewDelegate.m
+++ /dev/null
@@ -1,206 +0,0 @@
-#import "WebViewDelegate.h"
-#import "Sound.h"
-#import "Dock.h"
-#import "Notice.h"
-#import "Path.h"
-#import "App.h"
-#import "Window.h"
-#import "WindowController.h"
-#import "Clipboard.h"
-#import "Fonts.h"
-#import "MenuProxy.h"
-#import "UserDefaults.h"
-
-@implementation WebViewDelegate
-
-@synthesize sound;
-@synthesize dock;
-@synthesize growl;
-@synthesize notice;
-@synthesize path;
-@synthesize app;
-@synthesize window;
-@synthesize requestedWindow;
-@synthesize clipboard;
-@synthesize fonts;
-@synthesize menu;
-@synthesize userDefaults;
-
-- (id) initWithMenu:(NSMenu*)aMenu
-{
- self = [super init];
- if (!self)
- return nil;
-
- mainMenu = aMenu;
- return self;
-}
-
-- (void) webView:(WebView*)webView didClearWindowObject:(WebScriptObject*)windowScriptObject forFrame:(WebFrame *)frame
-{
- JSContextRef context = [frame globalContext];
- if (self.sound == nil) { self.sound = [[Sound alloc] initWithContext:context]; }
- if (self.dock == nil) { self.dock = [Dock new]; }
- if (self.path == nil) { self.path = [Path new]; }
- if (self.clipboard == nil) { self.clipboard = [Clipboard new]; }
- if (self.fonts == nil) { self.fonts = [Fonts new]; }
-
- if (self.notice == nil && [Notice available] == YES) {
- self.notice = [[Notice alloc] initWithWebView:webView];
- }
-
- if (self.app == nil) {
- self.app = [[App alloc] initWithWebView:webView];
- }
-
- if (self.window == nil) {
- self.window = [[Window alloc] initWithWebView:webView];
- }
-
- if (self.menu == nil) {
- self.menu = [MenuProxy proxyWithContext:context andMenu:mainMenu];
- }
-
- if (self.userDefaults == nil) {
- self.userDefaults = [[UserDefaults alloc] initWithWebView:webView];
- }
-
- [windowScriptObject setValue:self forKey:kWebScriptNamespace];
-}
-
-
-- (void)webView:(WebView *)sender runOpenPanelForFileButtonWithResultListener:(id < WebOpenPanelResultListener >)resultListener allowMultipleFiles:(BOOL)allowMultipleFiles{
-
- NSOpenPanel * openDlg = [NSOpenPanel openPanel];
-
- [openDlg setCanChooseFiles:YES];
- [openDlg setCanChooseDirectories:NO];
-
- [openDlg beginWithCompletionHandler:^(NSInteger result){
- if (result == NSFileHandlingPanelOKButton) {
- NSArray * files = [[openDlg URLs] valueForKey: @"relativePath"];
- [resultListener chooseFilenames: files];
- } else {
- [resultListener cancel];
- }
- }];
-}
-
-- (void) webView:(WebView*)webView addMessageToConsole:(NSDictionary*)message
-{
- if (![message isKindOfClass:[NSDictionary class]]) {
- return;
- }
-
- NSLog(@"JavaScript console: %@:%@: %@",
- [[message objectForKey:@"sourceURL"] lastPathComponent], // could be nil
- [message objectForKey:@"lineNumber"],
- [message objectForKey:@"message"]);
-}
-
-- (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame
-{
- NSAlert *alert = [[NSAlert alloc] init];
- [alert addButtonWithTitle:@"OK"];
- [alert setMessageText:message];
- [alert setAlertStyle:NSWarningAlertStyle];
- [alert runModal];
-}
-
-- (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame
-{
- NSAlert *alert = [[NSAlert alloc] init];
- [alert addButtonWithTitle:@"Yes"];
- [alert addButtonWithTitle:@"No"];
- [alert setMessageText:message];
- [alert setAlertStyle:NSWarningAlertStyle];
-
- if ([alert runModal] == NSAlertFirstButtonReturn)
- return YES;
- else
- return NO;
-}
-
-/*
- By default the size of a database is set to 0 [1]. When a database is being created
- it calls this delegate method to get an increase in quota size - or call an error.
- PS this method is defined in WebUIDelegatePrivate and may make it difficult, but
- not impossible [2], to get an app accepted into the mac app store.
-
- Further reading:
- [1] http://stackoverflow.com/questions/353808/implementing-a-webview-database-quota-delegate
- [2] http://stackoverflow.com/questions/4527905/how-do-i-enable-local-storage-in-my-webkit-based-application/4608549#4608549
- */
-- (void)webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(id) origin database:(NSString *)databaseIdentifier
-{
- static const unsigned long long defaultQuota = 5 * 1024 * 1024;
- if ([origin respondsToSelector: @selector(setQuota:)]) {
- [origin performSelector:@selector(setQuota:) withObject:[NSNumber numberWithLongLong: defaultQuota]];
- } else {
- NSLog(@"could not increase quota for %lld", defaultQuota);
- }
-}
-
-- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
-{
- NSMutableArray *webViewMenuItems = [defaultMenuItems mutableCopy];
-
- if (webViewMenuItems)
- {
- NSEnumerator *itemEnumerator = [defaultMenuItems objectEnumerator];
- NSMenuItem *menuItem = nil;
- while ((menuItem = [itemEnumerator nextObject]))
- {
- NSInteger tag = [menuItem tag];
-
- switch (tag)
- {
- case WebMenuItemTagOpenLinkInNewWindow:
- case WebMenuItemTagDownloadLinkToDisk:
- case WebMenuItemTagCopyLinkToClipboard:
- case WebMenuItemTagOpenImageInNewWindow:
- case WebMenuItemTagDownloadImageToDisk:
- case WebMenuItemTagCopyImageToClipboard:
- case WebMenuItemTagOpenFrameInNewWindow:
- case WebMenuItemTagGoBack:
- case WebMenuItemTagGoForward:
- case WebMenuItemTagStop:
- case WebMenuItemTagOpenWithDefaultApplication:
- case WebMenuItemTagReload:
- [webViewMenuItems removeObjectIdenticalTo: menuItem];
- }
- }
- }
-
- return webViewMenuItems;
-}
-
-- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request{
- requestedWindow = [[WindowController alloc] initWithRequest:request];
- return requestedWindow.contentView.webView;
-}
-
-- (void)webViewShow:(WebView *)sender{
- [requestedWindow showWindow:sender];
-}
-
-- (void)webView:(WebView *)webView decidePolicyForNewWindowAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request newFrameName:(NSString *)frameName decisionListener:(id < WebPolicyDecisionListener >)listener
-{
- [[NSWorkspace sharedWorkspace] openURL:[request URL]];
- [listener ignore];
-}
-
-#pragma mark WebScripting protocol
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return YES;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return NO;
-}
-
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.h
deleted file mode 100644
index f721376e..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "WindowController.h"
-
-@interface Window : NSObject{
- CGRect _oldRestoreFrame;
-}
-
-@property (retain, nonatomic) WindowController *windowController;
-@property (nonatomic, retain) WebView *webView;
-
-- (id) initWithWebView:(WebView *)view;
-- (void) open:(NSDictionary *)properties;
-- (void) move:(NSDictionary *)properties;
-- (void) resize:(NSDictionary *) properties;
-- (Boolean) isMaximized;
-- (CGFloat) getX;
-- (CGFloat) getY;
-- (void) maximize;
-- (void) restore;
-- (void) toggleFullscreen;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.m
deleted file mode 100644
index 2444f62e..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Classes/Window.m
+++ /dev/null
@@ -1,94 +0,0 @@
-#import "Window.h"
-
-@implementation Window
-
-@synthesize windowController, webView;
-
-- (id) initWithWebView:(WebView*)view
-{
- if(self = [super init]) {
- self.webView = view;
- }
- return self;
-}
-
-- (void) open:(NSDictionary *)properties
-{
- self.windowController = [[WindowController alloc] initWithURL:[properties valueForKey:@"url"]];
- [self.windowController showWindow: [NSApplication sharedApplication].delegate];
- [self.windowController.window makeKeyWindow];
-}
-
-- (void) minimize {
- [[NSApp mainWindow] miniaturize:[NSApp mainWindow]];
-}
-
-- (void) toggleFullscreen {
- [[NSApp mainWindow] toggleFullScreen:[NSApp mainWindow]];
-}
-
-- (void) maximize {
- CGRect a = [NSApp mainWindow].frame;
- _oldRestoreFrame = CGRectMake(a.origin.x, a.origin.y, a.size.width, a.size.height);
- [[NSApp mainWindow] setFrame:[[NSScreen mainScreen] visibleFrame] display:YES];
-}
-
-- (Boolean) isMaximized {
- NSRect a = [NSApp mainWindow].frame;
- NSRect b = [[NSScreen mainScreen] visibleFrame];
- return a.origin.x == b.origin.x && a.origin.y == b.origin.y && a.size.width == b.size.width && a.size.height == b.size.height;
-}
-
-- (CGFloat) getX {
- NSRect frame = [self.webView window].frame;
- return frame.origin.x;
-}
-
-- (CGFloat) getY {
- NSRect frame = [self.webView window].frame;
- return frame.origin.y;
-}
-
-- (void) move:(NSDictionary *)properties
-{
- NSRect frame = [self.webView window].frame;
- frame.origin.x = [[properties valueForKey:@"x"] doubleValue];
- frame.origin.y = [[properties valueForKey:@"y"] doubleValue];
- [[self.webView window] setFrame:frame display:YES];
-
-}
-
-- (void) resize:(NSDictionary *) properties
-{
- NSRect frame = [self.webView window].frame;
- frame.size.width = [[properties valueForKey:@"width"] doubleValue];
- frame.size.height = [[properties valueForKey:@"height"] doubleValue];
- [[self.webView window] setFrame:frame display:YES];
-}
-
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return NO;
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector{
- id result = nil;
-
- if (selector == @selector(open:)) {
- result = @"open";
- }else if (selector == @selector(move:)){
- result = @"move";
- }else if (selector == @selector(resize:)){
- result = @"resize";
- }
-
- return result;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return YES;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.h
deleted file mode 100644
index 6c1a2f51..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#import <Foundation/Foundation.h>
-
-@interface Clipboard : NSObject {
-
-}
-
-- (void) copy:(NSString*)text;
-- (NSString *) paste;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.m
deleted file mode 100644
index 1c18dea3..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/Clipboard.m
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// clipboard.m
-// MacGap
-//
-// Created by David Zorychta on 2013-07-22.
-// Copyright (c) 2013 Twitter. All rights reserved.
-//
-
-#import "Clipboard.h"
-
-@implementation Clipboard
-
-- (void) copy:(NSString*)text {
- [[NSPasteboard generalPasteboard] clearContents];
- [[NSPasteboard generalPasteboard] setString:text forType:NSStringPboardType];
-}
-
-- (NSString *) paste {
- NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
- NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
- NSDictionary *options = [NSDictionary dictionary];
- BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
- if (ok) {
- NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
- return (NSString *) [objectsToPaste objectAtIndex:0];
- }
- return @"";
-}
-
-+ (NSString*) webScriptNameForSelector:(SEL)selector
-{
- id result = nil;
-
- if (selector == @selector(copy:)) {
- result = @"copy";
- }
-
- return result;
-}
-
-+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector
-{
- return NO;
-}
-
-+ (BOOL) isKeyExcludedFromWebScript:(const char*)name
-{
- return YES;
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Prefix.pch b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Prefix.pch
deleted file mode 100644
index ad05e842..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Prefix.pch
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Prefix header for all source files of the 'MacGap' target in the 'MacGap' project
-//
-
-#ifdef __OBJC__
- #ifdef _DEBUG
- #define DebugNSLog(format, ...) NSLog(format, ## __VA_ARGS__)
- #else
- #define DebugNSLog(format, ...)
- #endif
-
- #import <Cocoa/Cocoa.h>
- #import "Constants.h"
- #import "Utils.h"
-#endif
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.h b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.h
deleted file mode 100644
index 72927eff..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#import <Cocoa/Cocoa.h>
-#import "ContentView.h"
-
-@interface WindowController : NSWindowController {
-
-}
-
-- (id) initWithURL:(NSString *) url;
-- (id) initWithRequest: (NSURLRequest *)request;
-@property (retain) NSURL * url;
-@property (retain) IBOutlet ContentView *contentView;
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.m
deleted file mode 100644
index 2765a2e3..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/WindowController.m
+++ /dev/null
@@ -1,54 +0,0 @@
-#import "WindowController.h"
-
-
-@interface WindowController() {
-
-}
-
--(void) notificationCenter;
-
-@end
-
-@implementation WindowController
-
-@synthesize contentView, url;
-
-- (id) initWithURL:(NSString *) relativeURL{
- self = [super initWithWindowNibName:@"Window"];
- self.url = [NSURL URLWithString:relativeURL relativeToURL:[[NSBundle mainBundle] resourceURL]];
-
- [self.window setFrameAutosaveName:@"MacGapWindow"];
- [self notificationCenter];
-
- return self;
-}
-
--(id) initWithRequest: (NSURLRequest *)request{
- self = [super initWithWindowNibName:@"Window"];
- [self notificationCenter];
- [[self.contentView.webView mainFrame] loadRequest:request];
-
- return self;
-}
-
--(void) notificationCenter{
- [[NSNotificationCenter defaultCenter] addObserver:self.contentView
- selector:@selector(windowResized:)
- name:NSWindowDidResizeNotification
- object:[self window]];
-}
-
-- (void)windowDidLoad
-{
- [super windowDidLoad];
-
- if (self.url != nil) {
- [self.contentView.webView setMainFrameURL:[self.url absoluteString]];
- }
-
-
- // Implement this method to handle any initialization after your
- // window controller's window has been loaded from its nib file.
-}
-
-@end
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Credits.rtf b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Credits.rtf
deleted file mode 100644
index 6f388f66..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Credits.rtf
+++ /dev/null
@@ -1,13 +0,0 @@
-{\rtf1\ansi\ansicpg1252\cocoartf1347\cocoasubrtf570
-{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
-{\colortbl;\red255\green255\blue255;}
-\vieww9600\viewh8400\viewkind0
-\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
-
-\f0\b\fs24 \cf0 (c)2011-2015 ZeroTier, Inc.\
-Licensed under the GNU GPLv3\
-\
-UI Wrapper MacGap (c) Twitter, Inc.\
-Licensed under the MIT License\
-http://macgap.com/\
-} \ No newline at end of file
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/InfoPlist.strings b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/InfoPlist.strings
deleted file mode 100644
index 477b28ff..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,2 +0,0 @@
-/* Localized versions of Info.plist keys */
-
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/MainMenu.xib b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/MainMenu.xib
deleted file mode 100644
index dd67a86a..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/MainMenu.xib
+++ /dev/null
@@ -1,3404 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
- <data>
- <int key="IBDocument.SystemTarget">1070</int>
- <string key="IBDocument.SystemVersion">14D136</string>
- <string key="IBDocument.InterfaceBuilderVersion">7702</string>
- <string key="IBDocument.AppKitVersion">1347.57</string>
- <string key="IBDocument.HIToolboxVersion">758.70</string>
- <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
- <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="NS.object.0">7702</string>
- </object>
- <array key="IBDocument.IntegratedClassDependencies">
- <string>NSCustomObject</string>
- <string>NSMenu</string>
- <string>NSMenuItem</string>
- </array>
- <array key="IBDocument.PluginDependencies">
- <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- </array>
- <object class="NSMutableDictionary" key="IBDocument.Metadata">
- <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
- <integer value="1" key="NS.object.0"/>
- </object>
- <array class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
- <object class="NSCustomObject" id="1021">
- <string key="NSClassName">NSApplication</string>
- </object>
- <object class="NSCustomObject" id="1014">
- <string key="NSClassName">FirstResponder</string>
- </object>
- <object class="NSCustomObject" id="1050">
- <string key="NSClassName">NSApplication</string>
- </object>
- <object class="NSCustomObject" id="976324537">
- <string key="NSClassName">AppDelegate</string>
- </object>
- <object class="NSMenu" id="649796088">
- <string key="NSTitle">AMainMenu</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="694149608">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">ZeroTier One</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <object class="NSCustomResource" key="NSOnImage" id="35465992">
- <string key="NSClassName">NSImage</string>
- <string key="NSResourceName">NSMenuCheckmark</string>
- </object>
- <object class="NSCustomResource" key="NSMixedImage" id="502551668">
- <string key="NSClassName">NSImage</string>
- <string key="NSResourceName">NSMenuMixedState</string>
- </object>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="110575045"/>
- <object class="NSMenu" key="NSSubmenu" id="110575045">
- <string key="NSTitle">ZeroTier One</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="238522557">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">About ZeroTier One</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="304266470">
- <reference key="NSMenu" ref="110575045"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="609285721">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">Preferences…</string>
- <string key="NSKeyEquiv">,</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="481834944">
- <reference key="NSMenu" ref="110575045"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1046388886">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">Services</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="752062318"/>
- <object class="NSMenu" key="NSSubmenu" id="752062318">
- <string key="NSTitle">Services</string>
- <array class="NSMutableArray" key="NSMenuItems"/>
- <string key="NSName">_NSServicesMenu</string>
- </object>
- </object>
- <object class="NSMenuItem" id="646227648">
- <reference key="NSMenu" ref="110575045"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="755159360">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">Hide ZeroTier One</string>
- <string key="NSKeyEquiv">h</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="342932134">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">Hide Others</string>
- <string key="NSKeyEquiv">h</string>
- <int key="NSKeyEquivModMask">1572864</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="908899353">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">Show All</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1056857174">
- <reference key="NSMenu" ref="110575045"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="632727374">
- <reference key="NSMenu" ref="110575045"/>
- <string key="NSTitle">Quit ZeroTier One</string>
- <string key="NSKeyEquiv">q</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- <string key="NSName">_NSAppleMenu</string>
- </object>
- </object>
- <object class="NSMenuItem" id="379814623">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">File</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="720053764"/>
- <object class="NSMenu" key="NSSubmenu" id="720053764">
- <string key="NSTitle">File</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="705341025">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">New</string>
- <string key="NSKeyEquiv">n</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="722745758">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Open…</string>
- <string key="NSKeyEquiv">o</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1025936716">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Open Recent</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="1065607017"/>
- <object class="NSMenu" key="NSSubmenu" id="1065607017">
- <string key="NSTitle">Open Recent</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="759406840">
- <reference key="NSMenu" ref="1065607017"/>
- <string key="NSTitle">Clear Menu</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- <string key="NSName">_NSRecentDocumentsMenu</string>
- </object>
- </object>
- <object class="NSMenuItem" id="425164168">
- <reference key="NSMenu" ref="720053764"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="776162233">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Close</string>
- <string key="NSKeyEquiv">w</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1023925487">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Save…</string>
- <string key="NSKeyEquiv">s</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="579971712">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Revert to Saved</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1010469920">
- <reference key="NSMenu" ref="720053764"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="294629803">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Page Setup...</string>
- <string key="NSKeyEquiv">P</string>
- <int key="NSKeyEquivModMask">1179648</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSToolTip"/>
- </object>
- <object class="NSMenuItem" id="49223823">
- <reference key="NSMenu" ref="720053764"/>
- <string key="NSTitle">Print…</string>
- <string key="NSKeyEquiv">p</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="952259628">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">Edit</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="789758025"/>
- <object class="NSMenu" key="NSSubmenu" id="789758025">
- <string key="NSTitle">Edit</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="1058277027">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Undo</string>
- <string key="NSKeyEquiv">z</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="790794224">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Redo</string>
- <string key="NSKeyEquiv">Z</string>
- <int key="NSKeyEquivModMask">1179648</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1040322652">
- <reference key="NSMenu" ref="789758025"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="296257095">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Cut</string>
- <string key="NSKeyEquiv">x</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="860595796">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Copy</string>
- <string key="NSKeyEquiv">c</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="29853731">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Paste</string>
- <string key="NSKeyEquiv">v</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="82994268">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Paste and Match Style</string>
- <string key="NSKeyEquiv">V</string>
- <int key="NSKeyEquivModMask">1572864</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="437104165">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Delete</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="583158037">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Select All</string>
- <string key="NSKeyEquiv">a</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="212016141">
- <reference key="NSMenu" ref="789758025"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="892235320">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Find</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="963351320"/>
- <object class="NSMenu" key="NSSubmenu" id="963351320">
- <string key="NSTitle">Find</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="447796847">
- <reference key="NSMenu" ref="963351320"/>
- <string key="NSTitle">Find…</string>
- <string key="NSKeyEquiv">f</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">1</int>
- </object>
- <object class="NSMenuItem" id="738670835">
- <reference key="NSMenu" ref="963351320"/>
- <string key="NSTitle">Find and Replace…</string>
- <string key="NSKeyEquiv">f</string>
- <int key="NSKeyEquivModMask">1572864</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">12</int>
- </object>
- <object class="NSMenuItem" id="326711663">
- <reference key="NSMenu" ref="963351320"/>
- <string key="NSTitle">Find Next</string>
- <string key="NSKeyEquiv">g</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">2</int>
- </object>
- <object class="NSMenuItem" id="270902937">
- <reference key="NSMenu" ref="963351320"/>
- <string key="NSTitle">Find Previous</string>
- <string key="NSKeyEquiv">G</string>
- <int key="NSKeyEquivModMask">1179648</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">3</int>
- </object>
- <object class="NSMenuItem" id="159080638">
- <reference key="NSMenu" ref="963351320"/>
- <string key="NSTitle">Use Selection for Find</string>
- <string key="NSKeyEquiv">e</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">7</int>
- </object>
- <object class="NSMenuItem" id="88285865">
- <reference key="NSMenu" ref="963351320"/>
- <string key="NSTitle">Jump to Selection</string>
- <string key="NSKeyEquiv">j</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="972420730">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Spelling and Grammar</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="769623530"/>
- <object class="NSMenu" key="NSSubmenu" id="769623530">
- <string key="NSTitle">Spelling and Grammar</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="679648819">
- <reference key="NSMenu" ref="769623530"/>
- <string key="NSTitle">Show Spelling and Grammar</string>
- <string key="NSKeyEquiv">:</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="96193923">
- <reference key="NSMenu" ref="769623530"/>
- <string key="NSTitle">Check Document Now</string>
- <string key="NSKeyEquiv">;</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="859480356">
- <reference key="NSMenu" ref="769623530"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="948374510">
- <reference key="NSMenu" ref="769623530"/>
- <string key="NSTitle">Check Spelling While Typing</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="967646866">
- <reference key="NSMenu" ref="769623530"/>
- <string key="NSTitle">Check Grammar With Spelling</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="795346622">
- <reference key="NSMenu" ref="769623530"/>
- <string key="NSTitle">Correct Spelling Automatically</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="507821607">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Substitutions</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="698887838"/>
- <object class="NSMenu" key="NSSubmenu" id="698887838">
- <string key="NSTitle">Substitutions</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="65139061">
- <reference key="NSMenu" ref="698887838"/>
- <string key="NSTitle">Show Substitutions</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="19036812">
- <reference key="NSMenu" ref="698887838"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="605118523">
- <reference key="NSMenu" ref="698887838"/>
- <string key="NSTitle">Smart Copy/Paste</string>
- <string key="NSKeyEquiv">f</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">1</int>
- </object>
- <object class="NSMenuItem" id="197661976">
- <reference key="NSMenu" ref="698887838"/>
- <string key="NSTitle">Smart Quotes</string>
- <string key="NSKeyEquiv">g</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">2</int>
- </object>
- <object class="NSMenuItem" id="672708820">
- <reference key="NSMenu" ref="698887838"/>
- <string key="NSTitle">Smart Dashes</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="708854459">
- <reference key="NSMenu" ref="698887838"/>
- <string key="NSTitle">Smart Links</string>
- <string key="NSKeyEquiv">G</string>
- <int key="NSKeyEquivModMask">1179648</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">3</int>
- </object>
- <object class="NSMenuItem" id="537092702">
- <reference key="NSMenu" ref="698887838"/>
- <string key="NSTitle">Text Replacement</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="288088188">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Transformations</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="579392910"/>
- <object class="NSMenu" key="NSSubmenu" id="579392910">
- <string key="NSTitle">Transformations</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="1060694897">
- <reference key="NSMenu" ref="579392910"/>
- <string key="NSTitle">Make Upper Case</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="879586729">
- <reference key="NSMenu" ref="579392910"/>
- <string key="NSTitle">Make Lower Case</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="56570060">
- <reference key="NSMenu" ref="579392910"/>
- <string key="NSTitle">Capitalize</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="676164635">
- <reference key="NSMenu" ref="789758025"/>
- <string key="NSTitle">Speech</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="785027613"/>
- <object class="NSMenu" key="NSSubmenu" id="785027613">
- <string key="NSTitle">Speech</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="731782645">
- <reference key="NSMenu" ref="785027613"/>
- <string key="NSTitle">Start Speaking</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="680220178">
- <reference key="NSMenu" ref="785027613"/>
- <string key="NSTitle">Stop Speaking</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="302598603">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">Format</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="941447902"/>
- <object class="NSMenu" key="NSSubmenu" id="941447902">
- <string key="NSTitle">Format</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="792887677">
- <reference key="NSMenu" ref="941447902"/>
- <string key="NSTitle">Font</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="786677654"/>
- <object class="NSMenu" key="NSSubmenu" id="786677654">
- <string key="NSTitle">Font</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="159677712">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Show Fonts</string>
- <string key="NSKeyEquiv">t</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="305399458">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Bold</string>
- <string key="NSKeyEquiv">b</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">2</int>
- </object>
- <object class="NSMenuItem" id="814362025">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Italic</string>
- <string key="NSKeyEquiv">i</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">1</int>
- </object>
- <object class="NSMenuItem" id="330926929">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Underline</string>
- <string key="NSKeyEquiv">u</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="533507878">
- <reference key="NSMenu" ref="786677654"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="158063935">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Bigger</string>
- <string key="NSKeyEquiv">+</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">3</int>
- </object>
- <object class="NSMenuItem" id="885547335">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Smaller</string>
- <string key="NSKeyEquiv">-</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <int key="NSTag">4</int>
- </object>
- <object class="NSMenuItem" id="901062459">
- <reference key="NSMenu" ref="786677654"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="767671776">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Kern</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="175441468"/>
- <object class="NSMenu" key="NSSubmenu" id="175441468">
- <string key="NSTitle">Kern</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="252969304">
- <reference key="NSMenu" ref="175441468"/>
- <string key="NSTitle">Use Default</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="766922938">
- <reference key="NSMenu" ref="175441468"/>
- <string key="NSTitle">Use None</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="677519740">
- <reference key="NSMenu" ref="175441468"/>
- <string key="NSTitle">Tighten</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="238351151">
- <reference key="NSMenu" ref="175441468"/>
- <string key="NSTitle">Loosen</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="691570813">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Ligature</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="1058217995"/>
- <object class="NSMenu" key="NSSubmenu" id="1058217995">
- <string key="NSTitle">Ligature</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="706297211">
- <reference key="NSMenu" ref="1058217995"/>
- <string key="NSTitle">Use Default</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="568384683">
- <reference key="NSMenu" ref="1058217995"/>
- <string key="NSTitle">Use None</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="663508465">
- <reference key="NSMenu" ref="1058217995"/>
- <string key="NSTitle">Use All</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="769124883">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Baseline</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="18263474"/>
- <object class="NSMenu" key="NSSubmenu" id="18263474">
- <string key="NSTitle">Baseline</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="257962622">
- <reference key="NSMenu" ref="18263474"/>
- <string key="NSTitle">Use Default</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="644725453">
- <reference key="NSMenu" ref="18263474"/>
- <string key="NSTitle">Superscript</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1037576581">
- <reference key="NSMenu" ref="18263474"/>
- <string key="NSTitle">Subscript</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="941806246">
- <reference key="NSMenu" ref="18263474"/>
- <string key="NSTitle">Raise</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1045724900">
- <reference key="NSMenu" ref="18263474"/>
- <string key="NSTitle">Lower</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="739652853">
- <reference key="NSMenu" ref="786677654"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="1012600125">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Show Colors</string>
- <string key="NSKeyEquiv">C</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="214559597">
- <reference key="NSMenu" ref="786677654"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="596732606">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Copy Style</string>
- <string key="NSKeyEquiv">c</string>
- <int key="NSKeyEquivModMask">1572864</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="393423671">
- <reference key="NSMenu" ref="786677654"/>
- <string key="NSTitle">Paste Style</string>
- <string key="NSKeyEquiv">v</string>
- <int key="NSKeyEquivModMask">1572864</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- <string key="NSName">_NSFontMenu</string>
- </object>
- </object>
- <object class="NSMenuItem" id="215659978">
- <reference key="NSMenu" ref="941447902"/>
- <string key="NSTitle">Text</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="446991534"/>
- <object class="NSMenu" key="NSSubmenu" id="446991534">
- <string key="NSTitle">Text</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="875092757">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Align Left</string>
- <string key="NSKeyEquiv">{</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="630155264">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Center</string>
- <string key="NSKeyEquiv">|</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="945678886">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Justify</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="512868991">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Align Right</string>
- <string key="NSKeyEquiv">}</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="163117631">
- <reference key="NSMenu" ref="446991534"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="31516759">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Writing Direction</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="956096989"/>
- <object class="NSMenu" key="NSSubmenu" id="956096989">
- <string key="NSTitle">Writing Direction</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="257099033">
- <reference key="NSMenu" ref="956096989"/>
- <bool key="NSIsDisabled">YES</bool>
- <string key="NSTitle">Paragraph</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="551969625">
- <reference key="NSMenu" ref="956096989"/>
- <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="249532473">
- <reference key="NSMenu" ref="956096989"/>
- <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="607364498">
- <reference key="NSMenu" ref="956096989"/>
- <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="508151438">
- <reference key="NSMenu" ref="956096989"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="981751889">
- <reference key="NSMenu" ref="956096989"/>
- <bool key="NSIsDisabled">YES</bool>
- <string key="NSTitle">Selection</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="380031999">
- <reference key="NSMenu" ref="956096989"/>
- <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="825984362">
- <reference key="NSMenu" ref="956096989"/>
- <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="560145579">
- <reference key="NSMenu" ref="956096989"/>
- <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="908105787">
- <reference key="NSMenu" ref="446991534"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="644046920">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Show Ruler</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="231811626">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Copy Ruler</string>
- <string key="NSKeyEquiv">c</string>
- <int key="NSKeyEquivModMask">1310720</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="883618387">
- <reference key="NSMenu" ref="446991534"/>
- <string key="NSTitle">Paste Ruler</string>
- <string key="NSKeyEquiv">v</string>
- <int key="NSKeyEquivModMask">1310720</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="586577488">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">View</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="466310130"/>
- <object class="NSMenu" key="NSSubmenu" id="466310130">
- <string key="NSTitle">View</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="102151532">
- <reference key="NSMenu" ref="466310130"/>
- <string key="NSTitle">Show Toolbar</string>
- <string key="NSKeyEquiv">t</string>
- <int key="NSKeyEquivModMask">1572864</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="237841660">
- <reference key="NSMenu" ref="466310130"/>
- <string key="NSTitle">Customize Toolbar…</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- </object>
- </object>
- <object class="NSMenuItem" id="713487014">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">Window</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="835318025"/>
- <object class="NSMenu" key="NSSubmenu" id="835318025">
- <string key="NSTitle">Window</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="1011231497">
- <reference key="NSMenu" ref="835318025"/>
- <string key="NSTitle">Minimize</string>
- <string key="NSKeyEquiv">m</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="575023229">
- <reference key="NSMenu" ref="835318025"/>
- <string key="NSTitle">Zoom</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="299356726">
- <reference key="NSMenu" ref="835318025"/>
- <bool key="NSIsDisabled">YES</bool>
- <bool key="NSIsSeparator">YES</bool>
- <string key="NSTitle"/>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- <object class="NSMenuItem" id="625202149">
- <reference key="NSMenu" ref="835318025"/>
- <string key="NSTitle">Bring All to Front</string>
- <string key="NSKeyEquiv"/>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- <string key="NSName">_NSWindowsMenu</string>
- </object>
- </object>
- <object class="NSMenuItem" id="448692316">
- <reference key="NSMenu" ref="649796088"/>
- <string key="NSTitle">Help</string>
- <string key="NSKeyEquiv"/>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- <string key="NSAction">submenuAction:</string>
- <reference key="NSTarget" ref="992780483"/>
- <object class="NSMenu" key="NSSubmenu" id="992780483">
- <string key="NSTitle">Help</string>
- <array class="NSMutableArray" key="NSMenuItems">
- <object class="NSMenuItem" id="105068016">
- <reference key="NSMenu" ref="992780483"/>
- <string key="NSTitle">ZeroTier One Help</string>
- <string key="NSKeyEquiv">?</string>
- <int key="NSKeyEquivModMask">1048576</int>
- <int key="NSMnemonicLoc">2147483647</int>
- <reference key="NSOnImage" ref="35465992"/>
- <reference key="NSMixedImage" ref="502551668"/>
- </object>
- </array>
- <string key="NSName">_NSHelpMenu</string>
- </object>
- </object>
- </array>
- <string key="NSName">_NSMainMenu</string>
- </object>
- </array>
- <object class="IBObjectContainer" key="IBDocument.Objects">
- <array key="connectionRecords">
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">terminate:</string>
- <reference key="source" ref="1050"/>
- <reference key="destination" ref="632727374"/>
- </object>
- <int key="connectionID">449</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">orderFrontStandardAboutPanel:</string>
- <reference key="source" ref="1021"/>
- <reference key="destination" ref="238522557"/>
- </object>
- <int key="connectionID">142</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="1021"/>
- <reference key="destination" ref="976324537"/>
- </object>
- <int key="connectionID">547</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performMiniaturize:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1011231497"/>
- </object>
- <int key="connectionID">37</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">arrangeInFront:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="625202149"/>
- </object>
- <int key="connectionID">39</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">print:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="49223823"/>
- </object>
- <int key="connectionID">86</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">runPageLayout:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="294629803"/>
- </object>
- <int key="connectionID">87</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">clearRecentDocuments:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="759406840"/>
- </object>
- <int key="connectionID">127</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performClose:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="776162233"/>
- </object>
- <int key="connectionID">193</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleContinuousSpellChecking:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="948374510"/>
- </object>
- <int key="connectionID">222</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">undo:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1058277027"/>
- </object>
- <int key="connectionID">223</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">copy:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="860595796"/>
- </object>
- <int key="connectionID">224</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">checkSpelling:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="96193923"/>
- </object>
- <int key="connectionID">225</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">paste:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="29853731"/>
- </object>
- <int key="connectionID">226</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">stopSpeaking:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="680220178"/>
- </object>
- <int key="connectionID">227</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">cut:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="296257095"/>
- </object>
- <int key="connectionID">228</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">showGuessPanel:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="679648819"/>
- </object>
- <int key="connectionID">230</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">redo:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="790794224"/>
- </object>
- <int key="connectionID">231</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">selectAll:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="583158037"/>
- </object>
- <int key="connectionID">232</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">startSpeaking:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="731782645"/>
- </object>
- <int key="connectionID">233</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">delete:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="437104165"/>
- </object>
- <int key="connectionID">235</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performZoom:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="575023229"/>
- </object>
- <int key="connectionID">240</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performFindPanelAction:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="447796847"/>
- </object>
- <int key="connectionID">241</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">centerSelectionInVisibleArea:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="88285865"/>
- </object>
- <int key="connectionID">245</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleGrammarChecking:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="967646866"/>
- </object>
- <int key="connectionID">347</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleSmartInsertDelete:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="605118523"/>
- </object>
- <int key="connectionID">355</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleAutomaticQuoteSubstitution:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="197661976"/>
- </object>
- <int key="connectionID">356</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleAutomaticLinkDetection:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="708854459"/>
- </object>
- <int key="connectionID">357</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">saveDocument:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1023925487"/>
- </object>
- <int key="connectionID">362</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">revertDocumentToSaved:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="579971712"/>
- </object>
- <int key="connectionID">364</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">runToolbarCustomizationPalette:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="237841660"/>
- </object>
- <int key="connectionID">365</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleToolbarShown:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="102151532"/>
- </object>
- <int key="connectionID">366</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">hide:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="755159360"/>
- </object>
- <int key="connectionID">367</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">hideOtherApplications:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="342932134"/>
- </object>
- <int key="connectionID">368</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">unhideAllApplications:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="908899353"/>
- </object>
- <int key="connectionID">370</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">newDocument:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="705341025"/>
- </object>
- <int key="connectionID">373</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">openDocument:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="722745758"/>
- </object>
- <int key="connectionID">374</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">raiseBaseline:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="941806246"/>
- </object>
- <int key="connectionID">426</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">lowerBaseline:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1045724900"/>
- </object>
- <int key="connectionID">427</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">copyFont:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="596732606"/>
- </object>
- <int key="connectionID">428</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">subscript:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1037576581"/>
- </object>
- <int key="connectionID">429</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">superscript:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="644725453"/>
- </object>
- <int key="connectionID">430</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">tightenKerning:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="677519740"/>
- </object>
- <int key="connectionID">431</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">underline:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="330926929"/>
- </object>
- <int key="connectionID">432</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">orderFrontColorPanel:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1012600125"/>
- </object>
- <int key="connectionID">433</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">useAllLigatures:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="663508465"/>
- </object>
- <int key="connectionID">434</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">loosenKerning:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="238351151"/>
- </object>
- <int key="connectionID">435</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">pasteFont:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="393423671"/>
- </object>
- <int key="connectionID">436</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">unscript:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="257962622"/>
- </object>
- <int key="connectionID">437</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">useStandardKerning:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="252969304"/>
- </object>
- <int key="connectionID">438</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">useStandardLigatures:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="706297211"/>
- </object>
- <int key="connectionID">439</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">turnOffLigatures:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="568384683"/>
- </object>
- <int key="connectionID">440</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">turnOffKerning:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="766922938"/>
- </object>
- <int key="connectionID">441</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleAutomaticSpellingCorrection:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="795346622"/>
- </object>
- <int key="connectionID">456</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">orderFrontSubstitutionsPanel:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="65139061"/>
- </object>
- <int key="connectionID">458</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleAutomaticDashSubstitution:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="672708820"/>
- </object>
- <int key="connectionID">461</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleAutomaticTextReplacement:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="537092702"/>
- </object>
- <int key="connectionID">463</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">uppercaseWord:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="1060694897"/>
- </object>
- <int key="connectionID">464</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">capitalizeWord:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="56570060"/>
- </object>
- <int key="connectionID">467</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">lowercaseWord:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="879586729"/>
- </object>
- <int key="connectionID">468</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">pasteAsPlainText:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="82994268"/>
- </object>
- <int key="connectionID">486</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performFindPanelAction:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="326711663"/>
- </object>
- <int key="connectionID">487</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performFindPanelAction:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="270902937"/>
- </object>
- <int key="connectionID">488</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performFindPanelAction:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="159080638"/>
- </object>
- <int key="connectionID">489</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">showHelp:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="105068016"/>
- </object>
- <int key="connectionID">493</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">alignCenter:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="630155264"/>
- </object>
- <int key="connectionID">518</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">pasteRuler:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="883618387"/>
- </object>
- <int key="connectionID">519</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">toggleRuler:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="644046920"/>
- </object>
- <int key="connectionID">520</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">alignRight:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="512868991"/>
- </object>
- <int key="connectionID">521</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">copyRuler:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="231811626"/>
- </object>
- <int key="connectionID">522</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">alignJustified:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="945678886"/>
- </object>
- <int key="connectionID">523</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">alignLeft:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="875092757"/>
- </object>
- <int key="connectionID">524</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">makeBaseWritingDirectionNatural:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="551969625"/>
- </object>
- <int key="connectionID">525</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">makeBaseWritingDirectionLeftToRight:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="249532473"/>
- </object>
- <int key="connectionID">526</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">makeBaseWritingDirectionRightToLeft:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="607364498"/>
- </object>
- <int key="connectionID">527</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">makeTextWritingDirectionNatural:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="380031999"/>
- </object>
- <int key="connectionID">528</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">makeTextWritingDirectionLeftToRight:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="825984362"/>
- </object>
- <int key="connectionID">529</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">makeTextWritingDirectionRightToLeft:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="560145579"/>
- </object>
- <int key="connectionID">530</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">performFindPanelAction:</string>
- <reference key="source" ref="1014"/>
- <reference key="destination" ref="738670835"/>
- </object>
- <int key="connectionID">535</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
- <string key="label">delegate</string>
- <reference key="source" ref="649796088"/>
- <reference key="destination" ref="1021"/>
- </object>
- <int key="connectionID">545</int>
- </object>
- </array>
- <object class="IBMutableOrderedSet" key="objectRecords">
- <array key="orderedObjects">
- <object class="IBObjectRecord">
- <int key="objectID">0</int>
- <array key="object" id="0"/>
- <reference key="children" ref="1048"/>
- <nil key="parent"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">-2</int>
- <reference key="object" ref="1021"/>
- <reference key="parent" ref="0"/>
- <string key="objectName">File's Owner</string>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">-1</int>
- <reference key="object" ref="1014"/>
- <reference key="parent" ref="0"/>
- <string key="objectName">First Responder</string>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">-3</int>
- <reference key="object" ref="1050"/>
- <reference key="parent" ref="0"/>
- <string key="objectName">Application</string>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">29</int>
- <reference key="object" ref="649796088"/>
- <array class="NSMutableArray" key="children">
- <reference ref="713487014"/>
- <reference ref="694149608"/>
- <reference ref="952259628"/>
- <reference ref="379814623"/>
- <reference ref="586577488"/>
- <reference ref="302598603"/>
- <reference ref="448692316"/>
- </array>
- <reference key="parent" ref="0"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">19</int>
- <reference key="object" ref="713487014"/>
- <array class="NSMutableArray" key="children">
- <reference ref="835318025"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">56</int>
- <reference key="object" ref="694149608"/>
- <array class="NSMutableArray" key="children">
- <reference ref="110575045"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">217</int>
- <reference key="object" ref="952259628"/>
- <array class="NSMutableArray" key="children">
- <reference ref="789758025"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">83</int>
- <reference key="object" ref="379814623"/>
- <array class="NSMutableArray" key="children">
- <reference ref="720053764"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">81</int>
- <reference key="object" ref="720053764"/>
- <array class="NSMutableArray" key="children">
- <reference ref="1023925487"/>
- <reference ref="49223823"/>
- <reference ref="722745758"/>
- <reference ref="705341025"/>
- <reference ref="1025936716"/>
- <reference ref="294629803"/>
- <reference ref="776162233"/>
- <reference ref="425164168"/>
- <reference ref="579971712"/>
- <reference ref="1010469920"/>
- </array>
- <reference key="parent" ref="379814623"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">75</int>
- <reference key="object" ref="1023925487"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">78</int>
- <reference key="object" ref="49223823"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">72</int>
- <reference key="object" ref="722745758"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">82</int>
- <reference key="object" ref="705341025"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">124</int>
- <reference key="object" ref="1025936716"/>
- <array class="NSMutableArray" key="children">
- <reference ref="1065607017"/>
- </array>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">77</int>
- <reference key="object" ref="294629803"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">73</int>
- <reference key="object" ref="776162233"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">79</int>
- <reference key="object" ref="425164168"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">112</int>
- <reference key="object" ref="579971712"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">74</int>
- <reference key="object" ref="1010469920"/>
- <reference key="parent" ref="720053764"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">125</int>
- <reference key="object" ref="1065607017"/>
- <array class="NSMutableArray" key="children">
- <reference ref="759406840"/>
- </array>
- <reference key="parent" ref="1025936716"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">126</int>
- <reference key="object" ref="759406840"/>
- <reference key="parent" ref="1065607017"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">205</int>
- <reference key="object" ref="789758025"/>
- <array class="NSMutableArray" key="children">
- <reference ref="437104165"/>
- <reference ref="583158037"/>
- <reference ref="1058277027"/>
- <reference ref="212016141"/>
- <reference ref="296257095"/>
- <reference ref="29853731"/>
- <reference ref="860595796"/>
- <reference ref="1040322652"/>
- <reference ref="790794224"/>
- <reference ref="892235320"/>
- <reference ref="972420730"/>
- <reference ref="676164635"/>
- <reference ref="507821607"/>
- <reference ref="288088188"/>
- <reference ref="82994268"/>
- </array>
- <reference key="parent" ref="952259628"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">202</int>
- <reference key="object" ref="437104165"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">198</int>
- <reference key="object" ref="583158037"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">207</int>
- <reference key="object" ref="1058277027"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">214</int>
- <reference key="object" ref="212016141"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">199</int>
- <reference key="object" ref="296257095"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">203</int>
- <reference key="object" ref="29853731"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">197</int>
- <reference key="object" ref="860595796"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">206</int>
- <reference key="object" ref="1040322652"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">215</int>
- <reference key="object" ref="790794224"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">218</int>
- <reference key="object" ref="892235320"/>
- <array class="NSMutableArray" key="children">
- <reference ref="963351320"/>
- </array>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">216</int>
- <reference key="object" ref="972420730"/>
- <array class="NSMutableArray" key="children">
- <reference ref="769623530"/>
- </array>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">200</int>
- <reference key="object" ref="769623530"/>
- <array class="NSMutableArray" key="children">
- <reference ref="948374510"/>
- <reference ref="96193923"/>
- <reference ref="679648819"/>
- <reference ref="967646866"/>
- <reference ref="859480356"/>
- <reference ref="795346622"/>
- </array>
- <reference key="parent" ref="972420730"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">219</int>
- <reference key="object" ref="948374510"/>
- <reference key="parent" ref="769623530"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">201</int>
- <reference key="object" ref="96193923"/>
- <reference key="parent" ref="769623530"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">204</int>
- <reference key="object" ref="679648819"/>
- <reference key="parent" ref="769623530"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">220</int>
- <reference key="object" ref="963351320"/>
- <array class="NSMutableArray" key="children">
- <reference ref="270902937"/>
- <reference ref="88285865"/>
- <reference ref="159080638"/>
- <reference ref="326711663"/>
- <reference ref="447796847"/>
- <reference ref="738670835"/>
- </array>
- <reference key="parent" ref="892235320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">213</int>
- <reference key="object" ref="270902937"/>
- <reference key="parent" ref="963351320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">210</int>
- <reference key="object" ref="88285865"/>
- <reference key="parent" ref="963351320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">221</int>
- <reference key="object" ref="159080638"/>
- <reference key="parent" ref="963351320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">208</int>
- <reference key="object" ref="326711663"/>
- <reference key="parent" ref="963351320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">209</int>
- <reference key="object" ref="447796847"/>
- <reference key="parent" ref="963351320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">57</int>
- <reference key="object" ref="110575045"/>
- <array class="NSMutableArray" key="children">
- <reference ref="238522557"/>
- <reference ref="755159360"/>
- <reference ref="908899353"/>
- <reference ref="632727374"/>
- <reference ref="646227648"/>
- <reference ref="609285721"/>
- <reference ref="481834944"/>
- <reference ref="304266470"/>
- <reference ref="1046388886"/>
- <reference ref="1056857174"/>
- <reference ref="342932134"/>
- </array>
- <reference key="parent" ref="694149608"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">58</int>
- <reference key="object" ref="238522557"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">134</int>
- <reference key="object" ref="755159360"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">150</int>
- <reference key="object" ref="908899353"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">136</int>
- <reference key="object" ref="632727374"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">144</int>
- <reference key="object" ref="646227648"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">129</int>
- <reference key="object" ref="609285721"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">143</int>
- <reference key="object" ref="481834944"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">236</int>
- <reference key="object" ref="304266470"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">131</int>
- <reference key="object" ref="1046388886"/>
- <array class="NSMutableArray" key="children">
- <reference ref="752062318"/>
- </array>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">149</int>
- <reference key="object" ref="1056857174"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">145</int>
- <reference key="object" ref="342932134"/>
- <reference key="parent" ref="110575045"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">130</int>
- <reference key="object" ref="752062318"/>
- <reference key="parent" ref="1046388886"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">24</int>
- <reference key="object" ref="835318025"/>
- <array class="NSMutableArray" key="children">
- <reference ref="299356726"/>
- <reference ref="625202149"/>
- <reference ref="575023229"/>
- <reference ref="1011231497"/>
- </array>
- <reference key="parent" ref="713487014"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">92</int>
- <reference key="object" ref="299356726"/>
- <reference key="parent" ref="835318025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">5</int>
- <reference key="object" ref="625202149"/>
- <reference key="parent" ref="835318025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">239</int>
- <reference key="object" ref="575023229"/>
- <reference key="parent" ref="835318025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">23</int>
- <reference key="object" ref="1011231497"/>
- <reference key="parent" ref="835318025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">295</int>
- <reference key="object" ref="586577488"/>
- <array class="NSMutableArray" key="children">
- <reference ref="466310130"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">296</int>
- <reference key="object" ref="466310130"/>
- <array class="NSMutableArray" key="children">
- <reference ref="102151532"/>
- <reference ref="237841660"/>
- </array>
- <reference key="parent" ref="586577488"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">297</int>
- <reference key="object" ref="102151532"/>
- <reference key="parent" ref="466310130"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">298</int>
- <reference key="object" ref="237841660"/>
- <reference key="parent" ref="466310130"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">211</int>
- <reference key="object" ref="676164635"/>
- <array class="NSMutableArray" key="children">
- <reference ref="785027613"/>
- </array>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">212</int>
- <reference key="object" ref="785027613"/>
- <array class="NSMutableArray" key="children">
- <reference ref="680220178"/>
- <reference ref="731782645"/>
- </array>
- <reference key="parent" ref="676164635"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">195</int>
- <reference key="object" ref="680220178"/>
- <reference key="parent" ref="785027613"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">196</int>
- <reference key="object" ref="731782645"/>
- <reference key="parent" ref="785027613"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">346</int>
- <reference key="object" ref="967646866"/>
- <reference key="parent" ref="769623530"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">348</int>
- <reference key="object" ref="507821607"/>
- <array class="NSMutableArray" key="children">
- <reference ref="698887838"/>
- </array>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">349</int>
- <reference key="object" ref="698887838"/>
- <array class="NSMutableArray" key="children">
- <reference ref="605118523"/>
- <reference ref="197661976"/>
- <reference ref="708854459"/>
- <reference ref="65139061"/>
- <reference ref="19036812"/>
- <reference ref="672708820"/>
- <reference ref="537092702"/>
- </array>
- <reference key="parent" ref="507821607"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">350</int>
- <reference key="object" ref="605118523"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">351</int>
- <reference key="object" ref="197661976"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">354</int>
- <reference key="object" ref="708854459"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">375</int>
- <reference key="object" ref="302598603"/>
- <array class="NSMutableArray" key="children">
- <reference ref="941447902"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">376</int>
- <reference key="object" ref="941447902"/>
- <array class="NSMutableArray" key="children">
- <reference ref="792887677"/>
- <reference ref="215659978"/>
- </array>
- <reference key="parent" ref="302598603"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">377</int>
- <reference key="object" ref="792887677"/>
- <array class="NSMutableArray" key="children">
- <reference ref="786677654"/>
- </array>
- <reference key="parent" ref="941447902"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">388</int>
- <reference key="object" ref="786677654"/>
- <array class="NSMutableArray" key="children">
- <reference ref="159677712"/>
- <reference ref="305399458"/>
- <reference ref="814362025"/>
- <reference ref="330926929"/>
- <reference ref="533507878"/>
- <reference ref="158063935"/>
- <reference ref="885547335"/>
- <reference ref="901062459"/>
- <reference ref="767671776"/>
- <reference ref="691570813"/>
- <reference ref="769124883"/>
- <reference ref="739652853"/>
- <reference ref="1012600125"/>
- <reference ref="214559597"/>
- <reference ref="596732606"/>
- <reference ref="393423671"/>
- </array>
- <reference key="parent" ref="792887677"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">389</int>
- <reference key="object" ref="159677712"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">390</int>
- <reference key="object" ref="305399458"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">391</int>
- <reference key="object" ref="814362025"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">392</int>
- <reference key="object" ref="330926929"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">393</int>
- <reference key="object" ref="533507878"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">394</int>
- <reference key="object" ref="158063935"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">395</int>
- <reference key="object" ref="885547335"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">396</int>
- <reference key="object" ref="901062459"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">397</int>
- <reference key="object" ref="767671776"/>
- <array class="NSMutableArray" key="children">
- <reference ref="175441468"/>
- </array>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">398</int>
- <reference key="object" ref="691570813"/>
- <array class="NSMutableArray" key="children">
- <reference ref="1058217995"/>
- </array>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">399</int>
- <reference key="object" ref="769124883"/>
- <array class="NSMutableArray" key="children">
- <reference ref="18263474"/>
- </array>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">400</int>
- <reference key="object" ref="739652853"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">401</int>
- <reference key="object" ref="1012600125"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">402</int>
- <reference key="object" ref="214559597"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">403</int>
- <reference key="object" ref="596732606"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">404</int>
- <reference key="object" ref="393423671"/>
- <reference key="parent" ref="786677654"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">405</int>
- <reference key="object" ref="18263474"/>
- <array class="NSMutableArray" key="children">
- <reference ref="257962622"/>
- <reference ref="644725453"/>
- <reference ref="1037576581"/>
- <reference ref="941806246"/>
- <reference ref="1045724900"/>
- </array>
- <reference key="parent" ref="769124883"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">406</int>
- <reference key="object" ref="257962622"/>
- <reference key="parent" ref="18263474"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">407</int>
- <reference key="object" ref="644725453"/>
- <reference key="parent" ref="18263474"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">408</int>
- <reference key="object" ref="1037576581"/>
- <reference key="parent" ref="18263474"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">409</int>
- <reference key="object" ref="941806246"/>
- <reference key="parent" ref="18263474"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">410</int>
- <reference key="object" ref="1045724900"/>
- <reference key="parent" ref="18263474"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">411</int>
- <reference key="object" ref="1058217995"/>
- <array class="NSMutableArray" key="children">
- <reference ref="706297211"/>
- <reference ref="568384683"/>
- <reference ref="663508465"/>
- </array>
- <reference key="parent" ref="691570813"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">412</int>
- <reference key="object" ref="706297211"/>
- <reference key="parent" ref="1058217995"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">413</int>
- <reference key="object" ref="568384683"/>
- <reference key="parent" ref="1058217995"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">414</int>
- <reference key="object" ref="663508465"/>
- <reference key="parent" ref="1058217995"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">415</int>
- <reference key="object" ref="175441468"/>
- <array class="NSMutableArray" key="children">
- <reference ref="252969304"/>
- <reference ref="766922938"/>
- <reference ref="677519740"/>
- <reference ref="238351151"/>
- </array>
- <reference key="parent" ref="767671776"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">416</int>
- <reference key="object" ref="252969304"/>
- <reference key="parent" ref="175441468"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">417</int>
- <reference key="object" ref="766922938"/>
- <reference key="parent" ref="175441468"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">418</int>
- <reference key="object" ref="677519740"/>
- <reference key="parent" ref="175441468"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">419</int>
- <reference key="object" ref="238351151"/>
- <reference key="parent" ref="175441468"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">450</int>
- <reference key="object" ref="288088188"/>
- <array class="NSMutableArray" key="children">
- <reference ref="579392910"/>
- </array>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">451</int>
- <reference key="object" ref="579392910"/>
- <array class="NSMutableArray" key="children">
- <reference ref="1060694897"/>
- <reference ref="879586729"/>
- <reference ref="56570060"/>
- </array>
- <reference key="parent" ref="288088188"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">452</int>
- <reference key="object" ref="1060694897"/>
- <reference key="parent" ref="579392910"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">453</int>
- <reference key="object" ref="859480356"/>
- <reference key="parent" ref="769623530"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">454</int>
- <reference key="object" ref="795346622"/>
- <reference key="parent" ref="769623530"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">457</int>
- <reference key="object" ref="65139061"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">459</int>
- <reference key="object" ref="19036812"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">460</int>
- <reference key="object" ref="672708820"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">462</int>
- <reference key="object" ref="537092702"/>
- <reference key="parent" ref="698887838"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">465</int>
- <reference key="object" ref="879586729"/>
- <reference key="parent" ref="579392910"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">466</int>
- <reference key="object" ref="56570060"/>
- <reference key="parent" ref="579392910"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">485</int>
- <reference key="object" ref="82994268"/>
- <reference key="parent" ref="789758025"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">490</int>
- <reference key="object" ref="448692316"/>
- <array class="NSMutableArray" key="children">
- <reference ref="992780483"/>
- </array>
- <reference key="parent" ref="649796088"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">491</int>
- <reference key="object" ref="992780483"/>
- <array class="NSMutableArray" key="children">
- <reference ref="105068016"/>
- </array>
- <reference key="parent" ref="448692316"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">492</int>
- <reference key="object" ref="105068016"/>
- <reference key="parent" ref="992780483"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">496</int>
- <reference key="object" ref="215659978"/>
- <array class="NSMutableArray" key="children">
- <reference ref="446991534"/>
- </array>
- <reference key="parent" ref="941447902"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">497</int>
- <reference key="object" ref="446991534"/>
- <array class="NSMutableArray" key="children">
- <reference ref="875092757"/>
- <reference ref="630155264"/>
- <reference ref="945678886"/>
- <reference ref="512868991"/>
- <reference ref="163117631"/>
- <reference ref="31516759"/>
- <reference ref="908105787"/>
- <reference ref="644046920"/>
- <reference ref="231811626"/>
- <reference ref="883618387"/>
- </array>
- <reference key="parent" ref="215659978"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">498</int>
- <reference key="object" ref="875092757"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">499</int>
- <reference key="object" ref="630155264"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">500</int>
- <reference key="object" ref="945678886"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">501</int>
- <reference key="object" ref="512868991"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">502</int>
- <reference key="object" ref="163117631"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">503</int>
- <reference key="object" ref="31516759"/>
- <array class="NSMutableArray" key="children">
- <reference ref="956096989"/>
- </array>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">504</int>
- <reference key="object" ref="908105787"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">505</int>
- <reference key="object" ref="644046920"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">506</int>
- <reference key="object" ref="231811626"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">507</int>
- <reference key="object" ref="883618387"/>
- <reference key="parent" ref="446991534"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">508</int>
- <reference key="object" ref="956096989"/>
- <array class="NSMutableArray" key="children">
- <reference ref="257099033"/>
- <reference ref="551969625"/>
- <reference ref="249532473"/>
- <reference ref="607364498"/>
- <reference ref="508151438"/>
- <reference ref="981751889"/>
- <reference ref="380031999"/>
- <reference ref="825984362"/>
- <reference ref="560145579"/>
- </array>
- <reference key="parent" ref="31516759"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">509</int>
- <reference key="object" ref="257099033"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">510</int>
- <reference key="object" ref="551969625"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">511</int>
- <reference key="object" ref="249532473"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">512</int>
- <reference key="object" ref="607364498"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">513</int>
- <reference key="object" ref="508151438"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">514</int>
- <reference key="object" ref="981751889"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">515</int>
- <reference key="object" ref="380031999"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">516</int>
- <reference key="object" ref="825984362"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">517</int>
- <reference key="object" ref="560145579"/>
- <reference key="parent" ref="956096989"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">534</int>
- <reference key="object" ref="738670835"/>
- <reference key="parent" ref="963351320"/>
- </object>
- <object class="IBObjectRecord">
- <int key="objectID">546</int>
- <reference key="object" ref="976324537"/>
- <reference key="parent" ref="0"/>
- </object>
- </array>
- </object>
- <dictionary class="NSMutableDictionary" key="flattenedProperties">
- <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="112.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="124.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="125.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="126.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="129.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="130.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="131.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="134.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="136.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="143.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="144.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="145.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="149.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="150.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="19.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="195.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="196.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="197.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="198.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="199.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="200.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="201.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="202.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="203.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="204.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="205.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="206.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="207.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="208.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="209.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="210.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="211.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="212.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="213.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="214.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="215.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="216.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="218.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="219.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="220.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="221.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="23.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="236.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="239.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="24.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="29.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="295.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="296.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="297.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="298.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="346.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="348.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="349.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="350.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="351.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="354.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="375.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="376.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="377.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="388.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="389.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="390.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="391.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="392.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="393.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="394.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="395.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="396.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="397.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="398.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="399.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="400.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="401.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="402.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="403.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="404.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="405.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="406.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="407.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="408.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="409.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="410.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="411.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="412.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="413.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="414.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="415.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="416.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="417.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="418.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="419.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="450.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="451.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="452.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="453.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="454.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="457.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="459.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="460.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="462.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="465.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="466.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="485.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="490.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="491.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="492.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="496.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="497.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="498.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="499.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="5.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="500.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="501.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="502.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="503.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="504.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="505.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="506.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="507.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="508.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="509.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="510.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="511.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="512.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="513.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="514.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="515.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="516.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="517.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="534.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="546.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="72.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="73.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="74.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="75.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="77.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="78.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="79.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="81.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="82.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="83.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string key="92.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
- </dictionary>
- <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
- <nil key="activeLocalization"/>
- <dictionary class="NSMutableDictionary" key="localizations"/>
- <nil key="sourceID"/>
- <int key="maxID">547</int>
- </object>
- <object class="IBClassDescriber" key="IBDocument.Classes">
- <array class="NSMutableArray" key="referencedPartialClassDescriptions">
- <object class="IBPartialClassDescription">
- <string key="className">AppDelegate</string>
- <string key="superclassName">NSObject</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBProjectSource</string>
- <string key="minorKey">../MacGap/AppDelegate.h</string>
- </object>
- </object>
- </array>
- <array class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
- <object class="IBPartialClassDescription">
- <string key="className">NSApplication</string>
- <string key="superclassName">NSResponder</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSApplication.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSBrowser</string>
- <string key="superclassName">NSControl</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSBrowser.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSControl</string>
- <string key="superclassName">NSView</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSControl.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSDocument</string>
- <string key="superclassName">NSObject</string>
- <dictionary class="NSMutableDictionary" key="actions">
- <string key="printDocument:">id</string>
- <string key="revertDocumentToSaved:">id</string>
- <string key="runPageLayout:">id</string>
- <string key="saveDocument:">id</string>
- <string key="saveDocumentAs:">id</string>
- <string key="saveDocumentTo:">id</string>
- </dictionary>
- <dictionary class="NSMutableDictionary" key="actionInfosByName">
- <object class="IBActionInfo" key="printDocument:">
- <string key="name">printDocument:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="revertDocumentToSaved:">
- <string key="name">revertDocumentToSaved:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="runPageLayout:">
- <string key="name">runPageLayout:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="saveDocument:">
- <string key="name">saveDocument:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="saveDocumentAs:">
- <string key="name">saveDocumentAs:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="saveDocumentTo:">
- <string key="name">saveDocumentTo:</string>
- <string key="candidateClassName">id</string>
- </object>
- </dictionary>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSDocument.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSDocumentController</string>
- <string key="superclassName">NSObject</string>
- <dictionary class="NSMutableDictionary" key="actions">
- <string key="clearRecentDocuments:">id</string>
- <string key="newDocument:">id</string>
- <string key="openDocument:">id</string>
- <string key="saveAllDocuments:">id</string>
- </dictionary>
- <dictionary class="NSMutableDictionary" key="actionInfosByName">
- <object class="IBActionInfo" key="clearRecentDocuments:">
- <string key="name">clearRecentDocuments:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="newDocument:">
- <string key="name">newDocument:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="openDocument:">
- <string key="name">openDocument:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="saveAllDocuments:">
- <string key="name">saveAllDocuments:</string>
- <string key="candidateClassName">id</string>
- </object>
- </dictionary>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSDocumentController.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSFormatter</string>
- <string key="superclassName">NSObject</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSMatrix</string>
- <string key="superclassName">NSControl</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSMatrix.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSMenu</string>
- <string key="superclassName">NSObject</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSMenu.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSMenuItem</string>
- <string key="superclassName">NSObject</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSMenuItem.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSMovieView</string>
- <string key="superclassName">NSView</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSMovieView.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSPopover</string>
- <string key="superclassName">NSResponder</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSPopover.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSResponder</string>
- <string key="superclassName">NSObject</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSResponder.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSTableView</string>
- <string key="superclassName">NSControl</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSTableView.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSText</string>
- <string key="superclassName">NSView</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSText.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSTextView</string>
- <string key="superclassName">NSText</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSTextView.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSView</string>
- <string key="superclassName">NSResponder</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSView.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSViewController</string>
- <string key="superclassName">NSResponder</string>
- <object class="NSMutableDictionary" key="outlets">
- <string key="NS.key.0">view</string>
- <string key="NS.object.0">NSView</string>
- </object>
- <object class="NSMutableDictionary" key="toOneOutletInfosByName">
- <string key="NS.key.0">view</string>
- <object class="IBToOneOutletInfo" key="NS.object.0">
- <string key="name">view</string>
- <string key="candidateClassName">NSView</string>
- </object>
- </object>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSViewController.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSWindow</string>
- <string key="superclassName">NSResponder</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSWindow.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">WebView</string>
- <string key="superclassName">NSView</string>
- <dictionary class="NSMutableDictionary" key="actions">
- <string key="goBack:">id</string>
- <string key="goForward:">id</string>
- <string key="makeTextLarger:">id</string>
- <string key="makeTextSmaller:">id</string>
- <string key="makeTextStandardSize:">id</string>
- <string key="reload:">id</string>
- <string key="reloadFromOrigin:">id</string>
- <string key="stopLoading:">id</string>
- <string key="takeStringURLFrom:">id</string>
- <string key="toggleContinuousSpellChecking:">id</string>
- <string key="toggleSmartInsertDelete:">id</string>
- </dictionary>
- <dictionary class="NSMutableDictionary" key="actionInfosByName">
- <object class="IBActionInfo" key="goBack:">
- <string key="name">goBack:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="goForward:">
- <string key="name">goForward:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="makeTextLarger:">
- <string key="name">makeTextLarger:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="makeTextSmaller:">
- <string key="name">makeTextSmaller:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="makeTextStandardSize:">
- <string key="name">makeTextStandardSize:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="reload:">
- <string key="name">reload:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="reloadFromOrigin:">
- <string key="name">reloadFromOrigin:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="stopLoading:">
- <string key="name">stopLoading:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="takeStringURLFrom:">
- <string key="name">takeStringURLFrom:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="toggleContinuousSpellChecking:">
- <string key="name">toggleContinuousSpellChecking:</string>
- <string key="candidateClassName">id</string>
- </object>
- <object class="IBActionInfo" key="toggleSmartInsertDelete:">
- <string key="name">toggleSmartInsertDelete:</string>
- <string key="candidateClassName">id</string>
- </object>
- </dictionary>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">WebKit.framework/Headers/WebView.h</string>
- </object>
- </object>
- </array>
- </object>
- <int key="IBDocument.localizationMode">0</int>
- <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
- <bool key="IBDocument.previouslyAttemptedUpgradeToXcode5">NO</bool>
- <object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
- <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
- <integer value="4600" key="NS.object.0"/>
- </object>
- <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
- <int key="IBDocument.defaultPropertyAccessControl">3</int>
- <dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
- <string key="NSMenuCheckmark">{12, 12}</string>
- <string key="NSMenuMixedState">{10, 2}</string>
- </dictionary>
- </data>
-</archive>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Window.xib b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Window.xib
deleted file mode 100644
index fa70acaa..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/en.lproj/Window.xib
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
- <dependencies>
- <deployment identifier="macosx"/>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9060"/>
- <plugIn identifier="com.apple.WebKitIBPlugin" version="9060"/>
- </dependencies>
- <objects>
- <customObject id="-2" userLabel="File's Owner" customClass="WindowController">
- <connections>
- <outlet property="contentView" destination="2" id="23"/>
- <outlet property="window" destination="1" id="25"/>
- </connections>
- </customObject>
- <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
- <customObject id="-3" userLabel="Application" customClass="NSObject"/>
- <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" animationBehavior="default" id="1">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
- <rect key="contentRect" x="575" y="564" width="500" height="500"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
- <view key="contentView" id="2" customClass="ContentView">
- <rect key="frame" x="0.0" y="0.0" width="500" height="500"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <webView id="5">
- <rect key="frame" x="0.0" y="0.0" width="500" height="500"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
- <animations/>
- <webPreferences key="preferences" defaultFontSize="12" defaultFixedFontSize="12">
- <nil key="identifier"/>
- </webPreferences>
- </webView>
- </subviews>
- <animations/>
- <connections>
- <outlet property="webView" destination="5" id="19"/>
- </connections>
- </view>
- <connections>
- <binding destination="-2" name="title" keyPath="contentView.webView.mainFrameTitle" id="31"/>
- </connections>
- </window>
- </objects>
-</document>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/main.m b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/main.m
deleted file mode 100644
index 4ad50ad5..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/main.m
+++ /dev/null
@@ -1,14 +0,0 @@
-//
-// main.m
-// MacGap
-//
-// Created by Alex MacCaw on 08/01/2012.
-// Copyright (c) 2012 Twitter. All rights reserved.
-//
-
-#import <Cocoa/Cocoa.h>
-
-int main(int argc, char *argv[])
-{
- return NSApplicationMain(argc, (const char **)argv);
-}
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/README.md b/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/README.md
deleted file mode 100644
index daf3eae9..00000000
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Mac Web UI Wrapper
-======
-
-This is a modified version of MacGap1 which launches a WebKit view and accesses the local ZeroTier service at its web URL. It builds the URL from the authtoken.secret file in the system home (or the user home) and the zerotier-one.port file that ZeroTier creates to advertise its control port.
-
-It's based on the original MacGap1 source by Twitter, Inc. which is licensed under the MIT license.
diff --git a/ext/installfiles/mac/postinst.sh b/ext/installfiles/mac/postinst.sh
index da15f9c8..b4ea2ee2 100755
--- a/ext/installfiles/mac/postinst.sh
+++ b/ext/installfiles/mac/postinst.sh
@@ -5,7 +5,7 @@ export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
OSX_RELEASE=`sw_vers -productVersion | cut -d . -f 1,2`
launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1
-sleep 1
+sleep 0.5
cd "/Library/Application Support/ZeroTier/One"
@@ -22,25 +22,40 @@ if [ "$OSX_RELEASE" = "10.7" ]; then
rm -f tap.kext.10_7.tar.gz
fi
-rm -rf node.log node.log.old root-topology shutdownIfUnreadable autoupdate.log updates.d
+rm -rf node.log node.log.old root-topology shutdownIfUnreadable autoupdate.log updates.d ui peers.save
+
chown -R 0 tap.kext
chgrp -R 0 tap.kext
+
if [ ! -f authtoken.secret ]; then
- head -c 4096 /dev/urandom | md5 | head -c 24 >authtoken.secret
+ head -c 1024 /dev/urandom | md5 | head -c 24 >authtoken.secret
chown 0 authtoken.secret
chgrp 0 authtoken.secret
chmod 0600 authtoken.secret
fi
+
rm -f zerotier-cli zerotier-idtool
ln -sf zerotier-one zerotier-cli
ln -sf zerotier-one zerotier-idtool
-
mkdir -p /usr/local/bin
cd /usr/local/bin
rm -f zerotier-cli zerotier-idtool
ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" zerotier-cli
ln -sf "/Library/Application Support/ZeroTier/One/zerotier-one" zerotier-idtool
+cd "/Library/Application Support/ZeroTier/One"
+kextload -r . tap.kext >>/dev/null 2>&1 &
+disown %1
+
launchctl load /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1
+sleep 1
+
+if [ -f /tmp/zt1-gui-restart.tmp ]; then
+ for u in `cat /tmp/zt1-gui-restart.tmp`; do
+ su $u -c '/Applications/ZeroTier\ One.app/Contents/MacOS/ZeroTier\ One &' >>/dev/null 2>&1 &
+ done
+fi
+rm -f /tmp/zt1-gui-restart.tmp
+
exit 0
diff --git a/ext/installfiles/mac/preinst.sh b/ext/installfiles/mac/preinst.sh
index c2cb494b..af2a9320 100755
--- a/ext/installfiles/mac/preinst.sh
+++ b/ext/installfiles/mac/preinst.sh
@@ -2,6 +2,19 @@
export PATH=/bin:/usr/bin:/sbin:/usr/sbin
+rm -f /tmp/zt1-gui-restart.tmp
+for i in `ps axuwww | tr -s ' ' ',' | grep -F '/Applications/ZeroTier,One.app' | grep -F -v grep | cut -d , -f 1,2 | xargs`; do
+ u=`echo $i | cut -d , -f 1`
+ p=`echo $i | cut -d , -f 2`
+ if [ ! -z "$u" -a "0$p" -gt 0 ]; then
+ kill $p >>/dev/null 2>&1
+ sleep 0.2
+ kill -9 $p >>/dev/null 2>&1
+ echo "$u" >>/tmp/zt1-gui-restart.tmp
+ fi
+done
+chmod 0600 /tmp/zt1-gui-restart.tmp
+
if [ -f /Library/LaunchDaemons/com.zerotier.one.plist ]; then
launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist >>/dev/null 2>&1
fi
diff --git a/ext/installfiles/mac/ui/Makefile b/ext/installfiles/mac/ui/Makefile
deleted file mode 100644
index 4be03228..00000000
--- a/ext/installfiles/mac/ui/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-all:
- mkdir -p build
- jsx --target es3 -x jsx . ./build
- rm -f ztui.min.js
- minify build/*.js >>ztui.min.js
- rm -rf build
diff --git a/ext/installfiles/mac/ui/README.md b/ext/installfiles/mac/ui/README.md
deleted file mode 100644
index bd5eddb6..00000000
--- a/ext/installfiles/mac/ui/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-ZeroTier HTML5 UI
-======
-
-This is the new (as of 1.0.3) ZeroTier One UI. It's implemented in HTML5 and React.
-
-If you make changes to the .jsx files, type 'make'. You will need NodeJS, react-tools, and minify installed and available in your path.
-
-For this to work, these files must be installed in the 'ui' subfolder of the ZeroTier home path. For development it's nice to symlink this to the 'ui' folder in your working directory. If the 'ui' subfolder is not present, the UI static files will not be served by the embedded web server.
-
-Packaging for Mac and Windows is accomplished by way of the wrappers in ext/. For Mac this is done with a modified version of MacGap. Windows uses a custom project that embeds a web view.
diff --git a/ext/installfiles/mac/ui/ZeroTierNetwork.jsx b/ext/installfiles/mac/ui/ZeroTierNetwork.jsx
deleted file mode 100644
index f842d758..00000000
--- a/ext/installfiles/mac/ui/ZeroTierNetwork.jsx
+++ /dev/null
@@ -1,74 +0,0 @@
-var ZeroTierNetwork = React.createClass({
- getInitialState: function() {
- return {};
- },
-
- leaveNetwork: function(event) {
- Ajax.call({
- url: 'network/'+this.props.nwid+'?auth='+this.props.authToken,
- cache: false,
- type: 'DELETE',
- success: function(data) {
- if (this.props.onNetworkDeleted)
- this.props.onNetworkDeleted(this.props.nwid);
- }.bind(this),
- error: function(error) {
- }.bind(this)
- });
- event.preventDefault();
- },
-
- render: function() {
- return (
- <div className="zeroTierNetwork">
- <div className="networkInfo">
- <span className="networkId">{this.props.nwid}</span>&nbsp;
- <span className="networkName">{this.props.name}</span>
- </div>
- <div className="networkProps">
- <div className="row">
- <div className="name">Status</div>
- <div className="value">{this.props['status']}</div>
- </div>
- <div className="row">
- <div className="name">Type</div>
- <div className="value">{this.props['type']}</div>
- </div>
- <div className="row">
- <div className="name">MAC</div>
- <div className="value zeroTierAddress">{this.props['mac']}</div>
- </div>
- <div className="row">
- <div className="name">MTU</div>
- <div className="value">{this.props['mtu']}</div>
- </div>
- <div className="row">
- <div className="name">Broadcast</div>
- <div className="value">{(this.props['broadcastEnabled']) ? 'ENABLED' : 'DISABLED'}</div>
- </div>
- <div className="row">
- <div className="name">Bridging</div>
- <div className="value">{(this.props['bridge']) ? 'ACTIVE' : 'DISABLED'}</div>
- </div>
- <div className="row">
- <div className="name">Device</div>
- <div className="value">{(this.props['portDeviceName']) ? this.props['portDeviceName'] : '(none)'}</div>
- </div>
- <div className="row">
- <div className="name">Managed&nbsp;IPs</div>
- <div className="value ipList">
- {
- this.props['assignedAddresses'].map(function(ipAssignment) {
- return (
- <div key={ipAssignment} className="ipAddress">{ipAssignment}</div>
- );
- })
- }
- </div>
- </div>
- </div>
- <button type="button" className="leaveNetworkButton" onClick={this.leaveNetwork}>Leave&nbsp;Network</button>
- </div>
- );
- }
-});
diff --git a/ext/installfiles/mac/ui/ZeroTierNode.jsx b/ext/installfiles/mac/ui/ZeroTierNode.jsx
deleted file mode 100644
index b4c29220..00000000
--- a/ext/installfiles/mac/ui/ZeroTierNode.jsx
+++ /dev/null
@@ -1,158 +0,0 @@
-var ZeroTierNode = React.createClass({
- getInitialState: function() {
- return {
- address: '----------',
- online: false,
- version: '_._._',
- _networks: [],
- _peers: []
- };
- },
-
- ago: function(ms) {
- if (ms > 0) {
- var tmp = Math.round((Date.now() - ms) / 1000);
- return ((tmp > 0) ? tmp : 0);
- } else return 0;
- },
-
- updatePeers: function() {
- Ajax.call({
- url: 'peer?auth='+this.props.authToken,
- cache: false,
- type: 'GET',
- success: function(data) {
- if (data) {
- var pl = JSON.parse(data);
- if (Array.isArray(pl)) {
- this.setState({_peers: pl});
- }
- }
- }.bind(this),
- error: function() {
- }.bind(this)
- });
- },
- updateNetworks: function() {
- Ajax.call({
- url: 'network?auth='+this.props.authToken,
- cache: false,
- type: 'GET',
- success: function(data) {
- if (data) {
- var nwl = JSON.parse(data);
- if (Array.isArray(nwl)) {
- this.setState({_networks: nwl});
- }
- }
- }.bind(this),
- error: function() {
- }.bind(this)
- });
- },
- updateAll: function() {
- Ajax.call({
- url: 'status?auth='+this.props.authToken,
- cache: false,
- type: 'GET',
- success: function(data) {
- this.alertedToFailure = false;
- if (data) {
- var status = JSON.parse(data);
- this.setState(status);
- document.title = 'ZeroTier One [' + status.address + ']';
- }
- this.updateNetworks();
- this.updatePeers();
- }.bind(this),
- error: function() {
- this.setState(this.getInitialState());
- if (!this.alertedToFailure) {
- this.alertedToFailure = true;
- alert('Authorization token invalid or ZeroTier One service not running.');
- }
- }.bind(this)
- });
- },
- joinNetwork: function(event) {
- event.preventDefault();
- if ((this.networkToJoin)&&(this.networkToJoin.length === 16)) {
- Ajax.call({
- url: 'network/'+this.networkToJoin+'?auth='+this.props.authToken,
- cache: false,
- type: 'POST',
- success: function(data) {
- this.networkToJoin = '';
- if (this.networkInputElement)
- this.networkInputElement.value = '';
- this.updateNetworks();
- }.bind(this),
- error: function() {
- }.bind(this)
- });
- } else {
- alert('To join a network, enter its 16-digit network ID.');
- }
- },
- handleNetworkIdEntry: function(event) {
- this.networkInputElement = event.target;
- var nid = this.networkInputElement.value;
- if (nid) {
- nid = nid.toLowerCase();
- var nnid = '';
- for(var i=0;((i<nid.length)&&(i<16));++i) {
- if ("0123456789abcdef".indexOf(nid.charAt(i)) >= 0)
- nnid += nid.charAt(i);
- }
- this.networkToJoin = nnid;
- this.networkInputElement.value = nnid;
- } else {
- this.networkToJoin = '';
- this.networkInputElement.value = '';
- }
- },
-
- handleNetworkDelete: function(nwid) {
- var networks = [];
- for(var i=0;i<this.state._networks.length;++i) {
- if (this.state._networks[i].nwid !== nwid)
- networks.push(this.state._networks[i]);
- }
- this.setState({_networks: networks});
- },
-
- componentDidMount: function() {
- this.updateAll();
- this.updateIntervalId = setInterval(this.updateAll,2500);
- },
- componentWillUnmount: function() {
- clearInterval(this.updateIntervalId);
- },
- render: function() {
- return (
- <div className="zeroTierNode">
- <div className="middle"><div className="middleCell">
- <div className="middleScroll">
- <div className="networks" key="_networks">
- {
- this.state._networks.map(function(network) {
- network['authToken'] = this.props.authToken;
- network['onNetworkDeleted'] = this.handleNetworkDelete;
- return React.createElement('div',{className: 'network',key: network.nwid},React.createElement(ZeroTierNetwork,network));
- }.bind(this))
- }
- </div>
- </div>
- </div></div>
- <div className="bottom">
- <div className="left">
- <span className="statusLine"><span className="zeroTierAddress">{this.state.address}</span>&nbsp;&nbsp;{this.state.online ? (this.state.tcpFallbackActive ? 'TUNNELED' : 'ONLINE') : 'OFFLINE'}&nbsp;&nbsp;{this.state.version}</span>
- </div>
- <div className="right">
- <form onSubmit={this.joinNetwork}><input type="text" maxlength="16" placeholder="[ Network ID ]" onChange={this.handleNetworkIdEntry} size="16"/><button type="button" onClick={this.joinNetwork}>Join</button></form>
- </div>
- </div>
- </div>
- );
- }
-});
diff --git a/ext/installfiles/mac/ui/index.html b/ext/installfiles/mac/ui/index.html
deleted file mode 100644
index 44edb399..00000000
--- a/ext/installfiles/mac/ui/index.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>ZeroTier One</title>
- <link rel="stylesheet" href="zerotier.css">
- <script src="simpleajax.min.js"></script>
- <!-- <script src="https://fb.me/react-0.13.2.js"></script> -->
- <script src="react.min.js"></script>
- <script src="ztui.min.js"></script>
-</head>
-<body><div style="width: 100%; height: 100%;" id="main"></div></body>
-<script src="main.js"></script>
-<script>
-/* Windows hacks */
-function isIE() {
- var myNav = navigator.userAgent.toLowerCase();
- return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
-}
-var ieVersion = isIE();
-function resizeMiddleScrollClasses() {
- var elems = document.getElementsByTagName('*'), i;
- for (i in elems) {
- if ((' ' + elems[i].className + ' ').indexOf(' middleScroll ') > -1) {
- elems[i].style.height = (document.body.clientHeight - (elems[i].parentNode.parentNode.previousElementSibling.clientHeight + elems[i].parentNode.parentNode.nextElementSibling.clientHeight)) + "px";
- }
- }
-}
-if (ieVersion !== false) {
- if (ieVersion < 7) {
- alert("Upgrade Internet Explorer on your system to use this interface. (detected version: " + ieVersion + ")");
- } else {
- resizeMiddleScrollClasses();
- window.onresize = resizeMiddleScrollClasses;
- }
-}
-
-/* MacGap hacks */
-if (typeof macgap !== 'undefined') {
- if (macgap.menu) {
- var tmp = macgap.menu.getItem("Help");
- if (tmp)
- tmp.remove();
- tmp = macgap.menu.getItem("Format");
- if (tmp)
- tmp.remove();
- tmp = macgap.menu.getItem("View");
- if (tmp)
- tmp.remove();
- tmp = macgap.menu.getItem("File");
- if (tmp)
- tmp.remove();
- }
-}
-</script>
-</html>
diff --git a/ext/installfiles/mac/ui/main.js b/ext/installfiles/mac/ui/main.js
deleted file mode 100644
index a1647127..00000000
--- a/ext/installfiles/mac/ui/main.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015 ZeroTier, 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 <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-function getUrlParameter(parameter)
-{
- var currLocation = window.location.search;
- if (currLocation.indexOf('?') < 0)
- return '';
- var parArr = currLocation.split("?")[1].split("&");
- for(var i = 0; i < parArr.length; i++){
- parr = parArr[i].split("=");
- if (parr[0] == parameter) {
- return decodeURIComponent(parr[1]);
- }
- }
- return '';
-}
-
-var ztAuthToken = getUrlParameter('authToken');
-if ((!ztAuthToken)||(ztAuthToken.length <= 0)) {
- ztAuthToken = prompt('No authToken specified in URL. Enter token from\nauthtoken.secret to authorize.');
-}
-
-React.render(
- React.createElement(ZeroTierNode, {authToken: ztAuthToken}),
- document.getElementById('main')
-);
diff --git a/ext/installfiles/mac/ui/react.min.js b/ext/installfiles/mac/ui/react.min.js
deleted file mode 100644
index 9040c970..00000000
--- a/ext/installfiles/mac/ui/react.min.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * React v0.13.2
- *
- * Copyright 2013-2015, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.React=e()}}(function(){return function e(t,n,r){function o(a,u){if(!n[a]){if(!t[a]){var s="function"==typeof require&&require;if(!u&&s)return s(a,!0);if(i)return i(a,!0);var l=new Error("Cannot find module '"+a+"'");throw l.code="MODULE_NOT_FOUND",l}var c=n[a]={exports:{}};t[a][0].call(c.exports,function(e){var n=t[a][1][e];return o(n?n:e)},c,c.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a<r.length;a++)o(r[a]);return o}({1:[function(e,t,n){"use strict";var r=e(19),o=e(32),i=e(34),a=e(33),u=e(38),s=e(39),l=e(55),c=(e(56),e(40)),p=e(51),d=e(54),f=e(64),h=e(68),m=e(73),v=e(76),g=e(79),y=e(82),C=e(27),E=e(115),b=e(142);d.inject();var _=l.createElement,x=l.createFactory,D=l.cloneElement,M=m.measure("React","render",h.render),N={Children:{map:o.map,forEach:o.forEach,count:o.count,only:b},Component:i,DOM:c,PropTypes:v,initializeTouchEvents:function(e){r.useTouchEvents=e},createClass:a.createClass,createElement:_,cloneElement:D,createFactory:x,createMixin:function(e){return e},constructAndRenderComponent:h.constructAndRenderComponent,constructAndRenderComponentByID:h.constructAndRenderComponentByID,findDOMNode:E,render:M,renderToString:y.renderToString,renderToStaticMarkup:y.renderToStaticMarkup,unmountComponentAtNode:h.unmountComponentAtNode,isValidElement:l.isValidElement,withContext:u.withContext,__spread:C};"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject&&__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({CurrentOwner:s,InstanceHandles:f,Mount:h,Reconciler:g,TextComponent:p});N.version="0.13.2",t.exports=N},{115:115,142:142,19:19,27:27,32:32,33:33,34:34,38:38,39:39,40:40,51:51,54:54,55:55,56:56,64:64,68:68,73:73,76:76,79:79,82:82}],2:[function(e,t,n){"use strict";var r=e(117),o={componentDidMount:function(){this.props.autoFocus&&r(this.getDOMNode())}};t.exports=o},{117:117}],3:[function(e,t,n){"use strict";function r(){var e=window.opera;return"object"==typeof e&&"function"==typeof e.version&&parseInt(e.version(),10)<=12}function o(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function i(e){switch(e){case T.topCompositionStart:return R.compositionStart;case T.topCompositionEnd:return R.compositionEnd;case T.topCompositionUpdate:return R.compositionUpdate}}function a(e,t){return e===T.topKeyDown&&t.keyCode===b}function u(e,t){switch(e){case T.topKeyUp:return-1!==E.indexOf(t.keyCode);case T.topKeyDown:return t.keyCode!==b;case T.topKeyPress:case T.topMouseDown:case T.topBlur:return!0;default:return!1}}function s(e){var t=e.detail;return"object"==typeof t&&"data"in t?t.data:null}function l(e,t,n,r){var o,l;if(_?o=i(e):w?u(e,r)&&(o=R.compositionEnd):a(e,r)&&(o=R.compositionStart),!o)return null;M&&(w||o!==R.compositionStart?o===R.compositionEnd&&w&&(l=w.getData()):w=v.getPooled(t));var c=g.getPooled(o,n,r);if(l)c.data=l;else{var p=s(r);null!==p&&(c.data=p)}return h.accumulateTwoPhaseDispatches(c),c}function c(e,t){switch(e){case T.topCompositionEnd:return s(t);case T.topKeyPress:var n=t.which;return n!==N?null:(P=!0,I);case T.topTextInput:var r=t.data;return r===I&&P?null:r;default:return null}}function p(e,t){if(w){if(e===T.topCompositionEnd||u(e,t)){var n=w.getData();return v.release(w),w=null,n}return null}switch(e){case T.topPaste:return null;case T.topKeyPress:return t.which&&!o(t)?String.fromCharCode(t.which):null;case T.topCompositionEnd:return M?null:t.data;default:return null}}function d(e,t,n,r){var o;if(o=D?c(e,r):p(e,r),!o)return null;var i=y.getPooled(R.beforeInput,n,r);return i.data=o,h.accumulateTwoPhaseDispatches(i),i}var f=e(15),h=e(20),m=e(21),v=e(22),g=e(91),y=e(95),C=e(139),E=[9,13,27,32],b=229,_=m.canUseDOM&&"CompositionEvent"in window,x=null;m.canUseDOM&&"documentMode"in document&&(x=document.documentMode);var D=m.canUseDOM&&"TextEvent"in window&&!x&&!r(),M=m.canUseDOM&&(!_||x&&x>8&&11>=x),N=32,I=String.fromCharCode(N),T=f.topLevelTypes,R={beforeInput:{phasedRegistrationNames:{bubbled:C({onBeforeInput:null}),captured:C({onBeforeInputCapture:null})},dependencies:[T.topCompositionEnd,T.topKeyPress,T.topTextInput,T.topPaste]},compositionEnd:{phasedRegistrationNames:{bubbled:C({onCompositionEnd:null}),captured:C({onCompositionEndCapture:null})},dependencies:[T.topBlur,T.topCompositionEnd,T.topKeyDown,T.topKeyPress,T.topKeyUp,T.topMouseDown]},compositionStart:{phasedRegistrationNames:{bubbled:C({onCompositionStart:null}),captured:C({onCompositionStartCapture:null})},dependencies:[T.topBlur,T.topCompositionStart,T.topKeyDown,T.topKeyPress,T.topKeyUp,T.topMouseDown]},compositionUpdate:{phasedRegistrationNames:{bubbled:C({onCompositionUpdate:null}),captured:C({onCompositionUpdateCapture:null})},dependencies:[T.topBlur,T.topCompositionUpdate,T.topKeyDown,T.topKeyPress,T.topKeyUp,T.topMouseDown]}},P=!1,w=null,O={eventTypes:R,extractEvents:function(e,t,n,r){return[l(e,t,n,r),d(e,t,n,r)]}};t.exports=O},{139:139,15:15,20:20,21:21,22:22,91:91,95:95}],4:[function(e,t,n){"use strict";function r(e,t){return e+t.charAt(0).toUpperCase()+t.substring(1)}var o={boxFlex:!0,boxFlexGroup:!0,columnCount:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,strokeDashoffset:!0,strokeOpacity:!0,strokeWidth:!0},i=["Webkit","ms","Moz","O"];Object.keys(o).forEach(function(e){i.forEach(function(t){o[r(t,e)]=o[e]})});var a={background:{backgroundImage:!0,backgroundPosition:!0,backgroundRepeat:!0,backgroundColor:!0},border:{borderWidth:!0,borderStyle:!0,borderColor:!0},borderBottom:{borderBottomWidth:!0,borderBottomStyle:!0,borderBottomColor:!0},borderLeft:{borderLeftWidth:!0,borderLeftStyle:!0,borderLeftColor:!0},borderRight:{borderRightWidth:!0,borderRightStyle:!0,borderRightColor:!0},borderTop:{borderTopWidth:!0,borderTopStyle:!0,borderTopColor:!0},font:{fontStyle:!0,fontVariant:!0,fontWeight:!0,fontSize:!0,lineHeight:!0,fontFamily:!0}},u={isUnitlessNumber:o,shorthandPropertyExpansions:a};t.exports=u},{}],5:[function(e,t,n){"use strict";var r=e(4),o=e(21),i=(e(106),e(111)),a=e(131),u=e(141),s=(e(150),u(function(e){return a(e)})),l="cssFloat";o.canUseDOM&&void 0===document.documentElement.style.cssFloat&&(l="styleFloat");var c={createMarkupForStyles:function(e){var t="";for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];null!=r&&(t+=s(n)+":",t+=i(n,r)+";")}return t||null},setValueForStyles:function(e,t){var n=e.style;for(var o in t)if(t.hasOwnProperty(o)){var a=i(o,t[o]);if("float"===o&&(o=l),a)n[o]=a;else{var u=r.shorthandPropertyExpansions[o];if(u)for(var s in u)n[s]="";else n[o]=""}}}};t.exports=c},{106:106,111:111,131:131,141:141,150:150,21:21,4:4}],6:[function(e,t,n){"use strict";function r(){this._callbacks=null,this._contexts=null}var o=e(28),i=e(27),a=e(133);i(r.prototype,{enqueue:function(e,t){this._callbacks=this._callbacks||[],this._contexts=this._contexts||[],this._callbacks.push(e),this._contexts.push(t)},notifyAll:function(){var e=this._callbacks,t=this._contexts;if(e){a(e.length===t.length),this._callbacks=null,this._contexts=null;for(var n=0,r=e.length;r>n;n++)e[n].call(t[n]);e.length=0,t.length=0}},reset:function(){this._callbacks=null,this._contexts=null},destructor:function(){this.reset()}}),o.addPoolingTo(r),t.exports=r},{133:133,27:27,28:28}],7:[function(e,t,n){"use strict";function r(e){return"SELECT"===e.nodeName||"INPUT"===e.nodeName&&"file"===e.type}function o(e){var t=x.getPooled(T.change,P,e);E.accumulateTwoPhaseDispatches(t),_.batchedUpdates(i,t)}function i(e){C.enqueueEvents(e),C.processEventQueue()}function a(e,t){R=e,P=t,R.attachEvent("onchange",o)}function u(){R&&(R.detachEvent("onchange",o),R=null,P=null)}function s(e,t,n){return e===I.topChange?n:void 0}function l(e,t,n){e===I.topFocus?(u(),a(t,n)):e===I.topBlur&&u()}function c(e,t){R=e,P=t,w=e.value,O=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(R,"value",k),R.attachEvent("onpropertychange",d)}function p(){R&&(delete R.value,R.detachEvent("onpropertychange",d),R=null,P=null,w=null,O=null)}function d(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==w&&(w=t,o(e))}}function f(e,t,n){return e===I.topInput?n:void 0}function h(e,t,n){e===I.topFocus?(p(),c(t,n)):e===I.topBlur&&p()}function m(e,t,n){return e!==I.topSelectionChange&&e!==I.topKeyUp&&e!==I.topKeyDown||!R||R.value===w?void 0:(w=R.value,P)}function v(e){return"INPUT"===e.nodeName&&("checkbox"===e.type||"radio"===e.type)}function g(e,t,n){return e===I.topClick?n:void 0}var y=e(15),C=e(17),E=e(20),b=e(21),_=e(85),x=e(93),D=e(134),M=e(136),N=e(139),I=y.topLevelTypes,T={change:{phasedRegistrationNames:{bubbled:N({onChange:null}),captured:N({onChangeCapture:null})},dependencies:[I.topBlur,I.topChange,I.topClick,I.topFocus,I.topInput,I.topKeyDown,I.topKeyUp,I.topSelectionChange]}},R=null,P=null,w=null,O=null,S=!1;b.canUseDOM&&(S=D("change")&&(!("documentMode"in document)||document.documentMode>8));var A=!1;b.canUseDOM&&(A=D("input")&&(!("documentMode"in document)||document.documentMode>9));var k={get:function(){return O.get.call(this)},set:function(e){w=""+e,O.set.call(this,e)}},L={eventTypes:T,extractEvents:function(e,t,n,o){var i,a;if(r(t)?S?i=s:a=l:M(t)?A?i=f:(i=m,a=h):v(t)&&(i=g),i){var u=i(e,t,n);if(u){var c=x.getPooled(T.change,u,o);return E.accumulateTwoPhaseDispatches(c),c}}a&&a(e,t,n)}};t.exports=L},{134:134,136:136,139:139,15:15,17:17,20:20,21:21,85:85,93:93}],8:[function(e,t,n){"use strict";var r=0,o={createReactRootIndex:function(){return r++}};t.exports=o},{}],9:[function(e,t,n){"use strict";function r(e,t,n){e.insertBefore(t,e.childNodes[n]||null)}var o=e(12),i=e(70),a=e(145),u=e(133),s={dangerouslyReplaceNodeWithMarkup:o.dangerouslyReplaceNodeWithMarkup,updateTextContent:a,processUpdates:function(e,t){for(var n,s=null,l=null,c=0;c<e.length;c++)if(n=e[c],n.type===i.MOVE_EXISTING||n.type===i.REMOVE_NODE){var p=n.fromIndex,d=n.parentNode.childNodes[p],f=n.parentID;u(d),s=s||{},s[f]=s[f]||[],s[f][p]=d,l=l||[],l.push(d)}var h=o.dangerouslyRenderMarkup(t);if(l)for(var m=0;m<l.length;m++)l[m].parentNode.removeChild(l[m]);for(var v=0;v<e.length;v++)switch(n=e[v],n.type){case i.INSERT_MARKUP:r(n.parentNode,h[n.markupIndex],n.toIndex);break;case i.MOVE_EXISTING:r(n.parentNode,s[n.parentID][n.fromIndex],n.toIndex);break;case i.TEXT_CONTENT:a(n.parentNode,n.textContent);break;case i.REMOVE_NODE:}}};t.exports=s},{12:12,133:133,145:145,70:70}],10:[function(e,t,n){"use strict";function r(e,t){return(e&t)===t}var o=e(133),i={MUST_USE_ATTRIBUTE:1,MUST_USE_PROPERTY:2,HAS_SIDE_EFFECTS:4,HAS_BOOLEAN_VALUE:8,HAS_NUMERIC_VALUE:16,HAS_POSITIVE_NUMERIC_VALUE:48,HAS_OVERLOADED_BOOLEAN_VALUE:64,injectDOMPropertyConfig:function(e){var t=e.Properties||{},n=e.DOMAttributeNames||{},a=e.DOMPropertyNames||{},s=e.DOMMutationMethods||{};e.isCustomAttribute&&u._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var l in t){o(!u.isStandardName.hasOwnProperty(l)),u.isStandardName[l]=!0;var c=l.toLowerCase();if(u.getPossibleStandardName[c]=l,n.hasOwnProperty(l)){var p=n[l];u.getPossibleStandardName[p]=l,u.getAttributeName[l]=p}else u.getAttributeName[l]=c;u.getPropertyName[l]=a.hasOwnProperty(l)?a[l]:l,s.hasOwnProperty(l)?u.getMutationMethod[l]=s[l]:u.getMutationMethod[l]=null;var d=t[l];u.mustUseAttribute[l]=r(d,i.MUST_USE_ATTRIBUTE),u.mustUseProperty[l]=r(d,i.MUST_USE_PROPERTY),u.hasSideEffects[l]=r(d,i.HAS_SIDE_EFFECTS),u.hasBooleanValue[l]=r(d,i.HAS_BOOLEAN_VALUE),u.hasNumericValue[l]=r(d,i.HAS_NUMERIC_VALUE),u.hasPositiveNumericValue[l]=r(d,i.HAS_POSITIVE_NUMERIC_VALUE),u.hasOverloadedBooleanValue[l]=r(d,i.HAS_OVERLOADED_BOOLEAN_VALUE),o(!u.mustUseAttribute[l]||!u.mustUseProperty[l]),o(u.mustUseProperty[l]||!u.hasSideEffects[l]),o(!!u.hasBooleanValue[l]+!!u.hasNumericValue[l]+!!u.hasOverloadedBooleanValue[l]<=1)}}},a={},u={ID_ATTRIBUTE_NAME:"data-reactid",isStandardName:{},getPossibleStandardName:{},getAttributeName:{},getPropertyName:{},getMutationMethod:{},mustUseAttribute:{},mustUseProperty:{},hasSideEffects:{},hasBooleanValue:{},hasNumericValue:{},hasPositiveNumericValue:{},hasOverloadedBooleanValue:{},_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t<u._isCustomAttributeFunctions.length;t++){var n=u._isCustomAttributeFunctions[t];if(n(e))return!0}return!1},getDefaultValueForProperty:function(e,t){var n,r=a[e];return r||(a[e]=r={}),t in r||(n=document.createElement(e),r[t]=n[t]),r[t]},injection:i};t.exports=u},{133:133}],11:[function(e,t,n){"use strict";function r(e,t){return null==t||o.hasBooleanValue[e]&&!t||o.hasNumericValue[e]&&isNaN(t)||o.hasPositiveNumericValue[e]&&1>t||o.hasOverloadedBooleanValue[e]&&t===!1}var o=e(10),i=e(143),a=(e(150),{createMarkupForID:function(e){return o.ID_ATTRIBUTE_NAME+"="+i(e)},createMarkupForProperty:function(e,t){if(o.isStandardName.hasOwnProperty(e)&&o.isStandardName[e]){if(r(e,t))return"";var n=o.getAttributeName[e];return o.hasBooleanValue[e]||o.hasOverloadedBooleanValue[e]&&t===!0?n:n+"="+i(t)}return o.isCustomAttribute(e)?null==t?"":e+"="+i(t):null},setValueForProperty:function(e,t,n){if(o.isStandardName.hasOwnProperty(t)&&o.isStandardName[t]){var i=o.getMutationMethod[t];if(i)i(e,n);else if(r(t,n))this.deleteValueForProperty(e,t);else if(o.mustUseAttribute[t])e.setAttribute(o.getAttributeName[t],""+n);else{var a=o.getPropertyName[t];o.hasSideEffects[t]&&""+e[a]==""+n||(e[a]=n)}}else o.isCustomAttribute(t)&&(null==n?e.removeAttribute(t):e.setAttribute(t,""+n))},deleteValueForProperty:function(e,t){if(o.isStandardName.hasOwnProperty(t)&&o.isStandardName[t]){var n=o.getMutationMethod[t];if(n)n(e,void 0);else if(o.mustUseAttribute[t])e.removeAttribute(o.getAttributeName[t]);else{var r=o.getPropertyName[t],i=o.getDefaultValueForProperty(e.nodeName,r);o.hasSideEffects[t]&&""+e[r]===i||(e[r]=i)}}else o.isCustomAttribute(t)&&e.removeAttribute(t)}});t.exports=a},{10:10,143:143,150:150}],12:[function(e,t,n){"use strict";function r(e){return e.substring(1,e.indexOf(" "))}var o=e(21),i=e(110),a=e(112),u=e(125),s=e(133),l=/^(<[^ \/>]+)/,c="data-danger-index",p={dangerouslyRenderMarkup:function(e){s(o.canUseDOM);for(var t,n={},p=0;p<e.length;p++)s(e[p]),t=r(e[p]),t=u(t)?t:"*",n[t]=n[t]||[],n[t][p]=e[p];var d=[],f=0;for(t in n)if(n.hasOwnProperty(t)){var h,m=n[t];for(h in m)if(m.hasOwnProperty(h)){var v=m[h];m[h]=v.replace(l,"$1 "+c+'="'+h+'" ')}for(var g=i(m.join(""),a),y=0;y<g.length;++y){var C=g[y];C.hasAttribute&&C.hasAttribute(c)&&(h=+C.getAttribute(c),C.removeAttribute(c),s(!d.hasOwnProperty(h)),d[h]=C,f+=1)}}return s(f===d.length),s(d.length===e.length),d},dangerouslyReplaceNodeWithMarkup:function(e,t){s(o.canUseDOM),s(t),s("html"!==e.tagName.toLowerCase());var n=i(t,a)[0];e.parentNode.replaceChild(n,e)}};t.exports=p},{110:110,112:112,125:125,133:133,21:21}],13:[function(e,t,n){"use strict";var r=e(139),o=[r({ResponderEventPlugin:null}),r({SimpleEventPlugin:null}),r({TapEventPlugin:null}),r({EnterLeaveEventPlugin:null}),r({ChangeEventPlugin:null}),r({SelectEventPlugin:null}),r({BeforeInputEventPlugin:null}),r({AnalyticsEventPlugin:null}),r({MobileSafariClickEventPlugin:null})];t.exports=o},{139:139}],14:[function(e,t,n){"use strict";var r=e(15),o=e(20),i=e(97),a=e(68),u=e(139),s=r.topLevelTypes,l=a.getFirstReactDOM,c={mouseEnter:{registrationName:u({onMouseEnter:null}),dependencies:[s.topMouseOut,s.topMouseOver]},mouseLeave:{registrationName:u({onMouseLeave:null}),dependencies:[s.topMouseOut,s.topMouseOver]}},p=[null,null],d={eventTypes:c,extractEvents:function(e,t,n,r){if(e===s.topMouseOver&&(r.relatedTarget||r.fromElement))return null;if(e!==s.topMouseOut&&e!==s.topMouseOver)return null;var u;if(t.window===t)u=t;else{var d=t.ownerDocument;u=d?d.defaultView||d.parentWindow:window}var f,h;if(e===s.topMouseOut?(f=t,h=l(r.relatedTarget||r.toElement)||u):(f=u,h=t),f===h)return null;var m=f?a.getID(f):"",v=h?a.getID(h):"",g=i.getPooled(c.mouseLeave,m,r);g.type="mouseleave",g.target=f,g.relatedTarget=h;var y=i.getPooled(c.mouseEnter,v,r);return y.type="mouseenter",y.target=h,y.relatedTarget=f,o.accumulateEnterLeaveDispatches(g,y,m,v),p[0]=g,p[1]=y,p}};t.exports=d},{139:139,15:15,20:20,68:68,97:97}],15:[function(e,t,n){"use strict";var r=e(138),o=r({bubbled:null,captured:null}),i=r({topBlur:null,topChange:null,topClick:null,topCompositionEnd:null,topCompositionStart:null,topCompositionUpdate:null,topContextMenu:null,topCopy:null,topCut:null,topDoubleClick:null,topDrag:null,topDragEnd:null,topDragEnter:null,topDragExit:null,topDragLeave:null,topDragOver:null,topDragStart:null,topDrop:null,topError:null,topFocus:null,topInput:null,topKeyDown:null,topKeyPress:null,topKeyUp:null,topLoad:null,topMouseDown:null,topMouseMove:null,topMouseOut:null,topMouseOver:null,topMouseUp:null,topPaste:null,topReset:null,topScroll:null,topSelectionChange:null,topSubmit:null,topTextInput:null,topTouchCancel:null,topTouchEnd:null,topTouchMove:null,topTouchStart:null,topWheel:null}),a={topLevelTypes:i,PropagationPhases:o};t.exports=a},{138:138}],16:[function(e,t,n){var r=e(112),o={listen:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!1),{remove:function(){e.removeEventListener(t,n,!1)}}):e.attachEvent?(e.attachEvent("on"+t,n),{remove:function(){e.detachEvent("on"+t,n)}}):void 0},capture:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!0),{remove:function(){e.removeEventListener(t,n,!0)}}):{remove:r}},registerDefault:function(){}};t.exports=o},{112:112}],17:[function(e,t,n){"use strict";var r=e(18),o=e(19),i=e(103),a=e(118),u=e(133),s={},l=null,c=function(e){if(e){var t=o.executeDispatch,n=r.getPluginModuleForEvent(e);n&&n.executeDispatch&&(t=n.executeDispatch),o.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e)}},p=null,d={injection:{injectMount:o.injection.injectMount,injectInstanceHandle:function(e){p=e},getInstanceHandle:function(){return p},injectEventPluginOrder:r.injectEventPluginOrder,injectEventPluginsByName:r.injectEventPluginsByName},eventNameDispatchConfigs:r.eventNameDispatchConfigs,registrationNameModules:r.registrationNameModules,putListener:function(e,t,n){u(!n||"function"==typeof n);var r=s[t]||(s[t]={});r[e]=n},getListener:function(e,t){var n=s[t];return n&&n[e]},deleteListener:function(e,t){var n=s[t];n&&delete n[e]},deleteAllListeners:function(e){for(var t in s)delete s[t][e]},extractEvents:function(e,t,n,o){for(var a,u=r.plugins,s=0,l=u.length;l>s;s++){var c=u[s];if(c){var p=c.extractEvents(e,t,n,o);p&&(a=i(a,p))}}return a},enqueueEvents:function(e){e&&(l=i(l,e))},processEventQueue:function(){var e=l;l=null,a(e,c),u(!l)},__purge:function(){s={}},__getListenerBank:function(){return s}};t.exports=d},{103:103,118:118,133:133,18:18,19:19}],18:[function(e,t,n){"use strict";function r(){if(u)for(var e in s){var t=s[e],n=u.indexOf(e);if(a(n>-1),!l.plugins[n]){a(t.extractEvents),l.plugins[n]=t;var r=t.eventTypes;for(var i in r)a(o(r[i],t,i))}}}function o(e,t,n){a(!l.eventNameDispatchConfigs.hasOwnProperty(n)),l.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var o in r)if(r.hasOwnProperty(o)){var u=r[o];i(u,t,n)}return!0}return e.registrationName?(i(e.registrationName,t,n),!0):!1}function i(e,t,n){a(!l.registrationNameModules[e]),l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=e(133),u=null,s={},l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},injectEventPluginOrder:function(e){a(!u),u=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];s.hasOwnProperty(n)&&s[n]===o||(a(!s[n]),s[n]=o,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;for(var n in t.phasedRegistrationNames)if(t.phasedRegistrationNames.hasOwnProperty(n)){var r=l.registrationNameModules[t.phasedRegistrationNames[n]];if(r)return r}return null},_resetEventPlugins:function(){u=null;for(var e in s)s.hasOwnProperty(e)&&delete s[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};t.exports=l},{133:133}],19:[function(e,t,n){"use strict";function r(e){return e===v.topMouseUp||e===v.topTouchEnd||e===v.topTouchCancel}function o(e){return e===v.topMouseMove||e===v.topTouchMove}function i(e){return e===v.topMouseDown||e===v.topTouchStart}function a(e,t){var n=e._dispatchListeners,r=e._dispatchIDs;if(Array.isArray(n))for(var o=0;o<n.length&&!e.isPropagationStopped();o++)t(e,n[o],r[o]);else n&&t(e,n,r)}function u(e,t,n){e.currentTarget=m.Mount.getNode(n);var r=t(e,n);return e.currentTarget=null,r}function s(e,t){a(e,t),e._dispatchListeners=null,e._dispatchIDs=null}function l(e){var t=e._dispatchListeners,n=e._dispatchIDs;if(Array.isArray(t)){for(var r=0;r<t.length&&!e.isPropagationStopped();r++)if(t[r](e,n[r]))return n[r]}else if(t&&t(e,n))return n;return null}function c(e){var t=l(e);return e._dispatchIDs=null,e._dispatchListeners=null,t}function p(e){var t=e._dispatchListeners,n=e._dispatchIDs;h(!Array.isArray(t));var r=t?t(e,n):null;return e._dispatchListeners=null,e._dispatchIDs=null,r}function d(e){return!!e._dispatchListeners}var f=e(15),h=e(133),m={Mount:null,injectMount:function(e){m.Mount=e}},v=f.topLevelTypes,g={isEndish:r,isMoveish:o,isStartish:i,executeDirectDispatch:p,executeDispatch:u,executeDispatchesInOrder:s,executeDispatchesInOrderStopAtTrue:c,hasDispatches:d,injection:m,useTouchEvents:!1};t.exports=g},{133:133,15:15}],20:[function(e,t,n){"use strict";function r(e,t,n){var r=t.dispatchConfig.phasedRegistrationNames[n];return v(e,r)}function o(e,t,n){var o=t?m.bubbled:m.captured,i=r(e,n,o);i&&(n._dispatchListeners=f(n._dispatchListeners,i),n._dispatchIDs=f(n._dispatchIDs,e))}function i(e){e&&e.dispatchConfig.phasedRegistrationNames&&d.injection.getInstanceHandle().traverseTwoPhase(e.dispatchMarker,o,e)}function a(e,t,n){if(n&&n.dispatchConfig.registrationName){var r=n.dispatchConfig.registrationName,o=v(e,r);o&&(n._dispatchListeners=f(n._dispatchListeners,o),n._dispatchIDs=f(n._dispatchIDs,e))}}function u(e){e&&e.dispatchConfig.registrationName&&a(e.dispatchMarker,null,e)}function s(e){h(e,i)}function l(e,t,n,r){d.injection.getInstanceHandle().traverseEnterLeave(n,r,a,e,t)}function c(e){h(e,u)}var p=e(15),d=e(17),f=e(103),h=e(118),m=p.PropagationPhases,v=d.getListener,g={accumulateTwoPhaseDispatches:s,accumulateDirectDispatches:c,accumulateEnterLeaveDispatches:l};t.exports=g},{103:103,118:118,15:15,17:17}],21:[function(e,t,n){"use strict";var r=!("undefined"==typeof window||!window.document||!window.document.createElement),o={canUseDOM:r,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};t.exports=o},{}],22:[function(e,t,n){"use strict";function r(e){this._root=e,this._startText=this.getText(),this._fallbackText=null}var o=e(28),i=e(27),a=e(128);i(r.prototype,{getText:function(){return"value"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var e,t,n=this._startText,r=n.length,o=this.getText(),i=o.length;for(e=0;r>e&&n[e]===o[e];e++);var a=r-e;for(t=1;a>=t&&n[r-t]===o[i-t];t++);var u=t>1?1-t:void 0;return this._fallbackText=o.slice(e,u),this._fallbackText}}),o.addPoolingTo(r),t.exports=r},{128:128,27:27,28:28}],23:[function(e,t,n){"use strict";var r,o=e(10),i=e(21),a=o.injection.MUST_USE_ATTRIBUTE,u=o.injection.MUST_USE_PROPERTY,s=o.injection.HAS_BOOLEAN_VALUE,l=o.injection.HAS_SIDE_EFFECTS,c=o.injection.HAS_NUMERIC_VALUE,p=o.injection.HAS_POSITIVE_NUMERIC_VALUE,d=o.injection.HAS_OVERLOADED_BOOLEAN_VALUE;if(i.canUseDOM){var f=document.implementation;r=f&&f.hasFeature&&f.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")}var h={isCustomAttribute:RegExp.prototype.test.bind(/^(data|aria)-[a-z_][a-z\d_.\-]*$/),Properties:{accept:null,acceptCharset:null,accessKey:null,action:null,allowFullScreen:a|s,allowTransparency:a,alt:null,async:s,autoComplete:null,autoPlay:s,cellPadding:null,cellSpacing:null,charSet:a,checked:u|s,classID:a,className:r?a:u,cols:a|p,colSpan:null,content:null,contentEditable:null,contextMenu:a,controls:u|s,coords:null,crossOrigin:null,data:null,dateTime:a,defer:s,dir:null,disabled:a|s,download:d,draggable:null,encType:null,form:a,formAction:a,formEncType:a,formMethod:a,formNoValidate:s,formTarget:a,frameBorder:a,headers:null,height:a,hidden:a|s,high:null,href:null,hrefLang:null,htmlFor:null,httpEquiv:null,icon:null,id:u,label:null,lang:null,list:a,loop:u|s,low:null,manifest:a,marginHeight:null,marginWidth:null,max:null,maxLength:a,media:a,mediaGroup:null,method:null,min:null,multiple:u|s,muted:u|s,name:null,noValidate:s,open:s,optimum:null,pattern:null,placeholder:null,poster:null,preload:null,radioGroup:null,readOnly:u|s,rel:null,required:s,role:a,rows:a|p,rowSpan:null,sandbox:null,scope:null,scoped:s,scrolling:null,seamless:a|s,selected:u|s,shape:null,size:a|p,sizes:a,span:p,spellCheck:null,src:null,srcDoc:u,srcSet:a,start:c,step:null,style:null,tabIndex:null,target:null,title:null,type:null,useMap:null,value:u|l,width:a,wmode:a,autoCapitalize:null,autoCorrect:null,itemProp:a,itemScope:a|s,itemType:a,itemID:a,itemRef:a,property:null,unselectable:a},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMPropertyNames:{autoCapitalize:"autocapitalize",autoComplete:"autocomplete",autoCorrect:"autocorrect",autoFocus:"autofocus",autoPlay:"autoplay",encType:"encoding",hrefLang:"hreflang",radioGroup:"radiogroup",spellCheck:"spellcheck",srcDoc:"srcdoc",srcSet:"srcset"}};t.exports=h},{10:10,21:21}],24:[function(e,t,n){"use strict";function r(e){l(null==e.props.checkedLink||null==e.props.valueLink)}function o(e){r(e),l(null==e.props.value&&null==e.props.onChange)}function i(e){r(e),l(null==e.props.checked&&null==e.props.onChange)}function a(e){this.props.valueLink.requestChange(e.target.value)}function u(e){this.props.checkedLink.requestChange(e.target.checked)}var s=e(76),l=e(133),c={button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0},p={Mixin:{propTypes:{value:function(e,t,n){return!e[t]||c[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.")},checked:function(e,t,n){return!e[t]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.")},onChange:s.func}},getValue:function(e){return e.props.valueLink?(o(e),e.props.valueLink.value):e.props.value},getChecked:function(e){return e.props.checkedLink?(i(e),e.props.checkedLink.value):e.props.checked},getOnChange:function(e){return e.props.valueLink?(o(e),a):e.props.checkedLink?(i(e),u):e.props.onChange}};t.exports=p},{133:133,76:76}],25:[function(e,t,n){"use strict";function r(e){e.remove()}var o=e(30),i=e(103),a=e(118),u=e(133),s={trapBubbledEvent:function(e,t){u(this.isMounted());var n=this.getDOMNode();u(n);var r=o.trapBubbledEvent(e,t,n);this._localEventListeners=i(this._localEventListeners,r)},componentWillUnmount:function(){this._localEventListeners&&a(this._localEventListeners,r)}};t.exports=s},{103:103,118:118,133:133,30:30}],26:[function(e,t,n){"use strict";var r=e(15),o=e(112),i=r.topLevelTypes,a={eventTypes:null,extractEvents:function(e,t,n,r){if(e===i.topTouchStart){var a=r.target;a&&!a.onclick&&(a.onclick=o)}}};t.exports=a},{112:112,15:15}],27:[function(e,t,n){"use strict";function r(e,t){if(null==e)throw new TypeError("Object.assign target cannot be null or undefined");for(var n=Object(e),r=Object.prototype.hasOwnProperty,o=1;o<arguments.length;o++){var i=arguments[o];if(null!=i){var a=Object(i);for(var u in a)r.call(a,u)&&(n[u]=a[u])}}return n}t.exports=r},{}],28:[function(e,t,n){"use strict";var r=e(133),o=function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)},i=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,e,t,n),o}return new r(e,t,n)},u=function(e,t,n,r,o){var i=this;if(i.instancePool.length){var a=i.instancePool.pop();return i.call(a,e,t,n,r,o),a}return new i(e,t,n,r,o)},s=function(e){var t=this;r(e instanceof t),e.destructor&&e.destructor(),t.instancePool.length<t.poolSize&&t.instancePool.push(e)},l=10,c=o,p=function(e,t){var n=e;return n.instancePool=[],n.getPooled=t||c,n.poolSize||(n.poolSize=l),n.release=s,n},d={addPoolingTo:p,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:a,fiveArgumentPooler:u};t.exports=d},{133:133}],29:[function(e,t,n){"use strict";var r=e(115),o={getDOMNode:function(){return r(this)}};t.exports=o},{115:115}],30:[function(e,t,n){"use strict";function r(e){return Object.prototype.hasOwnProperty.call(e,m)||(e[m]=f++,p[e[m]]={}),p[e[m]]}var o=e(15),i=e(17),a=e(18),u=e(59),s=e(102),l=e(27),c=e(134),p={},d=!1,f=0,h={topBlur:"blur",topChange:"change",topClick:"click",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topScroll:"scroll",topSelectionChange:"selectionchange",topTextInput:"textInput",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topWheel:"wheel"},m="_reactListenersID"+String(Math.random()).slice(2),v=l({},u,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(v.handleTopLevel),v.ReactEventListener=e}},setEnabled:function(e){v.ReactEventListener&&v.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!v.ReactEventListener||!v.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,i=r(n),u=a.registrationNameDependencies[e],s=o.topLevelTypes,l=0,p=u.length;p>l;l++){var d=u[l];i.hasOwnProperty(d)&&i[d]||(d===s.topWheel?c("wheel")?v.ReactEventListener.trapBubbledEvent(s.topWheel,"wheel",n):c("mousewheel")?v.ReactEventListener.trapBubbledEvent(s.topWheel,"mousewheel",n):v.ReactEventListener.trapBubbledEvent(s.topWheel,"DOMMouseScroll",n):d===s.topScroll?c("scroll",!0)?v.ReactEventListener.trapCapturedEvent(s.topScroll,"scroll",n):v.ReactEventListener.trapBubbledEvent(s.topScroll,"scroll",v.ReactEventListener.WINDOW_HANDLE):d===s.topFocus||d===s.topBlur?(c("focus",!0)?(v.ReactEventListener.trapCapturedEvent(s.topFocus,"focus",n),v.ReactEventListener.trapCapturedEvent(s.topBlur,"blur",n)):c("focusin")&&(v.ReactEventListener.trapBubbledEvent(s.topFocus,"focusin",n),v.ReactEventListener.trapBubbledEvent(s.topBlur,"focusout",n)),i[s.topBlur]=!0,i[s.topFocus]=!0):h.hasOwnProperty(d)&&v.ReactEventListener.trapBubbledEvent(d,h[d],n),i[d]=!0)}},trapBubbledEvent:function(e,t,n){
-return v.ReactEventListener.trapBubbledEvent(e,t,n)},trapCapturedEvent:function(e,t,n){return v.ReactEventListener.trapCapturedEvent(e,t,n)},ensureScrollValueMonitoring:function(){if(!d){var e=s.refreshScrollValues;v.ReactEventListener.monitorScrollValue(e),d=!0}},eventNameDispatchConfigs:i.eventNameDispatchConfigs,registrationNameModules:i.registrationNameModules,putListener:i.putListener,getListener:i.getListener,deleteListener:i.deleteListener,deleteAllListeners:i.deleteAllListeners});t.exports=v},{102:102,134:134,15:15,17:17,18:18,27:27,59:59}],31:[function(e,t,n){"use strict";var r=e(79),o=e(116),i=e(132),a=e(147),u={instantiateChildren:function(e,t,n){var r=o(e);for(var a in r)if(r.hasOwnProperty(a)){var u=r[a],s=i(u,null);r[a]=s}return r},updateChildren:function(e,t,n,u){var s=o(t);if(!s&&!e)return null;var l;for(l in s)if(s.hasOwnProperty(l)){var c=e&&e[l],p=c&&c._currentElement,d=s[l];if(a(p,d))r.receiveComponent(c,d,n,u),s[l]=c;else{c&&r.unmountComponent(c,l);var f=i(d,null);s[l]=f}}for(l in e)!e.hasOwnProperty(l)||s&&s.hasOwnProperty(l)||r.unmountComponent(e[l]);return s},unmountChildren:function(e){for(var t in e){var n=e[t];r.unmountComponent(n)}}};t.exports=u},{116:116,132:132,147:147,79:79}],32:[function(e,t,n){"use strict";function r(e,t){this.forEachFunction=e,this.forEachContext=t}function o(e,t,n,r){var o=e;o.forEachFunction.call(o.forEachContext,t,r)}function i(e,t,n){if(null==e)return e;var i=r.getPooled(t,n);f(e,o,i),r.release(i)}function a(e,t,n){this.mapResult=e,this.mapFunction=t,this.mapContext=n}function u(e,t,n,r){var o=e,i=o.mapResult,a=!i.hasOwnProperty(n);if(a){var u=o.mapFunction.call(o.mapContext,t,r);i[n]=u}}function s(e,t,n){if(null==e)return e;var r={},o=a.getPooled(r,t,n);return f(e,u,o),a.release(o),d.create(r)}function l(e,t,n,r){return null}function c(e,t){return f(e,l,null)}var p=e(28),d=e(61),f=e(149),h=(e(150),p.twoArgumentPooler),m=p.threeArgumentPooler;p.addPoolingTo(r,h),p.addPoolingTo(a,m);var v={forEach:i,map:s,count:c};t.exports=v},{149:149,150:150,28:28,61:61}],33:[function(e,t,n){"use strict";function r(e,t){var n=D.hasOwnProperty(t)?D[t]:null;N.hasOwnProperty(t)&&y(n===_.OVERRIDE_BASE),e.hasOwnProperty(t)&&y(n===_.DEFINE_MANY||n===_.DEFINE_MANY_MERGED)}function o(e,t){if(t){y("function"!=typeof t),y(!d.isValidElement(t));var n=e.prototype;t.hasOwnProperty(b)&&M.mixins(e,t.mixins);for(var o in t)if(t.hasOwnProperty(o)&&o!==b){var i=t[o];if(r(n,o),M.hasOwnProperty(o))M[o](e,i);else{var a=D.hasOwnProperty(o),l=n.hasOwnProperty(o),c=i&&i.__reactDontBind,p="function"==typeof i,f=p&&!a&&!l&&!c;if(f)n.__reactAutoBindMap||(n.__reactAutoBindMap={}),n.__reactAutoBindMap[o]=i,n[o]=i;else if(l){var h=D[o];y(a&&(h===_.DEFINE_MANY_MERGED||h===_.DEFINE_MANY)),h===_.DEFINE_MANY_MERGED?n[o]=u(n[o],i):h===_.DEFINE_MANY&&(n[o]=s(n[o],i))}else n[o]=i}}}}function i(e,t){if(t)for(var n in t){var r=t[n];if(t.hasOwnProperty(n)){var o=n in M;y(!o);var i=n in e;y(!i),e[n]=r}}}function a(e,t){y(e&&t&&"object"==typeof e&&"object"==typeof t);for(var n in t)t.hasOwnProperty(n)&&(y(void 0===e[n]),e[n]=t[n]);return e}function u(e,t){return function(){var n=e.apply(this,arguments),r=t.apply(this,arguments);if(null==n)return r;if(null==r)return n;var o={};return a(o,n),a(o,r),o}}function s(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}}function l(e,t){var n=t.bind(e);return n}function c(e){for(var t in e.__reactAutoBindMap)if(e.__reactAutoBindMap.hasOwnProperty(t)){var n=e.__reactAutoBindMap[t];e[t]=l(e,f.guard(n,e.constructor.displayName+"."+t))}}var p=e(34),d=(e(39),e(55)),f=e(58),h=e(65),m=e(66),v=(e(75),e(74),e(84)),g=e(27),y=e(133),C=e(138),E=e(139),b=(e(150),E({mixins:null})),_=C({DEFINE_ONCE:null,DEFINE_MANY:null,OVERRIDE_BASE:null,DEFINE_MANY_MERGED:null}),x=[],D={mixins:_.DEFINE_MANY,statics:_.DEFINE_MANY,propTypes:_.DEFINE_MANY,contextTypes:_.DEFINE_MANY,childContextTypes:_.DEFINE_MANY,getDefaultProps:_.DEFINE_MANY_MERGED,getInitialState:_.DEFINE_MANY_MERGED,getChildContext:_.DEFINE_MANY_MERGED,render:_.DEFINE_ONCE,componentWillMount:_.DEFINE_MANY,componentDidMount:_.DEFINE_MANY,componentWillReceiveProps:_.DEFINE_MANY,shouldComponentUpdate:_.DEFINE_ONCE,componentWillUpdate:_.DEFINE_MANY,componentDidUpdate:_.DEFINE_MANY,componentWillUnmount:_.DEFINE_MANY,updateComponent:_.OVERRIDE_BASE},M={displayName:function(e,t){e.displayName=t},mixins:function(e,t){if(t)for(var n=0;n<t.length;n++)o(e,t[n])},childContextTypes:function(e,t){e.childContextTypes=g({},e.childContextTypes,t)},contextTypes:function(e,t){e.contextTypes=g({},e.contextTypes,t)},getDefaultProps:function(e,t){e.getDefaultProps?e.getDefaultProps=u(e.getDefaultProps,t):e.getDefaultProps=t},propTypes:function(e,t){e.propTypes=g({},e.propTypes,t)},statics:function(e,t){i(e,t)}},N={replaceState:function(e,t){v.enqueueReplaceState(this,e),t&&v.enqueueCallback(this,t)},isMounted:function(){var e=h.get(this);return e&&e!==m.currentlyMountingInstance},setProps:function(e,t){v.enqueueSetProps(this,e),t&&v.enqueueCallback(this,t)},replaceProps:function(e,t){v.enqueueReplaceProps(this,e),t&&v.enqueueCallback(this,t)}},I=function(){};g(I.prototype,p.prototype,N);var T={createClass:function(e){var t=function(e,t){this.__reactAutoBindMap&&c(this),this.props=e,this.context=t,this.state=null;var n=this.getInitialState?this.getInitialState():null;y("object"==typeof n&&!Array.isArray(n)),this.state=n};t.prototype=new I,t.prototype.constructor=t,x.forEach(o.bind(null,t)),o(t,e),t.getDefaultProps&&(t.defaultProps=t.getDefaultProps()),y(t.prototype.render);for(var n in D)t.prototype[n]||(t.prototype[n]=null);return t.type=t,t},injection:{injectMixin:function(e){x.push(e)}}};t.exports=T},{133:133,138:138,139:139,150:150,27:27,34:34,39:39,55:55,58:58,65:65,66:66,74:74,75:75,84:84}],34:[function(e,t,n){"use strict";function r(e,t){this.props=e,this.context=t}{var o=e(84),i=e(133);e(150)}r.prototype.setState=function(e,t){i("object"==typeof e||"function"==typeof e||null==e),o.enqueueSetState(this,e),t&&o.enqueueCallback(this,t)},r.prototype.forceUpdate=function(e){o.enqueueForceUpdate(this),e&&o.enqueueCallback(this,e)};t.exports=r},{133:133,150:150,84:84}],35:[function(e,t,n){"use strict";var r=e(44),o=e(68),i={processChildrenUpdates:r.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkupByID:r.dangerouslyReplaceNodeWithMarkupByID,unmountIDFromEnvironment:function(e){o.purgeID(e)}};t.exports=i},{44:44,68:68}],36:[function(e,t,n){"use strict";var r=e(133),o=!1,i={unmountIDFromEnvironment:null,replaceNodeWithMarkupByID:null,processChildrenUpdates:null,injection:{injectEnvironment:function(e){r(!o),i.unmountIDFromEnvironment=e.unmountIDFromEnvironment,i.replaceNodeWithMarkupByID=e.replaceNodeWithMarkupByID,i.processChildrenUpdates=e.processChildrenUpdates,o=!0}}};t.exports=i},{133:133}],37:[function(e,t,n){"use strict";function r(e){var t=e._currentElement._owner||null;if(t){var n=t.getName();if(n)return" Check the render method of `"+n+"`."}return""}var o=e(36),i=e(38),a=e(39),u=e(55),s=(e(56),e(65)),l=e(66),c=e(71),p=e(73),d=e(75),f=(e(74),e(79)),h=e(85),m=e(27),v=e(113),g=e(133),y=e(147),C=(e(150),1),E={construct:function(e){this._currentElement=e,this._rootNodeID=null,this._instance=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._isTopLevel=!1,this._pendingCallbacks=null},mountComponent:function(e,t,n){this._context=n,this._mountOrder=C++,this._rootNodeID=e;var r=this._processProps(this._currentElement.props),o=this._processContext(this._currentElement._context),i=c.getComponentClassForElement(this._currentElement),a=new i(r,o);a.props=r,a.context=o,a.refs=v,this._instance=a,s.set(a,this);var u=a.state;void 0===u&&(a.state=u=null),g("object"==typeof u&&!Array.isArray(u)),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var p,d=l.currentlyMountingInstance;l.currentlyMountingInstance=this;try{a.componentWillMount&&(a.componentWillMount(),this._pendingStateQueue&&(a.state=this._processPendingState(a.props,a.context))),p=this._renderValidatedComponent()}finally{l.currentlyMountingInstance=d}this._renderedComponent=this._instantiateReactComponent(p,this._currentElement.type);var h=f.mountComponent(this._renderedComponent,e,t,this._processChildContext(n));return a.componentDidMount&&t.getReactMountReady().enqueue(a.componentDidMount,a),h},unmountComponent:function(){var e=this._instance;if(e.componentWillUnmount){var t=l.currentlyUnmountingInstance;l.currentlyUnmountingInstance=this;try{e.componentWillUnmount()}finally{l.currentlyUnmountingInstance=t}}f.unmountComponent(this._renderedComponent),this._renderedComponent=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=null,s.remove(e)},_setPropsInternal:function(e,t){var n=this._pendingElement||this._currentElement;this._pendingElement=u.cloneAndReplaceProps(n,m({},n.props,e)),h.enqueueUpdate(this,t)},_maskContext:function(e){var t=null;if("string"==typeof this._currentElement.type)return v;var n=this._currentElement.type.contextTypes;if(!n)return v;t={};for(var r in n)t[r]=e[r];return t},_processContext:function(e){var t=this._maskContext(e);return t},_processChildContext:function(e){var t=this._instance,n=t.getChildContext&&t.getChildContext();if(n){g("object"==typeof t.constructor.childContextTypes);for(var r in n)g(r in t.constructor.childContextTypes);return m({},e,n)}return e},_processProps:function(e){return e},_checkPropTypes:function(e,t,n){var o=this.getName();for(var i in e)if(e.hasOwnProperty(i)){var a;try{g("function"==typeof e[i]),a=e[i](t,i,o,n)}catch(u){a=u}a instanceof Error&&(r(this),n===d.prop)}},receiveComponent:function(e,t,n){var r=this._currentElement,o=this._context;this._pendingElement=null,this.updateComponent(t,r,e,o,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement&&f.receiveComponent(this,this._pendingElement||this._currentElement,e,this._context),(null!==this._pendingStateQueue||this._pendingForceUpdate)&&this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context)},_warnIfContextsDiffer:function(e,t){e=this._maskContext(e),t=this._maskContext(t);for(var n=Object.keys(t).sort(),r=(this.getName()||"ReactCompositeComponent",0);r<n.length;r++)n[r]},updateComponent:function(e,t,n,r,o){var i=this._instance,a=i.context,u=i.props;t!==n&&(a=this._processContext(n._context),u=this._processProps(n.props),i.componentWillReceiveProps&&i.componentWillReceiveProps(u,a));var s=this._processPendingState(u,a),l=this._pendingForceUpdate||!i.shouldComponentUpdate||i.shouldComponentUpdate(u,s,a);l?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,u,s,a,e,o)):(this._currentElement=n,this._context=o,i.props=u,i.state=s,i.context=a)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,o=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;for(var i=m({},o?r[0]:n.state),a=o?1:0;a<r.length;a++){var u=r[a];m(i,"function"==typeof u?u.call(n,i,e,t):u)}return i},_performComponentUpdate:function(e,t,n,r,o,i){var a=this._instance,u=a.props,s=a.state,l=a.context;a.componentWillUpdate&&a.componentWillUpdate(t,n,r),this._currentElement=e,this._context=i,a.props=t,a.state=n,a.context=r,this._updateRenderedComponent(o,i),a.componentDidUpdate&&o.getReactMountReady().enqueue(a.componentDidUpdate.bind(a,u,s,l),a)},_updateRenderedComponent:function(e,t){var n=this._renderedComponent,r=n._currentElement,o=this._renderValidatedComponent();if(y(r,o))f.receiveComponent(n,o,e,this._processChildContext(t));else{var i=this._rootNodeID,a=n._rootNodeID;f.unmountComponent(n),this._renderedComponent=this._instantiateReactComponent(o,this._currentElement.type);var u=f.mountComponent(this._renderedComponent,i,e,this._processChildContext(t));this._replaceNodeWithMarkupByID(a,u)}},_replaceNodeWithMarkupByID:function(e,t){o.replaceNodeWithMarkupByID(e,t)},_renderValidatedComponentWithoutOwnerOrContext:function(){var e=this._instance,t=e.render();return t},_renderValidatedComponent:function(){var e,t=i.current;i.current=this._processChildContext(this._currentElement._context),a.current=this;try{e=this._renderValidatedComponentWithoutOwnerOrContext()}finally{i.current=t,a.current=null}return g(null===e||e===!1||u.isValidElement(e)),e},attachRef:function(e,t){var n=this.getPublicInstance(),r=n.refs===v?n.refs={}:n.refs;r[e]=t.getPublicInstance()},detachRef:function(e){var t=this.getPublicInstance().refs;delete t[e]},getName:function(){var e=this._currentElement.type,t=this._instance&&this._instance.constructor;return e.displayName||t&&t.displayName||e.name||t&&t.name||null},getPublicInstance:function(){return this._instance},_instantiateReactComponent:null};p.measureMethods(E,"ReactCompositeComponent",{mountComponent:"mountComponent",updateComponent:"updateComponent",_renderValidatedComponent:"_renderValidatedComponent"});var b={Mixin:E};t.exports=b},{113:113,133:133,147:147,150:150,27:27,36:36,38:38,39:39,55:55,56:56,65:65,66:66,71:71,73:73,74:74,75:75,79:79,85:85}],38:[function(e,t,n){"use strict";var r=e(27),o=e(113),i=(e(150),{current:o,withContext:function(e,t){var n,o=i.current;i.current=r({},o,e);try{n=t()}finally{i.current=o}return n}});t.exports=i},{113:113,150:150,27:27}],39:[function(e,t,n){"use strict";var r={current:null};t.exports=r},{}],40:[function(e,t,n){"use strict";function r(e){return o.createFactory(e)}var o=e(55),i=(e(56),e(140)),a=i({a:"a",abbr:"abbr",address:"address",area:"area",article:"article",aside:"aside",audio:"audio",b:"b",base:"base",bdi:"bdi",bdo:"bdo",big:"big",blockquote:"blockquote",body:"body",br:"br",button:"button",canvas:"canvas",caption:"caption",cite:"cite",code:"code",col:"col",colgroup:"colgroup",data:"data",datalist:"datalist",dd:"dd",del:"del",details:"details",dfn:"dfn",dialog:"dialog",div:"div",dl:"dl",dt:"dt",em:"em",embed:"embed",fieldset:"fieldset",figcaption:"figcaption",figure:"figure",footer:"footer",form:"form",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",h6:"h6",head:"head",header:"header",hr:"hr",html:"html",i:"i",iframe:"iframe",img:"img",input:"input",ins:"ins",kbd:"kbd",keygen:"keygen",label:"label",legend:"legend",li:"li",link:"link",main:"main",map:"map",mark:"mark",menu:"menu",menuitem:"menuitem",meta:"meta",meter:"meter",nav:"nav",noscript:"noscript",object:"object",ol:"ol",optgroup:"optgroup",option:"option",output:"output",p:"p",param:"param",picture:"picture",pre:"pre",progress:"progress",q:"q",rp:"rp",rt:"rt",ruby:"ruby",s:"s",samp:"samp",script:"script",section:"section",select:"select",small:"small",source:"source",span:"span",strong:"strong",style:"style",sub:"sub",summary:"summary",sup:"sup",table:"table",tbody:"tbody",td:"td",textarea:"textarea",tfoot:"tfoot",th:"th",thead:"thead",time:"time",title:"title",tr:"tr",track:"track",u:"u",ul:"ul","var":"var",video:"video",wbr:"wbr",circle:"circle",defs:"defs",ellipse:"ellipse",g:"g",line:"line",linearGradient:"linearGradient",mask:"mask",path:"path",pattern:"pattern",polygon:"polygon",polyline:"polyline",radialGradient:"radialGradient",rect:"rect",stop:"stop",svg:"svg",text:"text",tspan:"tspan"},r);t.exports=a},{140:140,55:55,56:56}],41:[function(e,t,n){"use strict";var r=e(2),o=e(29),i=e(33),a=e(55),u=e(138),s=a.createFactory("button"),l=u({onClick:!0,onDoubleClick:!0,onMouseDown:!0,onMouseMove:!0,onMouseUp:!0,onClickCapture:!0,onDoubleClickCapture:!0,onMouseDownCapture:!0,onMouseMoveCapture:!0,onMouseUpCapture:!0}),c=i.createClass({displayName:"ReactDOMButton",tagName:"BUTTON",mixins:[r,o],render:function(){var e={};for(var t in this.props)!this.props.hasOwnProperty(t)||this.props.disabled&&l[t]||(e[t]=this.props[t]);return s(e,this.props.children)}});t.exports=c},{138:138,2:2,29:29,33:33,55:55}],42:[function(e,t,n){"use strict";function r(e){e&&(null!=e.dangerouslySetInnerHTML&&(g(null==e.children),g(null!=e.dangerouslySetInnerHTML.__html)),g(null==e.style||"object"==typeof e.style))}function o(e,t,n,r){var o=d.findReactContainerForID(e);if(o){var i=o.nodeType===D?o.ownerDocument:o;E(t,i)}r.getPutListenerQueue().enqueuePutListener(e,t,n)}function i(e){R.call(T,e)||(g(I.test(e)),T[e]=!0)}function a(e){i(e),this._tag=e,this._renderedChildren=null,this._previousStyleCopy=null,this._rootNodeID=null}var u=e(5),s=e(10),l=e(11),c=e(30),p=e(35),d=e(68),f=e(69),h=e(73),m=e(27),v=e(114),g=e(133),y=(e(134),e(139)),C=(e(150),c.deleteListener),E=c.listenTo,b=c.registrationNameModules,_={string:!0,number:!0},x=y({style:null}),D=1,M=null,N={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},I=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,T={},R={}.hasOwnProperty;a.displayName="ReactDOMComponent",a.Mixin={construct:function(e){this._currentElement=e},mountComponent:function(e,t,n){this._rootNodeID=e,r(this._currentElement.props);var o=N[this._tag]?"":"</"+this._tag+">";return this._createOpenTagMarkupAndPutListeners(t)+this._createContentMarkup(t,n)+o},_createOpenTagMarkupAndPutListeners:function(e){var t=this._currentElement.props,n="<"+this._tag;for(var r in t)if(t.hasOwnProperty(r)){var i=t[r];if(null!=i)if(b.hasOwnProperty(r))o(this._rootNodeID,r,i,e);else{r===x&&(i&&(i=this._previousStyleCopy=m({},t.style)),i=u.createMarkupForStyles(i));var a=l.createMarkupForProperty(r,i);a&&(n+=" "+a)}}if(e.renderToStaticMarkup)return n+">";var s=l.createMarkupForID(this._rootNodeID);return n+" "+s+">"},_createContentMarkup:function(e,t){var n="";("listing"===this._tag||"pre"===this._tag||"textarea"===this._tag)&&(n="\n");var r=this._currentElement.props,o=r.dangerouslySetInnerHTML;if(null!=o){if(null!=o.__html)return n+o.__html}else{var i=_[typeof r.children]?r.children:null,a=null!=i?null:r.children;if(null!=i)return n+v(i);if(null!=a){var u=this.mountChildren(a,e,t);return n+u.join("")}}return n},receiveComponent:function(e,t,n){var r=this._currentElement;this._currentElement=e,this.updateComponent(t,r,e,n)},updateComponent:function(e,t,n,o){r(this._currentElement.props),this._updateDOMProperties(t.props,e),this._updateDOMChildren(t.props,e,o)},_updateDOMProperties:function(e,t){var n,r,i,a=this._currentElement.props;for(n in e)if(!a.hasOwnProperty(n)&&e.hasOwnProperty(n))if(n===x){var u=this._previousStyleCopy;for(r in u)u.hasOwnProperty(r)&&(i=i||{},i[r]="");this._previousStyleCopy=null}else b.hasOwnProperty(n)?C(this._rootNodeID,n):(s.isStandardName[n]||s.isCustomAttribute(n))&&M.deletePropertyByID(this._rootNodeID,n);for(n in a){var l=a[n],c=n===x?this._previousStyleCopy:e[n];if(a.hasOwnProperty(n)&&l!==c)if(n===x)if(l?l=this._previousStyleCopy=m({},l):this._previousStyleCopy=null,c){for(r in c)!c.hasOwnProperty(r)||l&&l.hasOwnProperty(r)||(i=i||{},i[r]="");for(r in l)l.hasOwnProperty(r)&&c[r]!==l[r]&&(i=i||{},i[r]=l[r])}else i=l;else b.hasOwnProperty(n)?o(this._rootNodeID,n,l,t):(s.isStandardName[n]||s.isCustomAttribute(n))&&M.updatePropertyByID(this._rootNodeID,n,l)}i&&M.updateStylesByID(this._rootNodeID,i)},_updateDOMChildren:function(e,t,n){var r=this._currentElement.props,o=_[typeof e.children]?e.children:null,i=_[typeof r.children]?r.children:null,a=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,u=r.dangerouslySetInnerHTML&&r.dangerouslySetInnerHTML.__html,s=null!=o?null:e.children,l=null!=i?null:r.children,c=null!=o||null!=a,p=null!=i||null!=u;null!=s&&null==l?this.updateChildren(null,t,n):c&&!p&&this.updateTextContent(""),null!=i?o!==i&&this.updateTextContent(""+i):null!=u?a!==u&&M.updateInnerHTMLByID(this._rootNodeID,u):null!=l&&this.updateChildren(l,t,n)},unmountComponent:function(){this.unmountChildren(),c.deleteAllListeners(this._rootNodeID),p.unmountIDFromEnvironment(this._rootNodeID),this._rootNodeID=null}},h.measureMethods(a,"ReactDOMComponent",{mountComponent:"mountComponent",updateComponent:"updateComponent"}),m(a.prototype,a.Mixin,f.Mixin),a.injection={injectIDOperations:function(e){a.BackendIDOperations=M=e}},t.exports=a},{10:10,11:11,114:114,133:133,134:134,139:139,150:150,27:27,30:30,35:35,5:5,68:68,69:69,73:73}],43:[function(e,t,n){"use strict";var r=e(15),o=e(25),i=e(29),a=e(33),u=e(55),s=u.createFactory("form"),l=a.createClass({displayName:"ReactDOMForm",tagName:"FORM",mixins:[i,o],render:function(){return s(this.props)},componentDidMount:function(){this.trapBubbledEvent(r.topLevelTypes.topReset,"reset"),this.trapBubbledEvent(r.topLevelTypes.topSubmit,"submit")}});t.exports=l},{15:15,25:25,29:29,33:33,55:55}],44:[function(e,t,n){"use strict";var r=e(5),o=e(9),i=e(11),a=e(68),u=e(73),s=e(133),l=e(144),c={dangerouslySetInnerHTML:"`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.",style:"`style` must be set using `updateStylesByID()`."},p={updatePropertyByID:function(e,t,n){var r=a.getNode(e);s(!c.hasOwnProperty(t)),null!=n?i.setValueForProperty(r,t,n):i.deleteValueForProperty(r,t)},deletePropertyByID:function(e,t,n){var r=a.getNode(e);s(!c.hasOwnProperty(t)),i.deleteValueForProperty(r,t,n)},updateStylesByID:function(e,t){var n=a.getNode(e);r.setValueForStyles(n,t)},updateInnerHTMLByID:function(e,t){var n=a.getNode(e);l(n,t)},updateTextContentByID:function(e,t){var n=a.getNode(e);o.updateTextContent(n,t)},dangerouslyReplaceNodeWithMarkupByID:function(e,t){var n=a.getNode(e);o.dangerouslyReplaceNodeWithMarkup(n,t)},dangerouslyProcessChildrenUpdates:function(e,t){for(var n=0;n<e.length;n++)e[n].parentNode=a.getNode(e[n].parentID);o.processUpdates(e,t)}};u.measureMethods(p,"ReactDOMIDOperations",{updatePropertyByID:"updatePropertyByID",deletePropertyByID:"deletePropertyByID",updateStylesByID:"updateStylesByID",updateInnerHTMLByID:"updateInnerHTMLByID",updateTextContentByID:"updateTextContentByID",dangerouslyReplaceNodeWithMarkupByID:"dangerouslyReplaceNodeWithMarkupByID",dangerouslyProcessChildrenUpdates:"dangerouslyProcessChildrenUpdates"}),t.exports=p},{11:11,133:133,144:144,5:5,68:68,73:73,9:9}],45:[function(e,t,n){"use strict";var r=e(15),o=e(25),i=e(29),a=e(33),u=e(55),s=u.createFactory("iframe"),l=a.createClass({displayName:"ReactDOMIframe",tagName:"IFRAME",mixins:[i,o],render:function(){return s(this.props)},componentDidMount:function(){this.trapBubbledEvent(r.topLevelTypes.topLoad,"load")}});t.exports=l},{15:15,25:25,29:29,33:33,55:55}],46:[function(e,t,n){"use strict";var r=e(15),o=e(25),i=e(29),a=e(33),u=e(55),s=u.createFactory("img"),l=a.createClass({displayName:"ReactDOMImg",tagName:"IMG",mixins:[i,o],render:function(){return s(this.props)},componentDidMount:function(){this.trapBubbledEvent(r.topLevelTypes.topLoad,"load"),this.trapBubbledEvent(r.topLevelTypes.topError,"error")}});t.exports=l},{15:15,25:25,29:29,33:33,55:55}],47:[function(e,t,n){"use strict";function r(){this.isMounted()&&this.forceUpdate()}var o=e(2),i=e(11),a=e(24),u=e(29),s=e(33),l=e(55),c=e(68),p=e(85),d=e(27),f=e(133),h=l.createFactory("input"),m={},v=s.createClass({displayName:"ReactDOMInput",tagName:"INPUT",mixins:[o,a.Mixin,u],getInitialState:function(){var e=this.props.defaultValue;return{initialChecked:this.props.defaultChecked||!1,initialValue:null!=e?e:null}},render:function(){var e=d({},this.props);e.defaultChecked=null,e.defaultValue=null;var t=a.getValue(this);e.value=null!=t?t:this.state.initialValue;var n=a.getChecked(this);return e.checked=null!=n?n:this.state.initialChecked,e.onChange=this._handleChange,h(e,this.props.children)},componentDidMount:function(){var e=c.getID(this.getDOMNode());m[e]=this},componentWillUnmount:function(){var e=this.getDOMNode(),t=c.getID(e);delete m[t]},componentDidUpdate:function(e,t,n){var r=this.getDOMNode();null!=this.props.checked&&i.setValueForProperty(r,"checked",this.props.checked||!1);var o=a.getValue(this);null!=o&&i.setValueForProperty(r,"value",""+o)},_handleChange:function(e){var t,n=a.getOnChange(this);n&&(t=n.call(this,e)),p.asap(r,this);var o=this.props.name;if("radio"===this.props.type&&null!=o){for(var i=this.getDOMNode(),u=i;u.parentNode;)u=u.parentNode;for(var s=u.querySelectorAll("input[name="+JSON.stringify(""+o)+'][type="radio"]'),l=0,d=s.length;d>l;l++){var h=s[l];if(h!==i&&h.form===i.form){var v=c.getID(h);f(v);var g=m[v];f(g),p.asap(r,g)}}}return t}});t.exports=v},{11:11,133:133,2:2,24:24,27:27,29:29,33:33,55:55,68:68,85:85}],48:[function(e,t,n){"use strict";var r=e(29),o=e(33),i=e(55),a=(e(150),i.createFactory("option")),u=o.createClass({displayName:"ReactDOMOption",tagName:"OPTION",mixins:[r],componentWillMount:function(){},render:function(){return a(this.props,this.props.children)}});t.exports=u},{150:150,29:29,33:33,55:55}],49:[function(e,t,n){"use strict";function r(){if(this._pendingUpdate){this._pendingUpdate=!1;var e=u.getValue(this);null!=e&&this.isMounted()&&i(this,e)}}function o(e,t,n){if(null==e[t])return null;if(e.multiple){if(!Array.isArray(e[t]))return new Error("The `"+t+"` prop supplied to <select> must be an array if `multiple` is true.")}else if(Array.isArray(e[t]))return new Error("The `"+t+"` prop supplied to <select> must be a scalar value if `multiple` is false.")}function i(e,t){var n,r,o,i=e.getDOMNode().options;if(e.props.multiple){for(n={},r=0,o=t.length;o>r;r++)n[""+t[r]]=!0;for(r=0,o=i.length;o>r;r++){var a=n.hasOwnProperty(i[r].value);i[r].selected!==a&&(i[r].selected=a)}}else{for(n=""+t,r=0,o=i.length;o>r;r++)if(i[r].value===n)return void(i[r].selected=!0);i.length&&(i[0].selected=!0)}}var a=e(2),u=e(24),s=e(29),l=e(33),c=e(55),p=e(85),d=e(27),f=c.createFactory("select"),h=l.createClass({displayName:"ReactDOMSelect",tagName:"SELECT",mixins:[a,u.Mixin,s],propTypes:{defaultValue:o,value:o},render:function(){var e=d({},this.props);return e.onChange=this._handleChange,e.value=null,f(e,this.props.children)},componentWillMount:function(){this._pendingUpdate=!1},componentDidMount:function(){var e=u.getValue(this);null!=e?i(this,e):null!=this.props.defaultValue&&i(this,this.props.defaultValue)},componentDidUpdate:function(e){var t=u.getValue(this);null!=t?(this._pendingUpdate=!1,i(this,t)):!e.multiple!=!this.props.multiple&&(null!=this.props.defaultValue?i(this,this.props.defaultValue):i(this,this.props.multiple?[]:""))},_handleChange:function(e){var t,n=u.getOnChange(this);return n&&(t=n.call(this,e)),this._pendingUpdate=!0,p.asap(r,this),t}});t.exports=h},{2:2,24:24,27:27,29:29,33:33,55:55,85:85}],50:[function(e,t,n){"use strict";function r(e,t,n,r){return e===n&&t===r}function o(e){var t=document.selection,n=t.createRange(),r=n.text.length,o=n.duplicate();o.moveToElementText(e),o.setEndPoint("EndToStart",n);var i=o.text.length,a=i+r;return{start:i,end:a}}function i(e){var t=window.getSelection&&window.getSelection();if(!t||0===t.rangeCount)return null;var n=t.anchorNode,o=t.anchorOffset,i=t.focusNode,a=t.focusOffset,u=t.getRangeAt(0),s=r(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset),l=s?0:u.toString().length,c=u.cloneRange();c.selectNodeContents(e),c.setEnd(u.startContainer,u.startOffset);var p=r(c.startContainer,c.startOffset,c.endContainer,c.endOffset),d=p?0:c.toString().length,f=d+l,h=document.createRange();h.setStart(n,o),h.setEnd(i,a);var m=h.collapsed;return{start:m?f:d,end:m?d:f}}function a(e,t){var n,r,o=document.selection.createRange().duplicate();"undefined"==typeof t.end?(n=t.start,r=n):t.start>t.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart("character",n),o.setEndPoint("EndToStart",o),o.moveEnd("character",r-n),o.select()}function u(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),i="undefined"==typeof t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var u=l(e,o),s=l(e,i);if(u&&s){var p=document.createRange();p.setStart(u.node,u.offset),n.removeAllRanges(),o>i?(n.addRange(p),n.extend(s.node,s.offset)):(p.setEnd(s.node,s.offset),n.addRange(p))}}}var s=e(21),l=e(126),c=e(128),p=s.canUseDOM&&"selection"in document&&!("getSelection"in window),d={getOffsets:p?o:i,setOffsets:p?a:u};t.exports=d},{126:126,128:128,21:21}],51:[function(e,t,n){"use strict";var r=e(11),o=e(35),i=e(42),a=e(27),u=e(114),s=function(e){};a(s.prototype,{construct:function(e){this._currentElement=e,this._stringText=""+e,this._rootNodeID=null,this._mountIndex=0},mountComponent:function(e,t,n){this._rootNodeID=e;var o=u(this._stringText);return t.renderToStaticMarkup?o:"<span "+r.createMarkupForID(e)+">"+o+"</span>"},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=""+e;n!==this._stringText&&(this._stringText=n,i.BackendIDOperations.updateTextContentByID(this._rootNodeID,n))}},unmountComponent:function(){o.unmountIDFromEnvironment(this._rootNodeID)}}),t.exports=s},{11:11,114:114,27:27,35:35,42:42}],52:[function(e,t,n){"use strict";function r(){this.isMounted()&&this.forceUpdate()}var o=e(2),i=e(11),a=e(24),u=e(29),s=e(33),l=e(55),c=e(85),p=e(27),d=e(133),f=(e(150),l.createFactory("textarea")),h=s.createClass({displayName:"ReactDOMTextarea",tagName:"TEXTAREA",mixins:[o,a.Mixin,u],getInitialState:function(){var e=this.props.defaultValue,t=this.props.children;null!=t&&(d(null==e),Array.isArray(t)&&(d(t.length<=1),t=t[0]),e=""+t),null==e&&(e="");var n=a.getValue(this);return{initialValue:""+(null!=n?n:e)}},render:function(){var e=p({},this.props);return d(null==e.dangerouslySetInnerHTML),e.defaultValue=null,e.value=null,e.onChange=this._handleChange,f(e,this.state.initialValue)},componentDidUpdate:function(e,t,n){var r=a.getValue(this);if(null!=r){var o=this.getDOMNode();i.setValueForProperty(o,"value",""+r)}},_handleChange:function(e){var t,n=a.getOnChange(this);return n&&(t=n.call(this,e)),c.asap(r,this),t}});t.exports=h},{11:11,133:133,150:150,2:2,24:24,27:27,29:29,33:33,55:55,85:85}],53:[function(e,t,n){"use strict";function r(){this.reinitializeTransaction()}var o=e(85),i=e(101),a=e(27),u=e(112),s={initialize:u,close:function(){d.isBatchingUpdates=!1}},l={initialize:u,close:o.flushBatchedUpdates.bind(o)},c=[l,s];a(r.prototype,i.Mixin,{getTransactionWrappers:function(){return c}});var p=new r,d={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,o){var i=d.isBatchingUpdates;d.isBatchingUpdates=!0,i?e(t,n,r,o):p.perform(e,null,t,n,r,o)}};t.exports=d},{101:101,112:112,27:27,85:85}],54:[function(e,t,n){"use strict";function r(e){return h.createClass({tagName:e.toUpperCase(),render:function(){return new T(e,null,null,null,null,this.props)}})}function o(){P.EventEmitter.injectReactEventListener(R),P.EventPluginHub.injectEventPluginOrder(s),P.EventPluginHub.injectInstanceHandle(w),P.EventPluginHub.injectMount(O),P.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:L,EnterLeaveEventPlugin:l,ChangeEventPlugin:a,MobileSafariClickEventPlugin:d,SelectEventPlugin:A,BeforeInputEventPlugin:i}),P.NativeComponent.injectGenericComponentClass(g),P.NativeComponent.injectTextComponentClass(I),P.NativeComponent.injectAutoWrapper(r),P.Class.injectMixin(f),P.NativeComponent.injectComponentClasses({button:y,form:C,iframe:_,img:E,input:x,option:D,select:M,textarea:N,html:F("html"),head:F("head"),body:F("body")}),P.DOMProperty.injectDOMPropertyConfig(p),P.DOMProperty.injectDOMPropertyConfig(U),P.EmptyComponent.injectEmptyComponent("noscript"),P.Updates.injectReconcileTransaction(S),P.Updates.injectBatchingStrategy(v),P.RootIndex.injectCreateReactRootIndex(c.canUseDOM?u.createReactRootIndex:k.createReactRootIndex),P.Component.injectEnvironment(m),P.DOMComponent.injectIDOperations(b)}var i=e(3),a=e(7),u=e(8),s=e(13),l=e(14),c=e(21),p=e(23),d=e(26),f=e(29),h=e(33),m=e(35),v=e(53),g=e(42),y=e(41),C=e(43),E=e(46),b=e(44),_=e(45),x=e(47),D=e(48),M=e(49),N=e(52),I=e(51),T=e(55),R=e(60),P=e(62),w=e(64),O=e(68),S=e(78),A=e(87),k=e(88),L=e(89),U=e(86),F=e(109);t.exports={inject:o}},{109:109,13:13,14:14,21:21,23:23,26:26,29:29,3:3,33:33,35:35,41:41,42:42,43:43,44:44,45:45,46:46,47:47,48:48,49:49,51:51,52:52,53:53,55:55,60:60,62:62,64:64,68:68,7:7,78:78,8:8,86:86,87:87,88:88,
-89:89}],55:[function(e,t,n){"use strict";var r=e(38),o=e(39),i=e(27),a=(e(150),{key:!0,ref:!0}),u=function(e,t,n,r,o,i){this.type=e,this.key=t,this.ref=n,this._owner=r,this._context=o,this.props=i};u.prototype={_isReactElement:!0},u.createElement=function(e,t,n){var i,s={},l=null,c=null;if(null!=t){c=void 0===t.ref?null:t.ref,l=void 0===t.key?null:""+t.key;for(i in t)t.hasOwnProperty(i)&&!a.hasOwnProperty(i)&&(s[i]=t[i])}var p=arguments.length-2;if(1===p)s.children=n;else if(p>1){for(var d=Array(p),f=0;p>f;f++)d[f]=arguments[f+2];s.children=d}if(e&&e.defaultProps){var h=e.defaultProps;for(i in h)"undefined"==typeof s[i]&&(s[i]=h[i])}return new u(e,l,c,o.current,r.current,s)},u.createFactory=function(e){var t=u.createElement.bind(null,e);return t.type=e,t},u.cloneAndReplaceProps=function(e,t){var n=new u(e.type,e.key,e.ref,e._owner,e._context,t);return n},u.cloneElement=function(e,t,n){var r,s=i({},e.props),l=e.key,c=e.ref,p=e._owner;if(null!=t){void 0!==t.ref&&(c=t.ref,p=o.current),void 0!==t.key&&(l=""+t.key);for(r in t)t.hasOwnProperty(r)&&!a.hasOwnProperty(r)&&(s[r]=t[r])}var d=arguments.length-2;if(1===d)s.children=n;else if(d>1){for(var f=Array(d),h=0;d>h;h++)f[h]=arguments[h+2];s.children=f}return new u(e.type,l,c,p,e._context,s)},u.isValidElement=function(e){var t=!(!e||!e._isReactElement);return t},t.exports=u},{150:150,27:27,38:38,39:39}],56:[function(e,t,n){"use strict";function r(){if(y.current){var e=y.current.getName();if(e)return" Check the render method of `"+e+"`."}return""}function o(e){var t=e&&e.getPublicInstance();if(!t)return void 0;var n=t.constructor;return n?n.displayName||n.name||void 0:void 0}function i(){var e=y.current;return e&&o(e)||void 0}function a(e,t){e._store.validated||null!=e.key||(e._store.validated=!0,s('Each child in an array or iterator should have a unique "key" prop.',e,t))}function u(e,t,n){D.test(e)&&s("Child objects should have non-numeric keys so ordering is preserved.",t,n)}function s(e,t,n){var r=i(),a="string"==typeof n?n:n.displayName||n.name,u=r||a,s=_[e]||(_[e]={});if(!s.hasOwnProperty(u)){s[u]=!0;var l="";if(t&&t._owner&&t._owner!==y.current){var c=o(t._owner);l=" It was passed a child from "+c+"."}}}function l(e,t){if(Array.isArray(e))for(var n=0;n<e.length;n++){var r=e[n];m.isValidElement(r)&&a(r,t)}else if(m.isValidElement(e))e._store.validated=!0;else if(e){var o=E(e);if(o){if(o!==e.entries)for(var i,s=o.call(e);!(i=s.next()).done;)m.isValidElement(i.value)&&a(i.value,t)}else if("object"==typeof e){var l=v.extractIfFragment(e);for(var c in l)l.hasOwnProperty(c)&&u(c,l[c],t)}}}function c(e,t,n,o){for(var i in t)if(t.hasOwnProperty(i)){var a;try{b("function"==typeof t[i]),a=t[i](n,i,e,o)}catch(u){a=u}a instanceof Error&&!(a.message in x)&&(x[a.message]=!0,r(this))}}function p(e,t){var n=t.type,r="string"==typeof n?n:n.displayName,o=t._owner?t._owner.getPublicInstance().constructor.displayName:null,i=e+"|"+r+"|"+o;if(!M.hasOwnProperty(i)){M[i]=!0;var a="";r&&(a=" <"+r+" />");var u="";o&&(u=" The element was created by "+o+".")}}function d(e,t){return e!==e?t!==t:0===e&&0===t?1/e===1/t:e===t}function f(e){if(e._store){var t=e._store.originalProps,n=e.props;for(var r in n)n.hasOwnProperty(r)&&(t.hasOwnProperty(r)&&d(t[r],n[r])||(p(r,e),t[r]=n[r]))}}function h(e){if(null!=e.type){var t=C.getComponentClassForElement(e),n=t.displayName||t.name;t.propTypes&&c(n,t.propTypes,e.props,g.prop),"function"==typeof t.getDefaultProps}}var m=e(55),v=e(61),g=e(75),y=(e(74),e(39)),C=e(71),E=e(124),b=e(133),_=(e(150),{}),x={},D=/^\d+$/,M={},N={checkAndWarnForMutatedProps:f,createElement:function(e,t,n){var r=m.createElement.apply(this,arguments);if(null==r)return r;for(var o=2;o<arguments.length;o++)l(arguments[o],e);return h(r),r},createFactory:function(e){var t=N.createElement.bind(null,e);return t.type=e,t},cloneElement:function(e,t,n){for(var r=m.cloneElement.apply(this,arguments),o=2;o<arguments.length;o++)l(arguments[o],r.type);return h(r),r}};t.exports=N},{124:124,133:133,150:150,39:39,55:55,61:61,71:71,74:74,75:75}],57:[function(e,t,n){"use strict";function r(e){c[e]=!0}function o(e){delete c[e]}function i(e){return!!c[e]}var a,u=e(55),s=e(65),l=e(133),c={},p={injectEmptyComponent:function(e){a=u.createFactory(e)}},d=function(){};d.prototype.componentDidMount=function(){var e=s.get(this);e&&r(e._rootNodeID)},d.prototype.componentWillUnmount=function(){var e=s.get(this);e&&o(e._rootNodeID)},d.prototype.render=function(){return l(a),a()};var f=u.createElement(d),h={emptyElement:f,injection:p,isNullComponentID:i};t.exports=h},{133:133,55:55,65:65}],58:[function(e,t,n){"use strict";var r={guard:function(e,t){return e}};t.exports=r},{}],59:[function(e,t,n){"use strict";function r(e){o.enqueueEvents(e),o.processEventQueue()}var o=e(17),i={handleTopLevel:function(e,t,n,i){var a=o.extractEvents(e,t,n,i);r(a)}};t.exports=i},{17:17}],60:[function(e,t,n){"use strict";function r(e){var t=p.getID(e),n=c.getReactRootIDFromNodeID(t),r=p.findReactContainerForID(n),o=p.getFirstReactDOM(r);return o}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){for(var t=p.getFirstReactDOM(h(e.nativeEvent))||window,n=t;n;)e.ancestors.push(n),n=r(n);for(var o=0,i=e.ancestors.length;i>o;o++){t=e.ancestors[o];var a=p.getID(t)||"";v._handleTopLevel(e.topLevelType,t,a,e.nativeEvent)}}function a(e){var t=m(window);e(t)}var u=e(16),s=e(21),l=e(28),c=e(64),p=e(68),d=e(85),f=e(27),h=e(123),m=e(129);f(o.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),l.addPoolingTo(o,l.twoArgumentPooler);var v={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:s.canUseDOM?window:null,setHandleTopLevel:function(e){v._handleTopLevel=e},setEnabled:function(e){v._enabled=!!e},isEnabled:function(){return v._enabled},trapBubbledEvent:function(e,t,n){var r=n;return r?u.listen(r,t,v.dispatchEvent.bind(null,e)):null},trapCapturedEvent:function(e,t,n){var r=n;return r?u.capture(r,t,v.dispatchEvent.bind(null,e)):null},monitorScrollValue:function(e){var t=a.bind(null,e);u.listen(window,"scroll",t)},dispatchEvent:function(e,t){if(v._enabled){var n=o.getPooled(e,t);try{d.batchedUpdates(i,n)}finally{o.release(n)}}}};t.exports=v},{123:123,129:129,16:16,21:21,27:27,28:28,64:64,68:68,85:85}],61:[function(e,t,n){"use strict";var r=(e(55),e(150),{create:function(e){return e},extract:function(e){return e},extractIfFragment:function(e){return e}});t.exports=r},{150:150,55:55}],62:[function(e,t,n){"use strict";var r=e(10),o=e(17),i=e(36),a=e(33),u=e(57),s=e(30),l=e(71),c=e(42),p=e(73),d=e(81),f=e(85),h={Component:i.injection,Class:a.injection,DOMComponent:c.injection,DOMProperty:r.injection,EmptyComponent:u.injection,EventPluginHub:o.injection,EventEmitter:s.injection,NativeComponent:l.injection,Perf:p.injection,RootIndex:d.injection,Updates:f.injection};t.exports=h},{10:10,17:17,30:30,33:33,36:36,42:42,57:57,71:71,73:73,81:81,85:85}],63:[function(e,t,n){"use strict";function r(e){return i(document.documentElement,e)}var o=e(50),i=e(107),a=e(117),u=e(119),s={hasSelectionCapabilities:function(e){return e&&("INPUT"===e.nodeName&&"text"===e.type||"TEXTAREA"===e.nodeName||"true"===e.contentEditable)},getSelectionInformation:function(){var e=u();return{focusedElem:e,selectionRange:s.hasSelectionCapabilities(e)?s.getSelection(e):null}},restoreSelection:function(e){var t=u(),n=e.focusedElem,o=e.selectionRange;t!==n&&r(n)&&(s.hasSelectionCapabilities(n)&&s.setSelection(n,o),a(n))},getSelection:function(e){var t;if("selectionStart"in e)t={start:e.selectionStart,end:e.selectionEnd};else if(document.selection&&"INPUT"===e.nodeName){var n=document.selection.createRange();n.parentElement()===e&&(t={start:-n.moveStart("character",-e.value.length),end:-n.moveEnd("character",-e.value.length)})}else t=o.getOffsets(e);return t||{start:0,end:0}},setSelection:function(e,t){var n=t.start,r=t.end;if("undefined"==typeof r&&(r=n),"selectionStart"in e)e.selectionStart=n,e.selectionEnd=Math.min(r,e.value.length);else if(document.selection&&"INPUT"===e.nodeName){var i=e.createTextRange();i.collapse(!0),i.moveStart("character",n),i.moveEnd("character",r-n),i.select()}else o.setOffsets(e,t)}};t.exports=s},{107:107,117:117,119:119,50:50}],64:[function(e,t,n){"use strict";function r(e){return f+e.toString(36)}function o(e,t){return e.charAt(t)===f||t===e.length}function i(e){return""===e||e.charAt(0)===f&&e.charAt(e.length-1)!==f}function a(e,t){return 0===t.indexOf(e)&&o(t,e.length)}function u(e){return e?e.substr(0,e.lastIndexOf(f)):""}function s(e,t){if(d(i(e)&&i(t)),d(a(e,t)),e===t)return e;var n,r=e.length+h;for(n=r;n<t.length&&!o(t,n);n++);return t.substr(0,n)}function l(e,t){var n=Math.min(e.length,t.length);if(0===n)return"";for(var r=0,a=0;n>=a;a++)if(o(e,a)&&o(t,a))r=a;else if(e.charAt(a)!==t.charAt(a))break;var u=e.substr(0,r);return d(i(u)),u}function c(e,t,n,r,o,i){e=e||"",t=t||"",d(e!==t);var l=a(t,e);d(l||a(e,t));for(var c=0,p=l?u:s,f=e;;f=p(f,t)){var h;if(o&&f===e||i&&f===t||(h=n(f,l,r)),h===!1||f===t)break;d(c++<m)}}var p=e(81),d=e(133),f=".",h=f.length,m=100,v={createReactRootID:function(){return r(p.createReactRootIndex())},createReactID:function(e,t){return e+t},getReactRootIDFromNodeID:function(e){if(e&&e.charAt(0)===f&&e.length>1){var t=e.indexOf(f,1);return t>-1?e.substr(0,t):e}return null},traverseEnterLeave:function(e,t,n,r,o){var i=l(e,t);i!==e&&c(e,i,n,r,!1,!0),i!==t&&c(i,t,n,o,!0,!1)},traverseTwoPhase:function(e,t,n){e&&(c("",e,t,n,!0,!1),c(e,"",t,n,!1,!0))},traverseAncestors:function(e,t,n){c("",e,t,n,!0,!1)},_getFirstCommonAncestorID:l,_getNextDescendantID:s,isAncestorIDOf:a,SEPARATOR:f};t.exports=v},{133:133,81:81}],65:[function(e,t,n){"use strict";var r={remove:function(e){e._reactInternalInstance=void 0},get:function(e){return e._reactInternalInstance},has:function(e){return void 0!==e._reactInternalInstance},set:function(e,t){e._reactInternalInstance=t}};t.exports=r},{}],66:[function(e,t,n){"use strict";var r={currentlyMountingInstance:null,currentlyUnmountingInstance:null};t.exports=r},{}],67:[function(e,t,n){"use strict";var r=e(104),o={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(e){var t=r(e);return e.replace(">"," "+o.CHECKSUM_ATTR_NAME+'="'+t+'">')},canReuseMarkup:function(e,t){var n=t.getAttribute(o.CHECKSUM_ATTR_NAME);n=n&&parseInt(n,10);var i=r(e);return i===n}};t.exports=o},{104:104}],68:[function(e,t,n){"use strict";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;n>r;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){var t=R(e);return t&&K.getID(t)}function i(e){var t=a(e);if(t)if(L.hasOwnProperty(t)){var n=L[t];n!==e&&(w(!c(n,t)),L[t]=e)}else L[t]=e;return t}function a(e){return e&&e.getAttribute&&e.getAttribute(k)||""}function u(e,t){var n=a(e);n!==t&&delete L[n],e.setAttribute(k,t),L[t]=e}function s(e){return L.hasOwnProperty(e)&&c(L[e],e)||(L[e]=K.findReactNodeByID(e)),L[e]}function l(e){var t=b.get(e)._rootNodeID;return C.isNullComponentID(t)?null:(L.hasOwnProperty(t)&&c(L[t],t)||(L[t]=K.findReactNodeByID(t)),L[t])}function c(e,t){if(e){w(a(e)===t);var n=K.findReactContainerForID(t);if(n&&T(n,e))return!0}return!1}function p(e){delete L[e]}function d(e){var t=L[e];return t&&c(t,e)?void(W=t):!1}function f(e){W=null,E.traverseAncestors(e,d);var t=W;return W=null,t}function h(e,t,n,r,o){var i=D.mountComponent(e,t,r,I);e._isTopLevel=!0,K._mountImageIntoNode(i,n,o)}function m(e,t,n,r){var o=N.ReactReconcileTransaction.getPooled();o.perform(h,null,e,t,n,o,r),N.ReactReconcileTransaction.release(o)}var v=e(10),g=e(30),y=(e(39),e(55)),C=(e(56),e(57)),E=e(64),b=e(65),_=e(67),x=e(73),D=e(79),M=e(84),N=e(85),I=e(113),T=e(107),R=e(127),P=e(132),w=e(133),O=e(144),S=e(147),A=(e(150),E.SEPARATOR),k=v.ID_ATTRIBUTE_NAME,L={},U=1,F=9,B={},V={},j=[],W=null,K={_instancesByReactRootID:B,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r){return K.scrollMonitor(n,function(){M.enqueueElementInternal(e,t),r&&M.enqueueCallbackInternal(e,r)}),e},_registerComponent:function(e,t){w(t&&(t.nodeType===U||t.nodeType===F)),g.ensureScrollValueMonitoring();var n=K.registerContainer(t);return B[n]=e,n},_renderNewRootComponent:function(e,t,n){var r=P(e,null),o=K._registerComponent(r,t);return N.batchedUpdates(m,r,o,t,n),r},render:function(e,t,n){w(y.isValidElement(e));var r=B[o(t)];if(r){var i=r._currentElement;if(S(i,e))return K._updateRootComponent(r,e,t,n).getPublicInstance();K.unmountComponentAtNode(t)}var a=R(t),u=a&&K.isRenderedByReact(a),s=u&&!r,l=K._renderNewRootComponent(e,t,s).getPublicInstance();return n&&n.call(l),l},constructAndRenderComponent:function(e,t,n){var r=y.createElement(e,t);return K.render(r,n)},constructAndRenderComponentByID:function(e,t,n){var r=document.getElementById(n);return w(r),K.constructAndRenderComponent(e,t,r)},registerContainer:function(e){var t=o(e);return t&&(t=E.getReactRootIDFromNodeID(t)),t||(t=E.createReactRootID()),V[t]=e,t},unmountComponentAtNode:function(e){w(e&&(e.nodeType===U||e.nodeType===F));var t=o(e),n=B[t];return n?(K.unmountComponentFromNode(n,e),delete B[t],delete V[t],!0):!1},unmountComponentFromNode:function(e,t){for(D.unmountComponent(e),t.nodeType===F&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)},findReactContainerForID:function(e){var t=E.getReactRootIDFromNodeID(e),n=V[t];return n},findReactNodeByID:function(e){var t=K.findReactContainerForID(e);return K.findComponentRoot(t,e)},isRenderedByReact:function(e){if(1!==e.nodeType)return!1;var t=K.getID(e);return t?t.charAt(0)===A:!1},getFirstReactDOM:function(e){for(var t=e;t&&t.parentNode!==t;){if(K.isRenderedByReact(t))return t;t=t.parentNode}return null},findComponentRoot:function(e,t){var n=j,r=0,o=f(t)||e;for(n[0]=o.firstChild,n.length=1;r<n.length;){for(var i,a=n[r++];a;){var u=K.getID(a);u?t===u?i=a:E.isAncestorIDOf(u,t)&&(n.length=r=0,n.push(a.firstChild)):n.push(a.firstChild),a=a.nextSibling}if(i)return n.length=0,i}n.length=0,w(!1)},_mountImageIntoNode:function(e,t,n){if(w(t&&(t.nodeType===U||t.nodeType===F)),n){var o=R(t);if(_.canReuseMarkup(e,o))return;var i=o.getAttribute(_.CHECKSUM_ATTR_NAME);o.removeAttribute(_.CHECKSUM_ATTR_NAME);var a=o.outerHTML;o.setAttribute(_.CHECKSUM_ATTR_NAME,i);var u=r(e,a);" (client) "+e.substring(u-20,u+20)+"\n (server) "+a.substring(u-20,u+20),w(t.nodeType!==F)}w(t.nodeType!==F),O(t,e)},getReactRootID:o,getID:i,setID:u,getNode:s,getNodeFromInstance:l,purgeID:p};x.measureMethods(K,"ReactMount",{_renderNewRootComponent:"_renderNewRootComponent",_mountImageIntoNode:"_mountImageIntoNode"}),t.exports=K},{10:10,107:107,113:113,127:127,132:132,133:133,144:144,147:147,150:150,30:30,39:39,55:55,56:56,57:57,64:64,65:65,67:67,73:73,79:79,84:84,85:85}],69:[function(e,t,n){"use strict";function r(e,t,n){h.push({parentID:e,parentNode:null,type:c.INSERT_MARKUP,markupIndex:m.push(t)-1,textContent:null,fromIndex:null,toIndex:n})}function o(e,t,n){h.push({parentID:e,parentNode:null,type:c.MOVE_EXISTING,markupIndex:null,textContent:null,fromIndex:t,toIndex:n})}function i(e,t){h.push({parentID:e,parentNode:null,type:c.REMOVE_NODE,markupIndex:null,textContent:null,fromIndex:t,toIndex:null})}function a(e,t){h.push({parentID:e,parentNode:null,type:c.TEXT_CONTENT,markupIndex:null,textContent:t,fromIndex:null,toIndex:null})}function u(){h.length&&(l.processChildrenUpdates(h,m),s())}function s(){h.length=0,m.length=0}var l=e(36),c=e(70),p=e(79),d=e(31),f=0,h=[],m=[],v={Mixin:{mountChildren:function(e,t,n){var r=d.instantiateChildren(e,t,n);this._renderedChildren=r;var o=[],i=0;for(var a in r)if(r.hasOwnProperty(a)){var u=r[a],s=this._rootNodeID+a,l=p.mountComponent(u,s,t,n);u._mountIndex=i,o.push(l),i++}return o},updateTextContent:function(e){f++;var t=!0;try{var n=this._renderedChildren;d.unmountChildren(n);for(var r in n)n.hasOwnProperty(r)&&this._unmountChildByName(n[r],r);this.setTextContent(e),t=!1}finally{f--,f||(t?s():u())}},updateChildren:function(e,t,n){f++;var r=!0;try{this._updateChildren(e,t,n),r=!1}finally{f--,f||(r?s():u())}},_updateChildren:function(e,t,n){var r=this._renderedChildren,o=d.updateChildren(r,e,t,n);if(this._renderedChildren=o,o||r){var i,a=0,u=0;for(i in o)if(o.hasOwnProperty(i)){var s=r&&r[i],l=o[i];s===l?(this.moveChild(s,u,a),a=Math.max(s._mountIndex,a),s._mountIndex=u):(s&&(a=Math.max(s._mountIndex,a),this._unmountChildByName(s,i)),this._mountChildByNameAtIndex(l,i,u,t,n)),u++}for(i in r)!r.hasOwnProperty(i)||o&&o.hasOwnProperty(i)||this._unmountChildByName(r[i],i)}},unmountChildren:function(){var e=this._renderedChildren;d.unmountChildren(e),this._renderedChildren=null},moveChild:function(e,t,n){e._mountIndex<n&&o(this._rootNodeID,e._mountIndex,t)},createChild:function(e,t){r(this._rootNodeID,t,e._mountIndex)},removeChild:function(e){i(this._rootNodeID,e._mountIndex)},setTextContent:function(e){a(this._rootNodeID,e)},_mountChildByNameAtIndex:function(e,t,n,r,o){var i=this._rootNodeID+t,a=p.mountComponent(e,i,r,o);e._mountIndex=n,this.createChild(e,a)},_unmountChildByName:function(e,t){this.removeChild(e),e._mountIndex=null}}};t.exports=v},{31:31,36:36,70:70,79:79}],70:[function(e,t,n){"use strict";var r=e(138),o=r({INSERT_MARKUP:null,MOVE_EXISTING:null,REMOVE_NODE:null,TEXT_CONTENT:null});t.exports=o},{138:138}],71:[function(e,t,n){"use strict";function r(e){if("function"==typeof e.type)return e.type;var t=e.type,n=p[t];return null==n&&(p[t]=n=l(t)),n}function o(e){return s(c),new c(e.type,e.props)}function i(e){return new d(e)}function a(e){return e instanceof d}var u=e(27),s=e(133),l=null,c=null,p={},d=null,f={injectGenericComponentClass:function(e){c=e},injectTextComponentClass:function(e){d=e},injectComponentClasses:function(e){u(p,e)},injectAutoWrapper:function(e){l=e}},h={getComponentClassForElement:r,createInternalComponent:o,createInstanceForText:i,isTextComponent:a,injection:f};t.exports=h},{133:133,27:27}],72:[function(e,t,n){"use strict";var r=e(133),o={isValidOwner:function(e){return!(!e||"function"!=typeof e.attachRef||"function"!=typeof e.detachRef)},addComponentAsRefTo:function(e,t,n){r(o.isValidOwner(n)),n.attachRef(t,e)},removeComponentAsRefFrom:function(e,t,n){r(o.isValidOwner(n)),n.getPublicInstance().refs[t]===e.getPublicInstance()&&n.detachRef(t)}};t.exports=o},{133:133}],73:[function(e,t,n){"use strict";function r(e,t,n){return n}var o={enableMeasure:!1,storedMeasure:r,measureMethods:function(e,t,n){},measure:function(e,t,n){return n},injection:{injectMeasure:function(e){o.storedMeasure=e}}};t.exports=o},{}],74:[function(e,t,n){"use strict";var r={};t.exports=r},{}],75:[function(e,t,n){"use strict";var r=e(138),o=r({prop:null,context:null,childContext:null});t.exports=o},{138:138}],76:[function(e,t,n){"use strict";function r(e){function t(t,n,r,o,i){if(o=o||b,null==n[r]){var a=C[i];return t?new Error("Required "+a+" `"+r+"` was not specified in "+("`"+o+"`.")):null}return e(n,r,o,i)}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}function o(e){function t(t,n,r,o){var i=t[n],a=m(i);if(a!==e){var u=C[o],s=v(i);return new Error("Invalid "+u+" `"+n+"` of type `"+s+"` "+("supplied to `"+r+"`, expected `"+e+"`."))}return null}return r(t)}function i(){return r(E.thatReturns(null))}function a(e){function t(t,n,r,o){var i=t[n];if(!Array.isArray(i)){var a=C[o],u=m(i);return new Error("Invalid "+a+" `"+n+"` of type "+("`"+u+"` supplied to `"+r+"`, expected an array."))}for(var s=0;s<i.length;s++){var l=e(i,s,r,o);if(l instanceof Error)return l}return null}return r(t)}function u(){function e(e,t,n,r){if(!g.isValidElement(e[t])){var o=C[r];return new Error("Invalid "+o+" `"+t+"` supplied to "+("`"+n+"`, expected a ReactElement."))}return null}return r(e)}function s(e){function t(t,n,r,o){if(!(t[n]instanceof e)){var i=C[o],a=e.name||b;return new Error("Invalid "+i+" `"+n+"` supplied to "+("`"+r+"`, expected instance of `"+a+"`."))}return null}return r(t)}function l(e){function t(t,n,r,o){for(var i=t[n],a=0;a<e.length;a++)if(i===e[a])return null;var u=C[o],s=JSON.stringify(e);return new Error("Invalid "+u+" `"+n+"` of value `"+i+"` "+("supplied to `"+r+"`, expected one of "+s+"."))}return r(t)}function c(e){function t(t,n,r,o){var i=t[n],a=m(i);if("object"!==a){var u=C[o];return new Error("Invalid "+u+" `"+n+"` of type "+("`"+a+"` supplied to `"+r+"`, expected an object."))}for(var s in i)if(i.hasOwnProperty(s)){var l=e(i,s,r,o);if(l instanceof Error)return l}return null}return r(t)}function p(e){function t(t,n,r,o){for(var i=0;i<e.length;i++){var a=e[i];if(null==a(t,n,r,o))return null}var u=C[o];return new Error("Invalid "+u+" `"+n+"` supplied to "+("`"+r+"`."))}return r(t)}function d(){function e(e,t,n,r){if(!h(e[t])){var o=C[r];return new Error("Invalid "+o+" `"+t+"` supplied to "+("`"+n+"`, expected a ReactNode."))}return null}return r(e)}function f(e){function t(t,n,r,o){var i=t[n],a=m(i);if("object"!==a){var u=C[o];return new Error("Invalid "+u+" `"+n+"` of type `"+a+"` "+("supplied to `"+r+"`, expected `object`."))}for(var s in e){var l=e[s];if(l){var c=l(i,s,r,o);if(c)return c}}return null}return r(t)}function h(e){switch(typeof e){case"number":case"string":case"undefined":return!0;case"boolean":return!e;case"object":if(Array.isArray(e))return e.every(h);if(null===e||g.isValidElement(e))return!0;e=y.extractIfFragment(e);for(var t in e)if(!h(e[t]))return!1;return!0;default:return!1}}function m(e){var t=typeof e;return Array.isArray(e)?"array":e instanceof RegExp?"object":t}function v(e){var t=m(e);if("object"===t){if(e instanceof Date)return"date";if(e instanceof RegExp)return"regexp"}return t}var g=e(55),y=e(61),C=e(74),E=e(112),b="<<anonymous>>",_=u(),x=d(),D={array:o("array"),bool:o("boolean"),func:o("function"),number:o("number"),object:o("object"),string:o("string"),any:i(),arrayOf:a,element:_,instanceOf:s,node:x,objectOf:c,oneOf:l,oneOfType:p,shape:f};t.exports=D},{112:112,55:55,61:61,74:74}],77:[function(e,t,n){"use strict";function r(){this.listenersToPut=[]}var o=e(28),i=e(30),a=e(27);a(r.prototype,{enqueuePutListener:function(e,t,n){this.listenersToPut.push({rootNodeID:e,propKey:t,propValue:n})},putListeners:function(){for(var e=0;e<this.listenersToPut.length;e++){var t=this.listenersToPut[e];i.putListener(t.rootNodeID,t.propKey,t.propValue)}},reset:function(){this.listenersToPut.length=0},destructor:function(){this.reset()}}),o.addPoolingTo(r),t.exports=r},{27:27,28:28,30:30}],78:[function(e,t,n){"use strict";function r(){this.reinitializeTransaction(),this.renderToStaticMarkup=!1,this.reactMountReady=o.getPooled(null),this.putListenerQueue=s.getPooled()}var o=e(6),i=e(28),a=e(30),u=e(63),s=e(77),l=e(101),c=e(27),p={initialize:u.getSelectionInformation,close:u.restoreSelection},d={initialize:function(){var e=a.isEnabled();return a.setEnabled(!1),e},close:function(e){a.setEnabled(e)}},f={initialize:function(){this.reactMountReady.reset()},close:function(){this.reactMountReady.notifyAll()}},h={initialize:function(){this.putListenerQueue.reset()},close:function(){this.putListenerQueue.putListeners()}},m=[h,p,d,f],v={getTransactionWrappers:function(){return m},getReactMountReady:function(){return this.reactMountReady},getPutListenerQueue:function(){return this.putListenerQueue},destructor:function(){o.release(this.reactMountReady),this.reactMountReady=null,s.release(this.putListenerQueue),this.putListenerQueue=null}};c(r.prototype,l.Mixin,v),i.addPoolingTo(r),t.exports=r},{101:101,27:27,28:28,30:30,6:6,63:63,77:77}],79:[function(e,t,n){"use strict";function r(){o.attachRefs(this,this._currentElement)}var o=e(80),i=(e(56),{mountComponent:function(e,t,n,o){var i=e.mountComponent(t,n,o);return n.getReactMountReady().enqueue(r,e),i},unmountComponent:function(e){o.detachRefs(e,e._currentElement),e.unmountComponent()},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||null==t._owner){var u=o.shouldUpdateRefs(a,t);u&&o.detachRefs(e,a),e.receiveComponent(t,n,i),u&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t){e.performUpdateIfNecessary(t)}});t.exports=i},{56:56,80:80}],80:[function(e,t,n){"use strict";function r(e,t,n){"function"==typeof e?e(t.getPublicInstance()):i.addComponentAsRefTo(t,e,n)}function o(e,t,n){"function"==typeof e?e(null):i.removeComponentAsRefFrom(t,e,n)}var i=e(72),a={};a.attachRefs=function(e,t){var n=t.ref;null!=n&&r(n,e,t._owner)},a.shouldUpdateRefs=function(e,t){return t._owner!==e._owner||t.ref!==e.ref},a.detachRefs=function(e,t){var n=t.ref;null!=n&&o(n,e,t._owner)},t.exports=a},{72:72}],81:[function(e,t,n){"use strict";var r={injectCreateReactRootIndex:function(e){o.createReactRootIndex=e}},o={createReactRootIndex:null,injection:r};t.exports=o},{}],82:[function(e,t,n){"use strict";function r(e){p(i.isValidElement(e));var t;try{var n=a.createReactRootID();return t=s.getPooled(!1),t.perform(function(){var r=c(e,null),o=r.mountComponent(n,t,l);return u.addChecksumToMarkup(o)},null)}finally{s.release(t)}}function o(e){p(i.isValidElement(e));var t;try{var n=a.createReactRootID();return t=s.getPooled(!0),t.perform(function(){var r=c(e,null);return r.mountComponent(n,t,l)},null)}finally{s.release(t)}}var i=e(55),a=e(64),u=e(67),s=e(83),l=e(113),c=e(132),p=e(133);t.exports={renderToString:r,renderToStaticMarkup:o}},{113:113,132:132,133:133,55:55,64:64,67:67,83:83}],83:[function(e,t,n){"use strict";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=e,this.reactMountReady=i.getPooled(null),this.putListenerQueue=a.getPooled()}var o=e(28),i=e(6),a=e(77),u=e(101),s=e(27),l=e(112),c={initialize:function(){this.reactMountReady.reset()},close:l},p={initialize:function(){this.putListenerQueue.reset()},close:l},d=[p,c],f={getTransactionWrappers:function(){return d},getReactMountReady:function(){return this.reactMountReady},getPutListenerQueue:function(){return this.putListenerQueue},destructor:function(){i.release(this.reactMountReady),this.reactMountReady=null,a.release(this.putListenerQueue),this.putListenerQueue=null}};s(r.prototype,u.Mixin,f),o.addPoolingTo(r),t.exports=r},{101:101,112:112,27:27,28:28,6:6,77:77}],84:[function(e,t,n){"use strict";function r(e){e!==i.currentlyMountingInstance&&l.enqueueUpdate(e)}function o(e,t){p(null==a.current);var n=s.get(e);return n?n===i.currentlyUnmountingInstance?null:n:null}var i=e(66),a=e(39),u=e(55),s=e(65),l=e(85),c=e(27),p=e(133),d=(e(150),{enqueueCallback:function(e,t){p("function"==typeof t);var n=o(e);return n&&n!==i.currentlyMountingInstance?(n._pendingCallbacks?n._pendingCallbacks.push(t):n._pendingCallbacks=[t],void r(n)):null},enqueueCallbackInternal:function(e,t){p("function"==typeof t),e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],r(e)},enqueueForceUpdate:function(e){var t=o(e,"forceUpdate");t&&(t._pendingForceUpdate=!0,r(t))},enqueueReplaceState:function(e,t){var n=o(e,"replaceState");n&&(n._pendingStateQueue=[t],n._pendingReplaceState=!0,r(n))},enqueueSetState:function(e,t){var n=o(e,"setState");if(n){var i=n._pendingStateQueue||(n._pendingStateQueue=[]);i.push(t),r(n)}},enqueueSetProps:function(e,t){var n=o(e,"setProps");if(n){p(n._isTopLevel);var i=n._pendingElement||n._currentElement,a=c({},i.props,t);n._pendingElement=u.cloneAndReplaceProps(i,a),r(n)}},enqueueReplaceProps:function(e,t){var n=o(e,"replaceProps");if(n){p(n._isTopLevel);var i=n._pendingElement||n._currentElement;n._pendingElement=u.cloneAndReplaceProps(i,t),r(n)}},enqueueElementInternal:function(e,t){e._pendingElement=t,r(e)}});t.exports=d},{133:133,150:150,27:27,39:39,55:55,65:65,66:66,85:85}],85:[function(e,t,n){"use strict";function r(){v(N.ReactReconcileTransaction&&E)}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=c.getPooled(),this.reconcileTransaction=N.ReactReconcileTransaction.getPooled()}function i(e,t,n,o,i){r(),E.batchedUpdates(e,t,n,o,i)}function a(e,t){return e._mountOrder-t._mountOrder}function u(e){var t=e.dirtyComponentsLength;v(t===g.length),g.sort(a);for(var n=0;t>n;n++){var r=g[n],o=r._pendingCallbacks;if(r._pendingCallbacks=null,f.performUpdateIfNecessary(r,e.reconcileTransaction),o)for(var i=0;i<o.length;i++)e.callbackQueue.enqueue(o[i],r.getPublicInstance())}}function s(e){return r(),E.isBatchingUpdates?void g.push(e):void E.batchedUpdates(s,e)}function l(e,t){v(E.isBatchingUpdates),y.enqueue(e,t),C=!0}var c=e(6),p=e(28),d=(e(39),e(73)),f=e(79),h=e(101),m=e(27),v=e(133),g=(e(150),[]),y=c.getPooled(),C=!1,E=null,b={initialize:function(){this.dirtyComponentsLength=g.length},close:function(){this.dirtyComponentsLength!==g.length?(g.splice(0,this.dirtyComponentsLength),D()):g.length=0}},_={initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}},x=[b,_];m(o.prototype,h.Mixin,{getTransactionWrappers:function(){return x},destructor:function(){this.dirtyComponentsLength=null,c.release(this.callbackQueue),this.callbackQueue=null,N.ReactReconcileTransaction.release(this.reconcileTransaction),this.reconcileTransaction=null},perform:function(e,t,n){return h.Mixin.perform.call(this,this.reconcileTransaction.perform,this.reconcileTransaction,e,t,n)}}),p.addPoolingTo(o);var D=function(){for(;g.length||C;){if(g.length){var e=o.getPooled();e.perform(u,null,e),o.release(e)}if(C){C=!1;var t=y;y=c.getPooled(),t.notifyAll(),c.release(t)}}};D=d.measure("ReactUpdates","flushBatchedUpdates",D);var M={injectReconcileTransaction:function(e){v(e),N.ReactReconcileTransaction=e},injectBatchingStrategy:function(e){v(e),v("function"==typeof e.batchedUpdates),v("boolean"==typeof e.isBatchingUpdates),E=e}},N={ReactReconcileTransaction:null,batchedUpdates:i,enqueueUpdate:s,flushBatchedUpdates:D,injection:M,asap:l};t.exports=N},{101:101,133:133,150:150,27:27,28:28,39:39,6:6,73:73,79:79}],86:[function(e,t,n){"use strict";var r=e(10),o=r.injection.MUST_USE_ATTRIBUTE,i={Properties:{cx:o,cy:o,d:o,dx:o,dy:o,fill:o,fillOpacity:o,fontFamily:o,fontSize:o,fx:o,fy:o,gradientTransform:o,gradientUnits:o,markerEnd:o,markerMid:o,markerStart:o,offset:o,opacity:o,patternContentUnits:o,patternUnits:o,points:o,preserveAspectRatio:o,r:o,rx:o,ry:o,spreadMethod:o,stopColor:o,stopOpacity:o,stroke:o,strokeDasharray:o,strokeLinecap:o,strokeOpacity:o,strokeWidth:o,textAnchor:o,transform:o,version:o,viewBox:o,x1:o,x2:o,x:o,y1:o,y2:o,y:o},DOMAttributeNames:{fillOpacity:"fill-opacity",fontFamily:"font-family",fontSize:"font-size",gradientTransform:"gradientTransform",gradientUnits:"gradientUnits",markerEnd:"marker-end",markerMid:"marker-mid",markerStart:"marker-start",patternContentUnits:"patternContentUnits",patternUnits:"patternUnits",preserveAspectRatio:"preserveAspectRatio",spreadMethod:"spreadMethod",stopColor:"stop-color",stopOpacity:"stop-opacity",strokeDasharray:"stroke-dasharray",strokeLinecap:"stroke-linecap",strokeOpacity:"stroke-opacity",strokeWidth:"stroke-width",textAnchor:"text-anchor",viewBox:"viewBox"}};t.exports=i},{10:10}],87:[function(e,t,n){"use strict";function r(e){if("selectionStart"in e&&u.hasSelectionCapabilities(e))return{start:e.selectionStart,end:e.selectionEnd};if(window.getSelection){var t=window.getSelection();return{anchorNode:t.anchorNode,anchorOffset:t.anchorOffset,focusNode:t.focusNode,focusOffset:t.focusOffset}}if(document.selection){var n=document.selection.createRange();return{parentElement:n.parentElement(),text:n.text,top:n.boundingTop,left:n.boundingLeft}}}function o(e){if(y||null==m||m!==l())return null;var t=r(m);if(!g||!d(g,t)){g=t;var n=s.getPooled(h.select,v,e);return n.type="select",n.target=m,a.accumulateTwoPhaseDispatches(n),n}}var i=e(15),a=e(20),u=e(63),s=e(93),l=e(119),c=e(136),p=e(139),d=e(146),f=i.topLevelTypes,h={select:{phasedRegistrationNames:{bubbled:p({onSelect:null}),captured:p({onSelectCapture:null})},dependencies:[f.topBlur,f.topContextMenu,f.topFocus,f.topKeyDown,f.topMouseDown,f.topMouseUp,f.topSelectionChange]}},m=null,v=null,g=null,y=!1,C={eventTypes:h,extractEvents:function(e,t,n,r){switch(e){case f.topFocus:(c(t)||"true"===t.contentEditable)&&(m=t,v=n,g=null);break;case f.topBlur:m=null,v=null,g=null;break;case f.topMouseDown:
-y=!0;break;case f.topContextMenu:case f.topMouseUp:return y=!1,o(r);case f.topSelectionChange:case f.topKeyDown:case f.topKeyUp:return o(r)}}};t.exports=C},{119:119,136:136,139:139,146:146,15:15,20:20,63:63,93:93}],88:[function(e,t,n){"use strict";var r=Math.pow(2,53),o={createReactRootIndex:function(){return Math.ceil(Math.random()*r)}};t.exports=o},{}],89:[function(e,t,n){"use strict";var r=e(15),o=e(19),i=e(20),a=e(90),u=e(93),s=e(94),l=e(96),c=e(97),p=e(92),d=e(98),f=e(99),h=e(100),m=e(120),v=e(133),g=e(139),y=(e(150),r.topLevelTypes),C={blur:{phasedRegistrationNames:{bubbled:g({onBlur:!0}),captured:g({onBlurCapture:!0})}},click:{phasedRegistrationNames:{bubbled:g({onClick:!0}),captured:g({onClickCapture:!0})}},contextMenu:{phasedRegistrationNames:{bubbled:g({onContextMenu:!0}),captured:g({onContextMenuCapture:!0})}},copy:{phasedRegistrationNames:{bubbled:g({onCopy:!0}),captured:g({onCopyCapture:!0})}},cut:{phasedRegistrationNames:{bubbled:g({onCut:!0}),captured:g({onCutCapture:!0})}},doubleClick:{phasedRegistrationNames:{bubbled:g({onDoubleClick:!0}),captured:g({onDoubleClickCapture:!0})}},drag:{phasedRegistrationNames:{bubbled:g({onDrag:!0}),captured:g({onDragCapture:!0})}},dragEnd:{phasedRegistrationNames:{bubbled:g({onDragEnd:!0}),captured:g({onDragEndCapture:!0})}},dragEnter:{phasedRegistrationNames:{bubbled:g({onDragEnter:!0}),captured:g({onDragEnterCapture:!0})}},dragExit:{phasedRegistrationNames:{bubbled:g({onDragExit:!0}),captured:g({onDragExitCapture:!0})}},dragLeave:{phasedRegistrationNames:{bubbled:g({onDragLeave:!0}),captured:g({onDragLeaveCapture:!0})}},dragOver:{phasedRegistrationNames:{bubbled:g({onDragOver:!0}),captured:g({onDragOverCapture:!0})}},dragStart:{phasedRegistrationNames:{bubbled:g({onDragStart:!0}),captured:g({onDragStartCapture:!0})}},drop:{phasedRegistrationNames:{bubbled:g({onDrop:!0}),captured:g({onDropCapture:!0})}},focus:{phasedRegistrationNames:{bubbled:g({onFocus:!0}),captured:g({onFocusCapture:!0})}},input:{phasedRegistrationNames:{bubbled:g({onInput:!0}),captured:g({onInputCapture:!0})}},keyDown:{phasedRegistrationNames:{bubbled:g({onKeyDown:!0}),captured:g({onKeyDownCapture:!0})}},keyPress:{phasedRegistrationNames:{bubbled:g({onKeyPress:!0}),captured:g({onKeyPressCapture:!0})}},keyUp:{phasedRegistrationNames:{bubbled:g({onKeyUp:!0}),captured:g({onKeyUpCapture:!0})}},load:{phasedRegistrationNames:{bubbled:g({onLoad:!0}),captured:g({onLoadCapture:!0})}},error:{phasedRegistrationNames:{bubbled:g({onError:!0}),captured:g({onErrorCapture:!0})}},mouseDown:{phasedRegistrationNames:{bubbled:g({onMouseDown:!0}),captured:g({onMouseDownCapture:!0})}},mouseMove:{phasedRegistrationNames:{bubbled:g({onMouseMove:!0}),captured:g({onMouseMoveCapture:!0})}},mouseOut:{phasedRegistrationNames:{bubbled:g({onMouseOut:!0}),captured:g({onMouseOutCapture:!0})}},mouseOver:{phasedRegistrationNames:{bubbled:g({onMouseOver:!0}),captured:g({onMouseOverCapture:!0})}},mouseUp:{phasedRegistrationNames:{bubbled:g({onMouseUp:!0}),captured:g({onMouseUpCapture:!0})}},paste:{phasedRegistrationNames:{bubbled:g({onPaste:!0}),captured:g({onPasteCapture:!0})}},reset:{phasedRegistrationNames:{bubbled:g({onReset:!0}),captured:g({onResetCapture:!0})}},scroll:{phasedRegistrationNames:{bubbled:g({onScroll:!0}),captured:g({onScrollCapture:!0})}},submit:{phasedRegistrationNames:{bubbled:g({onSubmit:!0}),captured:g({onSubmitCapture:!0})}},touchCancel:{phasedRegistrationNames:{bubbled:g({onTouchCancel:!0}),captured:g({onTouchCancelCapture:!0})}},touchEnd:{phasedRegistrationNames:{bubbled:g({onTouchEnd:!0}),captured:g({onTouchEndCapture:!0})}},touchMove:{phasedRegistrationNames:{bubbled:g({onTouchMove:!0}),captured:g({onTouchMoveCapture:!0})}},touchStart:{phasedRegistrationNames:{bubbled:g({onTouchStart:!0}),captured:g({onTouchStartCapture:!0})}},wheel:{phasedRegistrationNames:{bubbled:g({onWheel:!0}),captured:g({onWheelCapture:!0})}}},E={topBlur:C.blur,topClick:C.click,topContextMenu:C.contextMenu,topCopy:C.copy,topCut:C.cut,topDoubleClick:C.doubleClick,topDrag:C.drag,topDragEnd:C.dragEnd,topDragEnter:C.dragEnter,topDragExit:C.dragExit,topDragLeave:C.dragLeave,topDragOver:C.dragOver,topDragStart:C.dragStart,topDrop:C.drop,topError:C.error,topFocus:C.focus,topInput:C.input,topKeyDown:C.keyDown,topKeyPress:C.keyPress,topKeyUp:C.keyUp,topLoad:C.load,topMouseDown:C.mouseDown,topMouseMove:C.mouseMove,topMouseOut:C.mouseOut,topMouseOver:C.mouseOver,topMouseUp:C.mouseUp,topPaste:C.paste,topReset:C.reset,topScroll:C.scroll,topSubmit:C.submit,topTouchCancel:C.touchCancel,topTouchEnd:C.touchEnd,topTouchMove:C.touchMove,topTouchStart:C.touchStart,topWheel:C.wheel};for(var b in E)E[b].dependencies=[b];var _={eventTypes:C,executeDispatch:function(e,t,n){var r=o.executeDispatch(e,t,n);r===!1&&(e.stopPropagation(),e.preventDefault())},extractEvents:function(e,t,n,r){var o=E[e];if(!o)return null;var g;switch(e){case y.topInput:case y.topLoad:case y.topError:case y.topReset:case y.topSubmit:g=u;break;case y.topKeyPress:if(0===m(r))return null;case y.topKeyDown:case y.topKeyUp:g=l;break;case y.topBlur:case y.topFocus:g=s;break;case y.topClick:if(2===r.button)return null;case y.topContextMenu:case y.topDoubleClick:case y.topMouseDown:case y.topMouseMove:case y.topMouseOut:case y.topMouseOver:case y.topMouseUp:g=c;break;case y.topDrag:case y.topDragEnd:case y.topDragEnter:case y.topDragExit:case y.topDragLeave:case y.topDragOver:case y.topDragStart:case y.topDrop:g=p;break;case y.topTouchCancel:case y.topTouchEnd:case y.topTouchMove:case y.topTouchStart:g=d;break;case y.topScroll:g=f;break;case y.topWheel:g=h;break;case y.topCopy:case y.topCut:case y.topPaste:g=a}v(g);var C=g.getPooled(o,n,r);return i.accumulateTwoPhaseDispatches(C),C}};t.exports=_},{100:100,120:120,133:133,139:139,15:15,150:150,19:19,20:20,90:90,92:92,93:93,94:94,96:96,97:97,98:98,99:99}],90:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i={clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}};o.augmentClass(r,i),t.exports=r},{93:93}],91:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i={data:null};o.augmentClass(r,i),t.exports=r},{93:93}],92:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(97),i={dataTransfer:null};o.augmentClass(r,i),t.exports=r},{97:97}],93:[function(e,t,n){"use strict";function r(e,t,n){this.dispatchConfig=e,this.dispatchMarker=t,this.nativeEvent=n;var r=this.constructor.Interface;for(var o in r)if(r.hasOwnProperty(o)){var i=r[o];i?this[o]=i(n):this[o]=n[o]}var u=null!=n.defaultPrevented?n.defaultPrevented:n.returnValue===!1;u?this.isDefaultPrevented=a.thatReturnsTrue:this.isDefaultPrevented=a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse}var o=e(28),i=e(27),a=e(112),u=e(123),s={type:null,target:u,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};i(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e.preventDefault?e.preventDefault():e.returnValue=!1,this.isDefaultPrevented=a.thatReturnsTrue},stopPropagation:function(){var e=this.nativeEvent;e.stopPropagation?e.stopPropagation():e.cancelBubble=!0,this.isPropagationStopped=a.thatReturnsTrue},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;this.dispatchConfig=null,this.dispatchMarker=null,this.nativeEvent=null}}),r.Interface=s,r.augmentClass=function(e,t){var n=this,r=Object.create(n.prototype);i(r,e.prototype),e.prototype=r,e.prototype.constructor=e,e.Interface=i({},n.Interface,t),e.augmentClass=n.augmentClass,o.addPoolingTo(e,o.threeArgumentPooler)},o.addPoolingTo(r,o.threeArgumentPooler),t.exports=r},{112:112,123:123,27:27,28:28}],94:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i={relatedTarget:null};o.augmentClass(r,i),t.exports=r},{99:99}],95:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i={data:null};o.augmentClass(r,i),t.exports=r},{93:93}],96:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i=e(120),a=e(121),u=e(122),s={key:a,location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:u,charCode:function(e){return"keypress"===e.type?i(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?i(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}};o.augmentClass(r,s),t.exports=r},{120:120,121:121,122:122,99:99}],97:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i=e(102),a=e(122),u={screenX:null,screenY:null,clientX:null,clientY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:a,button:function(e){var t=e.button;return"which"in e?t:2===t?2:4===t?1:0},buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},pageX:function(e){return"pageX"in e?e.pageX:e.clientX+i.currentScrollLeft},pageY:function(e){return"pageY"in e?e.pageY:e.clientY+i.currentScrollTop}};o.augmentClass(r,u),t.exports=r},{102:102,122:122,99:99}],98:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(99),i=e(122),a={touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:i};o.augmentClass(r,a),t.exports=r},{122:122,99:99}],99:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(93),i=e(123),a={view:function(e){if(e.view)return e.view;var t=i(e);if(null!=t&&t.window===t)return t;var n=t.ownerDocument;return n?n.defaultView||n.parentWindow:window},detail:function(e){return e.detail||0}};o.augmentClass(r,a),t.exports=r},{123:123,93:93}],100:[function(e,t,n){"use strict";function r(e,t,n){o.call(this,e,t,n)}var o=e(97),i={deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null};o.augmentClass(r,i),t.exports=r},{97:97}],101:[function(e,t,n){"use strict";var r=e(133),o={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,u,s){r(!this.isInTransaction());var l,c;try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,n,o,i,a,u,s),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(p){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n<t.length;n++){var r=t[n];try{this.wrapperInitData[n]=i.OBSERVED_ERROR,this.wrapperInitData[n]=r.initialize?r.initialize.call(this):null}finally{if(this.wrapperInitData[n]===i.OBSERVED_ERROR)try{this.initializeAll(n+1)}catch(o){}}}},closeAll:function(e){r(this.isInTransaction());for(var t=this.transactionWrappers,n=e;n<t.length;n++){var o,a=t[n],u=this.wrapperInitData[n];try{o=!0,u!==i.OBSERVED_ERROR&&a.close&&a.close.call(this,u),o=!1}finally{if(o)try{this.closeAll(n+1)}catch(s){}}}this.wrapperInitData.length=0}},i={Mixin:o,OBSERVED_ERROR:{}};t.exports=i},{133:133}],102:[function(e,t,n){"use strict";var r={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(e){r.currentScrollLeft=e.x,r.currentScrollTop=e.y}};t.exports=r},{}],103:[function(e,t,n){"use strict";function r(e,t){if(o(null!=t),null==e)return t;var n=Array.isArray(e),r=Array.isArray(t);return n&&r?(e.push.apply(e,t),e):n?(e.push(t),e):r?[e].concat(t):[e,t]}var o=e(133);t.exports=r},{133:133}],104:[function(e,t,n){"use strict";function r(e){for(var t=1,n=0,r=0;r<e.length;r++)t=(t+e.charCodeAt(r))%o,n=(n+t)%o;return t|n<<16}var o=65521;t.exports=r},{}],105:[function(e,t,n){function r(e){return e.replace(o,function(e,t){return t.toUpperCase()})}var o=/-(.)/g;t.exports=r},{}],106:[function(e,t,n){"use strict";function r(e){return o(e.replace(i,"ms-"))}var o=e(105),i=/^-ms-/;t.exports=r},{105:105}],107:[function(e,t,n){function r(e,t){return e&&t?e===t?!0:o(e)?!1:o(t)?r(e,t.parentNode):e.contains?e.contains(t):e.compareDocumentPosition?!!(16&e.compareDocumentPosition(t)):!1:!1}var o=e(137);t.exports=r},{137:137}],108:[function(e,t,n){function r(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"length"in e&&!("setInterval"in e)&&"number"!=typeof e.nodeType&&(Array.isArray(e)||"callee"in e||"item"in e)}function o(e){return r(e)?Array.isArray(e)?e.slice():i(e):[e]}var i=e(148);t.exports=o},{148:148}],109:[function(e,t,n){"use strict";function r(e){var t=i.createFactory(e),n=o.createClass({tagName:e.toUpperCase(),displayName:"ReactFullPageComponent"+e,componentWillUnmount:function(){a(!1)},render:function(){return t(this.props)}});return n}var o=e(33),i=e(55),a=e(133);t.exports=r},{133:133,33:33,55:55}],110:[function(e,t,n){function r(e){var t=e.match(c);return t&&t[1].toLowerCase()}function o(e,t){var n=l;s(!!l);var o=r(e),i=o&&u(o);if(i){n.innerHTML=i[1]+e+i[2];for(var c=i[0];c--;)n=n.lastChild}else n.innerHTML=e;var p=n.getElementsByTagName("script");p.length&&(s(t),a(p).forEach(t));for(var d=a(n.childNodes);n.lastChild;)n.removeChild(n.lastChild);return d}var i=e(21),a=e(108),u=e(125),s=e(133),l=i.canUseDOM?document.createElement("div"):null,c=/^\s*<(\w+)/;t.exports=o},{108:108,125:125,133:133,21:21}],111:[function(e,t,n){"use strict";function r(e,t){var n=null==t||"boolean"==typeof t||""===t;if(n)return"";var r=isNaN(t);return r||0===t||i.hasOwnProperty(e)&&i[e]?""+t:("string"==typeof t&&(t=t.trim()),t+"px")}var o=e(4),i=o.isUnitlessNumber;t.exports=r},{4:4}],112:[function(e,t,n){function r(e){return function(){return e}}function o(){}o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},t.exports=o},{}],113:[function(e,t,n){"use strict";var r={};t.exports=r},{}],114:[function(e,t,n){"use strict";function r(e){return i[e]}function o(e){return(""+e).replace(a,r)}var i={"&":"&amp;",">":"&gt;","<":"&lt;",'"':"&quot;","'":"&#x27;"},a=/[&><"']/g;t.exports=o},{}],115:[function(e,t,n){"use strict";function r(e){return null==e?null:u(e)?e:o.has(e)?i.getNodeFromInstance(e):(a(null==e.render||"function"!=typeof e.render),void a(!1))}{var o=(e(39),e(65)),i=e(68),a=e(133),u=e(135);e(150)}t.exports=r},{133:133,135:135,150:150,39:39,65:65,68:68}],116:[function(e,t,n){"use strict";function r(e,t,n){var r=e,o=!r.hasOwnProperty(n);o&&null!=t&&(r[n]=t)}function o(e){if(null==e)return e;var t={};return i(e,r,t),t}{var i=e(149);e(150)}t.exports=o},{149:149,150:150}],117:[function(e,t,n){"use strict";function r(e){try{e.focus()}catch(t){}}t.exports=r},{}],118:[function(e,t,n){"use strict";var r=function(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)};t.exports=r},{}],119:[function(e,t,n){function r(){try{return document.activeElement||document.body}catch(e){return document.body}}t.exports=r},{}],120:[function(e,t,n){"use strict";function r(e){var t,n=e.keyCode;return"charCode"in e?(t=e.charCode,0===t&&13===n&&(t=13)):t=n,t>=32||13===t?t:0}t.exports=r},{}],121:[function(e,t,n){"use strict";function r(e){if(e.key){var t=i[e.key]||e.key;if("Unidentified"!==t)return t}if("keypress"===e.type){var n=o(e);return 13===n?"Enter":String.fromCharCode(n)}return"keydown"===e.type||"keyup"===e.type?a[e.keyCode]||"Unidentified":""}var o=e(120),i={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},a={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};t.exports=r},{120:120}],122:[function(e,t,n){"use strict";function r(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=i[e];return r?!!n[r]:!1}function o(e){return r}var i={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};t.exports=o},{}],123:[function(e,t,n){"use strict";function r(e){var t=e.target||e.srcElement||window;return 3===t.nodeType?t.parentNode:t}t.exports=r},{}],124:[function(e,t,n){"use strict";function r(e){var t=e&&(o&&e[o]||e[i]);return"function"==typeof t?t:void 0}var o="function"==typeof Symbol&&Symbol.iterator,i="@@iterator";t.exports=r},{}],125:[function(e,t,n){function r(e){return i(!!a),d.hasOwnProperty(e)||(e="*"),u.hasOwnProperty(e)||("*"===e?a.innerHTML="<link />":a.innerHTML="<"+e+"></"+e+">",u[e]=!a.firstChild),u[e]?d[e]:null}var o=e(21),i=e(133),a=o.canUseDOM?document.createElement("div"):null,u={circle:!0,defs:!0,ellipse:!0,g:!0,line:!0,linearGradient:!0,path:!0,polygon:!0,polyline:!0,radialGradient:!0,rect:!0,stop:!0,text:!0},s=[1,'<select multiple="true">',"</select>"],l=[1,"<table>","</table>"],c=[3,"<table><tbody><tr>","</tr></tbody></table>"],p=[1,"<svg>","</svg>"],d={"*":[1,"?<div>","</div>"],area:[1,"<map>","</map>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],legend:[1,"<fieldset>","</fieldset>"],param:[1,"<object>","</object>"],tr:[2,"<table><tbody>","</tbody></table>"],optgroup:s,option:s,caption:l,colgroup:l,tbody:l,tfoot:l,thead:l,td:c,th:c,circle:p,defs:p,ellipse:p,g:p,line:p,linearGradient:p,path:p,polygon:p,polyline:p,radialGradient:p,rect:p,stop:p,text:p};t.exports=r},{133:133,21:21}],126:[function(e,t,n){"use strict";function r(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function o(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function i(e,t){for(var n=r(e),i=0,a=0;n;){if(3===n.nodeType){if(a=i+n.textContent.length,t>=i&&a>=t)return{node:n,offset:t-i};i=a}n=r(o(n))}}t.exports=i},{}],127:[function(e,t,n){"use strict";function r(e){return e?e.nodeType===o?e.documentElement:e.firstChild:null}var o=9;t.exports=r},{}],128:[function(e,t,n){"use strict";function r(){return!i&&o.canUseDOM&&(i="textContent"in document.documentElement?"textContent":"innerText"),i}var o=e(21),i=null;t.exports=r},{21:21}],129:[function(e,t,n){"use strict";function r(e){return e===window?{x:window.pageXOffset||document.documentElement.scrollLeft,y:window.pageYOffset||document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}t.exports=r},{}],130:[function(e,t,n){function r(e){return e.replace(o,"-$1").toLowerCase()}var o=/([A-Z])/g;t.exports=r},{}],131:[function(e,t,n){"use strict";function r(e){return o(e).replace(i,"-ms-")}var o=e(130),i=/^ms-/;t.exports=r},{130:130}],132:[function(e,t,n){"use strict";function r(e){return"function"==typeof e&&"undefined"!=typeof e.prototype&&"function"==typeof e.prototype.mountComponent&&"function"==typeof e.prototype.receiveComponent}function o(e,t){var n;if((null===e||e===!1)&&(e=a.emptyElement),"object"==typeof e){var o=e;n=t===o.type&&"string"==typeof o.type?u.createInternalComponent(o):r(o.type)?new o.type(o):new c}else"string"==typeof e||"number"==typeof e?n=u.createInstanceForText(e):l(!1);return n.construct(e),n._mountIndex=0,n._mountImage=null,n}var i=e(37),a=e(57),u=e(71),s=e(27),l=e(133),c=(e(150),function(){});s(c.prototype,i.Mixin,{_instantiateReactComponent:o}),t.exports=o},{133:133,150:150,27:27,37:37,57:57,71:71}],133:[function(e,t,n){"use strict";var r=function(e,t,n,r,o,i,a,u){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var l=[n,r,o,i,a,u],c=0;s=new Error("Invariant Violation: "+t.replace(/%s/g,function(){return l[c++]}))}throw s.framesToPop=1,s}};t.exports=r},{}],134:[function(e,t,n){"use strict";function r(e,t){if(!i.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,r=n in document;if(!r){var a=document.createElement("div");a.setAttribute(n,"return;"),r="function"==typeof a[n]}return!r&&o&&"wheel"===e&&(r=document.implementation.hasFeature("Events.wheel","3.0")),r}var o,i=e(21);i.canUseDOM&&(o=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("","")!==!0),t.exports=r},{21:21}],135:[function(e,t,n){function r(e){return!(!e||!("function"==typeof Node?e instanceof Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}t.exports=r},{}],136:[function(e,t,n){"use strict";function r(e){return e&&("INPUT"===e.nodeName&&o[e.type]||"TEXTAREA"===e.nodeName)}var o={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};t.exports=r},{}],137:[function(e,t,n){function r(e){return o(e)&&3==e.nodeType}var o=e(135);t.exports=r},{135:135}],138:[function(e,t,n){"use strict";var r=e(133),o=function(e){var t,n={};r(e instanceof Object&&!Array.isArray(e));for(t in e)e.hasOwnProperty(t)&&(n[t]=t);return n};t.exports=o},{133:133}],139:[function(e,t,n){var r=function(e){var t;for(t in e)if(e.hasOwnProperty(t))return t;return null};t.exports=r},{}],140:[function(e,t,n){"use strict";function r(e,t,n){if(!e)return null;var r={};for(var i in e)o.call(e,i)&&(r[i]=t.call(n,e[i],i,e));return r}var o=Object.prototype.hasOwnProperty;t.exports=r},{}],141:[function(e,t,n){"use strict";function r(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}t.exports=r},{}],142:[function(e,t,n){"use strict";function r(e){return i(o.isValidElement(e)),e}var o=e(55),i=e(133);t.exports=r},{133:133,55:55}],143:[function(e,t,n){"use strict";function r(e){return'"'+o(e)+'"'}var o=e(114);t.exports=r},{114:114}],144:[function(e,t,n){"use strict";var r=e(21),o=/^[ \r\n\t\f]/,i=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,a=function(e,t){e.innerHTML=t};if("undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction&&(a=function(e,t){MSApp.execUnsafeLocalFunction(function(){e.innerHTML=t})}),r.canUseDOM){var u=document.createElement("div");u.innerHTML=" ",""===u.innerHTML&&(a=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),o.test(t)||"<"===t[0]&&i.test(t)){e.innerHTML="\ufeff"+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t})}t.exports=a},{21:21}],145:[function(e,t,n){"use strict";var r=e(21),o=e(114),i=e(144),a=function(e,t){e.textContent=t};r.canUseDOM&&("textContent"in document.documentElement||(a=function(e,t){i(e,o(t))})),t.exports=a},{114:114,144:144,21:21}],146:[function(e,t,n){"use strict";function r(e,t){if(e===t)return!0;var n;for(n in e)if(e.hasOwnProperty(n)&&(!t.hasOwnProperty(n)||e[n]!==t[n]))return!1;for(n in t)if(t.hasOwnProperty(n)&&!e.hasOwnProperty(n))return!1;return!0}t.exports=r},{}],147:[function(e,t,n){"use strict";function r(e,t){if(null!=e&&null!=t){var n=typeof e,r=typeof t;if("string"===n||"number"===n)return"string"===r||"number"===r;if("object"===r&&e.type===t.type&&e.key===t.key){var o=e._owner===t._owner;return o}}return!1}e(150);t.exports=r},{150:150}],148:[function(e,t,n){function r(e){var t=e.length;if(o(!Array.isArray(e)&&("object"==typeof e||"function"==typeof e)),o("number"==typeof t),o(0===t||t-1 in e),e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(n){}for(var r=Array(t),i=0;t>i;i++)r[i]=e[i];return r}var o=e(133);t.exports=r},{133:133}],149:[function(e,t,n){"use strict";function r(e){return v[e]}function o(e,t){return e&&null!=e.key?a(e.key):t.toString(36)}function i(e){return(""+e).replace(g,r)}function a(e){return"$"+i(e)}function u(e,t,n,r,i){var s=typeof e;if(("undefined"===s||"boolean"===s)&&(e=null),null===e||"string"===s||"number"===s||l.isValidElement(e))return r(i,e,""===t?h+o(e,0):t,n),1;var p,v,g,y=0;if(Array.isArray(e))for(var C=0;C<e.length;C++)p=e[C],v=(""!==t?t+m:h)+o(p,C),g=n+y,y+=u(p,v,g,r,i);else{var E=d(e);if(E){var b,_=E.call(e);if(E!==e.entries)for(var x=0;!(b=_.next()).done;)p=b.value,v=(""!==t?t+m:h)+o(p,x++),g=n+y,y+=u(p,v,g,r,i);else for(;!(b=_.next()).done;){var D=b.value;D&&(p=D[1],v=(""!==t?t+m:h)+a(D[0])+m+o(p,0),g=n+y,y+=u(p,v,g,r,i))}}else if("object"===s){f(1!==e.nodeType);var M=c.extract(e);for(var N in M)M.hasOwnProperty(N)&&(p=M[N],v=(""!==t?t+m:h)+a(N)+m+o(p,0),g=n+y,y+=u(p,v,g,r,i))}}return y}function s(e,t,n){return null==e?0:u(e,"",0,t,n)}var l=e(55),c=e(61),p=e(64),d=e(124),f=e(133),h=(e(150),p.SEPARATOR),m=":",v={"=":"=0",".":"=1",":":"=2"},g=/[=.:]/g;t.exports=s},{124:124,133:133,150:150,55:55,61:61,64:64}],150:[function(e,t,n){"use strict";var r=e(112),o=r;t.exports=o},{112:112}]},{},[1])(1)}); \ No newline at end of file
diff --git a/ext/installfiles/mac/ui/simpleajax.min.js b/ext/installfiles/mac/ui/simpleajax.min.js
deleted file mode 100644
index bd15dbdd..00000000
--- a/ext/installfiles/mac/ui/simpleajax.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/** SimpleAjax v1.0.1 - MIT license - https://github.com/freelancephp/SimpleAjax */
-(function(window){var SimpleAjax=window.SimpleAjax={xhr:null,settings:{url:"",type:"GET",dataType:"text",async:true,cache:true,data:null,contentType:"application/x-www-form-urlencoded",success:null,error:null,complete:null,accepts:{text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"}},call:function(a){var b=this,c=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"),d=function(a,b){var c={};for(var d in a)c[d]=typeof b[d]=="undefined"?a[d]:b[d];return c}(this.settings,a),e=function(){if(c.readyState==4){if(c.status>=200&&c.status<300||c.status===304){var a=d.dataType=="xml"?c.responseXML:c.responseText;if(d.dataType=="json")a=b.parseJSON(a);if(b.isFunction(d.success))d.success.call(d,a,c.status,c)}else{if(b.isFunction(d.error))d.error.call(d,c,c.status)}if(b.isFunction(d.complete))d.complete.call(d,c,c.status)}};this.xhr=c;if(!d.cache)d.url+=(d.url.indexOf("?")>-1?"&":"?")+"_nocache="+(new Date).getTime();if(d.data){if(d.type=="GET"){d.url+=(d.url.indexOf("?")>-1?"&":"?")+this.param(d.data);d.data=null}else{d.data=this.param(d.data)}}c.open(d.type,d.url,d.async);c.setRequestHeader("Content-type",d.contentType);if(d.dataType&&d.accepts[d.dataType])c.setRequestHeader("Accept",d.accepts[d.dataType]);if(d.async){c.onreadystatechange=e;c.send(d.data)}else{c.send(d.data);e()}return this},get:function(a,b,c){if(this.isFunction(b)){c=b;b=null}return this.call({url:a,type:"GET",data:b,success:c})},post:function(a,b,c){if(this.isFunction(b)){c=b;b=null}return this.call({url:a,type:"POST",data:b,success:c})},load:function(a,b,c,d){if(typeof a=="string")a=document.getElementById(a);return this.call({url:b,type:c?"POST":"GET",data:c||null,complete:d||null,success:function(b){try{a.innerHTML=b}catch(c){var d=document.createElement("div");d.innerHTML=b;while(a.firstChild)a.removeChild(a.firstChild);for(var e=0,f=d.childNodes.length;e<f;e++)a.appendChild(d.childNodes[e])}}})},param:function(a){var b=[];for(var c in a){b.push(encodeURIComponent(c)+"="+encodeURIComponent(a[c]))}return b.join("&")},parseJSON:function(data){if(typeof data!=="string"||!data)return null;return eval("("+this.trim(data)+")")},trim:function(a){return a.replace(/^\s+/,"").replace(/\s+$/,"")},isFunction:function(a){return Object.prototype.toString.call(a)==="[object Function]"}};if(!window.Ajax){window.Ajax=SimpleAjax}})(window) \ No newline at end of file
diff --git a/ext/installfiles/mac/ui/zerotier.css b/ext/installfiles/mac/ui/zerotier.css
deleted file mode 100644
index 9f72024a..00000000
--- a/ext/installfiles/mac/ui/zerotier.css
+++ /dev/null
@@ -1,199 +0,0 @@
-/* Dark blue-grey: #234447
- * Light blue-grey: #91a2a3
- * Light yellow: #ffffcc
- * Orange: #ffb354 */
-
-html,body {
- font-family: "Helvetica Neue","Lucida Sans Unicode",sans-serif;
- font-size: 12pt;
- margin: 0;
- padding: 0;
- width: 100%;
- height: 100%;
- overflow: hidden;
-}
-
-.zeroTierAddress {
- font-family: monospace;
-}
-
-.zeroTierNode {
- width: 100%;
- height: 100%;
- padding: 0;
- margin: 0;
- display: table;
-}
-
-.zeroTierNode > .middle {
- width: 100%;
- height: 100%;
- padding: 0;
- margin: 0;
- overflow: hidden;
- display: table-row;
-}
-.zeroTierNode > .middle > .middleCell {
- width: 100%;
- height: 100%;
- display: table-cell;
- border-bottom: 1px solid #cfcfcf;
-}
-.zeroTierNode > .middle > .middleCell > .middleScroll {
- display: block;
- width: 100%;
- height: 100%;
- padding: 0;
- margin: 0;
- overflow: scroll;
- overflow-x: hidden;
- overflow-y: scroll;
- background: #dddddd;
-}
-.zeroTierNode > .middle > .middleCell > .middleScroll > .networks {
- display: block;
- width: 100%;
- padding: 0 0 0.25rem 0;
- margin: 0;
- border: 0;
- text-align: left;
- border-collapse: collapse;
-}
-.zeroTierNode > .middle > .middleCell > .middleScroll > .networks > .network {
- display: block;
- border-top: 0.12rem solid #dddddd;
- border-bottom: 0.12rem solid #dddddd;
- padding: 0.25rem;
- background: #ffffff;
-}
-
-.zeroTierNode > .bottom {
- font-size: 12pt;
- width: 100%;
- overflow: hidden;
- display: table-row;
- color: #000000;
- background: #dfdfdf;
-}
-.zeroTierNode > .bottom > .left {
- text-align: left;
- white-space: nowrap;
- float: left;
- padding: 0 0 0 0.5rem;
- font-size: 12pt;
- height: 100%;
-}
-.zeroTierNode > .bottom > .left > .statusLine {
- font-family: monospace;
- white-space: nowrap;
- font-size: 11pt;
- height: 100%;
-}
-.zeroTierNode > .bottom > .right {
- text-align: right;
- height: 100%;
- white-space: nowrap;
- float: right;
- font-size: 12pt;
- background: #ffffff;
-}
-.zeroTierNode > .bottom > .right form {
- height: 100%;
-}
-.zeroTierNode > .bottom > .right input {
- font-family: monospace;
- font-size: 12pt;
- background: #ffffff;
- color: #000000;
- outline: none;
- outline-style: none;
- box-shadow: 0;
- border: 0;
- margin: 0;
- padding: 0 0.25rem 0 0.25rem;
- display: inline;
- height: 100%;
-}
-.zeroTierNode > .bottom > .right button {
- display: inline-block;
- font-size: 12pt;
- background: #ffb354;
- border: 1px solid #ffb354;
- color: #000000;
- margin: 0;
- padding: 0.05rem 0.75rem 0.05rem 0.75rem;
- outline: none;
- outline-style: none;
- height: 100%;
-}
-.zeroTierNode > .bottom > .right button:hover {
- cursor: pointer;
- outline: none;
- outline-style: none;
- border: 1px solid #000000;
-}
-
-.zeroTierNetwork {
- padding: 0;
- margin: 0;
- display: inline-block;
- text-align: right;
- width: 100%;
- position: relative;
-}
-.zeroTierNetwork .networkInfo {
- padding: 0 0 0.25rem 0;
- text-align: left;
- font-size: 12pt;
-}
-.zeroTierNetwork .networkInfo .networkId {
- font-size: 11pt;
- font-family: monospace;
- color: #000000;
-}
-.zeroTierNetwork .networkInfo .networkName {
- padding: 0 0 0 1rem;
- float: right;
- font-size: 12pt;
-}
-.zeroTierNetwork .networkProps {
- width: 100%;
- display: table;
- padding: 0;
- margin: 0 auto 0 auto;
- border-top: 1px solid #999999;
- border-bottom: 1px solid #999999;
-}
-.zeroTierNetwork .networkProps > .row {
- display: table-row;
-}
-.zeroTierNetwork .networkProps > .row > .name {
- display: table-cell;
- font-size: 10pt;
- padding: 0.1rem 0.5rem 0.1rem 0.5rem;
-}
-.zeroTierNetwork .networkProps > .row > .value {
- font-size: 10pt;
- display: table-cell;
- padding: 0.1rem 0.5rem 0.1rem 0.5rem;
- background: #eeeeee;
-}
-.zeroTierNetwork .ipList {
-}
-.zeroTierNetwork .ipAddress {
- font-family: monospace;
- font-size: 10pt;
-}
-.zeroTierNetwork .leaveNetworkButton {
- padding: 0.25rem 0.5rem 0.25rem 0.5rem;
- margin: 0.25rem 0 0 0;
- font-size: 9pt;
- background: #ffffff;
- outline: none;
- background: #ffb354;
- border: 1px solid #ffb354;
- cursor: pointer;
-}
-.zeroTierNetwork .leaveNetworkButton:hover {
- border: 1px solid #000000;
-}
diff --git a/ext/installfiles/mac/ui/ztui.min.js b/ext/installfiles/mac/ui/ztui.min.js
deleted file mode 100644
index 17982839..00000000
--- a/ext/installfiles/mac/ui/ztui.min.js
+++ /dev/null
@@ -1 +0,0 @@
-var ZeroTierNetwork=React.createClass({displayName:"ZeroTierNetwork",getInitialState:function(){return{}},leaveNetwork:function(e){Ajax.call({url:"network/"+this.props.nwid+"?auth="+this.props.authToken,cache:!1,type:"DELETE",success:function(e){this.props.onNetworkDeleted&&this.props.onNetworkDeleted(this.props.nwid)}.bind(this),error:function(e){}.bind(this)}),e.preventDefault()},render:function(){return React.createElement("div",{className:"zeroTierNetwork"},React.createElement("div",{className:"networkInfo"},React.createElement("span",{className:"networkId"},this.props.nwid)," ",React.createElement("span",{className:"networkName"},this.props.name)),React.createElement("div",{className:"networkProps"},React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"Status"),React.createElement("div",{className:"value"},this.props.status)),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"Type"),React.createElement("div",{className:"value"},this.props.type)),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"MAC"),React.createElement("div",{className:"value zeroTierAddress"},this.props.mac)),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"MTU"),React.createElement("div",{className:"value"},this.props.mtu)),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"Broadcast"),React.createElement("div",{className:"value"},this.props.broadcastEnabled?"ENABLED":"DISABLED")),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"Bridging"),React.createElement("div",{className:"value"},this.props.bridge?"ACTIVE":"DISABLED")),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"Device"),React.createElement("div",{className:"value"},this.props.portDeviceName?this.props.portDeviceName:"(none)")),React.createElement("div",{className:"row"},React.createElement("div",{className:"name"},"Managed IPs"),React.createElement("div",{className:"value ipList"},this.props.assignedAddresses.map(function(e){return React.createElement("div",{key:e,className:"ipAddress"},e)})))),React.createElement("button",{type:"button",className:"leaveNetworkButton",onClick:this.leaveNetwork},"Leave Network"))}}); var ZeroTierNode=React.createClass({displayName:"ZeroTierNode",getInitialState:function(){return{address:"----------",online:!1,version:"_._._",_networks:[],_peers:[]}},ago:function(e){if(e>0){var t=Math.round((Date.now()-e)/1e3);return t>0?t:0}return 0},updatePeers:function(){Ajax.call({url:"peer?auth="+this.props.authToken,cache:!1,type:"GET",success:function(e){if(e){var t=JSON.parse(e);Array.isArray(t)&&this.setState({_peers:t})}}.bind(this),error:function(){}.bind(this)})},updateNetworks:function(){Ajax.call({url:"network?auth="+this.props.authToken,cache:!1,type:"GET",success:function(e){if(e){var t=JSON.parse(e);Array.isArray(t)&&this.setState({_networks:t})}}.bind(this),error:function(){}.bind(this)})},updateAll:function(){Ajax.call({url:"status?auth="+this.props.authToken,cache:!1,type:"GET",success:function(e){if(this.alertedToFailure=!1,e){var t=JSON.parse(e);this.setState(t),document.title="ZeroTier One ["+t.address+"]"}this.updateNetworks(),this.updatePeers()}.bind(this),error:function(){this.setState(this.getInitialState()),this.alertedToFailure||(this.alertedToFailure=!0,alert("Authorization token invalid or ZeroTier One service not running."))}.bind(this)})},joinNetwork:function(e){e.preventDefault(),this.networkToJoin&&16===this.networkToJoin.length?Ajax.call({url:"network/"+this.networkToJoin+"?auth="+this.props.authToken,cache:!1,type:"POST",success:function(e){this.networkToJoin="",this.networkInputElement&&(this.networkInputElement.value=""),this.updateNetworks()}.bind(this),error:function(){}.bind(this)}):alert("To join a network, enter its 16-digit network ID.")},handleNetworkIdEntry:function(e){this.networkInputElement=e.target;var t=this.networkInputElement.value;if(t){t=t.toLowerCase();for(var n="",a=0;a<t.length&&16>a;++a)"0123456789abcdef".indexOf(t.charAt(a))>=0&&(n+=t.charAt(a));this.networkToJoin=n,this.networkInputElement.value=n}else this.networkToJoin="",this.networkInputElement.value=""},handleNetworkDelete:function(e){for(var t=[],n=0;n<this.state._networks.length;++n)this.state._networks[n].nwid!==e&&t.push(this.state._networks[n]);this.setState({_networks:t})},componentDidMount:function(){this.updateAll(),this.updateIntervalId=setInterval(this.updateAll,2500)},componentWillUnmount:function(){clearInterval(this.updateIntervalId)},render:function(){return React.createElement("div",{className:"zeroTierNode"},React.createElement("div",{className:"middle"},React.createElement("div",{className:"middleCell"},React.createElement("div",{className:"middleScroll"},React.createElement("div",{className:"networks",key:"_networks"},this.state._networks.map(function(e){return e.authToken=this.props.authToken,e.onNetworkDeleted=this.handleNetworkDelete,React.createElement("div",{className:"network",key:e.nwid},React.createElement(ZeroTierNetwork,e))}.bind(this)))))),React.createElement("div",{className:"bottom"},React.createElement("div",{className:"left"},React.createElement("span",{className:"statusLine"},React.createElement("span",{className:"zeroTierAddress"},this.state.address),"  ",this.state.online?this.state.tcpFallbackActive?"TUNNELED":"ONLINE":"OFFLINE","  ",this.state.version)),React.createElement("div",{className:"right"},React.createElement("form",{onSubmit:this.joinNetwork},React.createElement("input",{type:"text",maxlength:"16",placeholder:"[ Network ID ]",onChange:this.handleNetworkIdEntry,size:"16"}),React.createElement("button",{type:"button",onClick:this.joinNetwork},"Join")))))}});
diff --git a/ext/installfiles/mac/uninstall.sh b/ext/installfiles/mac/uninstall.sh
index d1effb93..9bf5d6fc 100755
--- a/ext/installfiles/mac/uninstall.sh
+++ b/ext/installfiles/mac/uninstall.sh
@@ -28,13 +28,11 @@ echo "Removing ZeroTier One files..."
rm -rf '/Applications/ZeroTier One.app'
rm -f '/usr/bin/zerotier-one' '/usr/bin/zerotier-idtool' '/usr/bin/zerotier-cli' '/Library/LaunchDaemons/com.zerotier.one.plist'
-mkdir -p /tmp/ZeroTierOne_uninstall_tmp
-cp "/Library/Application Support/ZeroTier/One/*.secret" /tmp/ZeroTierOne_uninstall_tmp
-rm -rf '/Library/Application Support/ZeroTier/One'
-mkdir -p '/Library/Application Support/ZeroTier/One'
-cp "/tmp/ZeroTierOne_uninstall_tmp/*.secret" '/Library/Application Support/ZeroTier/One'
-chmod 0600 "/Library/Application Support/ZeroTier/One/*.secret"
-rm -rf /tmp/ZeroTierOne_uninstall_tmp
+
+cd '/Library/Application Support/ZeroTier/One'
+if [ "`pwd`" = '/Library/Application Support/ZeroTier/One' ]; then
+ rm -rf *.d *.sh *.log *.old *.kext *.conf *.pkg *.dmg *.pid *.port *.save *.bin planet zerotier-* devicemap
+fi
echo 'Uninstall complete.'
echo
diff --git a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip
index a10cb1e5..7ff1a05e 100644
--- a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip
+++ b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x64).aip
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="12.3.1" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
+<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
<COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
- <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
+ <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent;AppXAppDetailsComponent;AppXCapabilitiesComponent;AppXDependenciesComponent;AppXProductDetailsComponent;AppXVisualAssetsComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
@@ -15,50 +15,67 @@
<ROW Property="LIMITUI" MultiBuildValue="DefaultBuild:1"/>
<ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
<ROW Property="Manufacturer" Value="ZeroTier"/>
- <ROW Property="ProductCode" Value="1033:{D7093B7A-71BA-484B-A27C-59ACDAD2206F} " Type="16"/>
+ <ROW Property="ProductCode" Value="1033:{4AFE4740-C680-40FE-B6B0-0C15EB0176F1} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One Virtual Network Port"/>
<ROW Property="ProductVersion" Value="1.0.0" Type="32"/>
+ <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
<ROW Property="UpgradeCode" Value="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}"/>
<ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
<ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/>
- <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+ <ROW Directory="TempFolder" Directory_Parent="TARGETDIR" DefaultDir="TEMPFO~1|TempFolder" IsPseudoRoot="1" DirectoryOptions="3"/>
<ROW Directory="zttap300_Dir" Directory_Parent="APPDIR" DefaultDir="zttap300"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="ProductInformation" ComponentId="{FFBF63D7-E03E-4ACA-966A-824C3DF6DEED}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+ <ROW Component="certutil.exe" ComponentId="{675B9939-A983-4E36-A852-54FB122599B1}" Directory_="zttap300_Dir" Attributes="256" KeyPath="certutil.exe"/>
+ <ROW Component="zttap300.cer" ComponentId="{D72CCDC6-1947-4FE1-9DBA-ECE0A024C1A3}" Directory_="zttap300_Dir" Attributes="0" KeyPath="zttap300.cer" Type="0"/>
+ <ROW Component="zttap300.cer_1" ComponentId="{939B64E0-7725-434B-93FE-B1C30940929E}" Directory_="TempFolder" Attributes="0" KeyPath="zttap300.cer_1" Type="0"/>
<ROW Component="zttap300.inf_2" ComponentId="{CB0C4519-617F-4DB5-9BF5-C6CFC6573F9C}" Directory_="zttap300_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.inf"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
- <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation"/>
+ <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation certutil.exe zttap300.cer zttap300.cer_1"/>
<ROW Feature="zttap300" Feature_Parent="MainFeature" Title="zttap300" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="zttap300.inf_2"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
- <ROW File="zttap300.cat" Component_="zttap300.inf_2" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false"/>
+ <ROW File="certutil.exe" Component_="certutil.exe" FileName="certutil.exe" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\certutil.exe" SelfReg="false" NextFile="zttap300.cer_1" DigSign="true"/>
+ <ROW File="zttap300.cat" Component_="zttap300.inf_2" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.cer"/>
+ <ROW File="zttap300.cer" Component_="zttap300.cer" FileName="zttap300.cer" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false" NextFile="certutil.exe"/>
+ <ROW File="zttap300.cer_1" Component_="zttap300.cer_1" FileName="zttap300.cer" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false"/>
<ROW File="zttap300.inf" Component_="zttap300.inf_2" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.sys"/>
<ROW File="zttap300.sys" Component_="zttap300.inf_2" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.cat"/>
</COMPONENT>
+ <COMPONENT cid="caphyon.advinst.custcomp.AiInstallExecuteSequenceAliasComponent">
+ <ROW AliasRowId="certutil.exe" AliasRowOperation="2" Sequence="4001"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+ <ROW BootstrOptKey="GlobalOptions" GeneralOptions="q" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+ </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTierOne_NDIS6_x64" Languages="en" InstallationType="4" UseLargeSchema="true" MsiPackageType="x64"/>
- <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
<ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
<ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
- <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="[|ProductName]" SignOptions="7" SignTool="0" Thumbprint="2ad023dc7aa92bf4265b33852a2ed2406d2bee86 Subject: ZeroTier Networks LLC&#10;Issuer: DigiCert High Assurance Code Signing CA-1&#10;Valid from 04/24/2015 to 04/01/2016"/>
+ <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="[|ProductName]" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
<ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
@@ -105,10 +122,13 @@
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
<ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+ <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
<ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
<ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
<ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
@@ -116,9 +136,10 @@
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFiles64Folder][Manufacturer]\[ProductName]"/>
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+ <ROW Action="certutil.exe" Type="3154" Source="certutil.exe" Target="-addstore -f TrustedPublisher [#zttap300.cer_1]"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDriverPackagesComponent">
- <ROW InfFileName="zttap300.inf" Flags="6"/>
+ <ROW InfFileName="zttap300.inf" Flags="15"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
@@ -128,19 +149,26 @@
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
<ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1501"/>
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="certutil.exe" Condition="( NOT Installed )" Sequence="6401"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
- <ROW Action="AI_DpiContentScale" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_DpiContentScale" Sequence="52"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="101"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
- <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &gt;= 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 2)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &gt;= 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &gt;= 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 2))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 3))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 4))) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 3))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]" DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
<ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
diff --git a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip
index 831516d9..ef3d58cc 100644
--- a/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip
+++ b/ext/installfiles/windows/ZeroTier One Virtual Network Port (NDIS6_x86).aip
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="12.3.1" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
+<DOCUMENT Type="Advanced Installer" CreateVersion="12.0" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{4070644B-EC9F-4962-B14A-770B9E309DC3}">
<COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
- <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
+ <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiServInstComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;MsiEnvComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;FirewallExceptionComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent;AppXAppDetailsComponent;AppXCapabilitiesComponent;AppXDependenciesComponent;AppXProductDetailsComponent;AppXVisualAssetsComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
@@ -16,50 +16,67 @@
<ROW Property="LIMITUI" MultiBuildValue="DefaultBuild:1"/>
<ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
<ROW Property="Manufacturer" Value="ZeroTier"/>
- <ROW Property="ProductCode" Value="1033:{6223AB10-D6CD-4580-B357-91CCD7F355D2} " Type="16"/>
+ <ROW Property="ProductCode" Value="1033:{75F0AFE9-A004-428A-89DB-9BB9ACC4489E} " Type="16"/>
<ROW Property="ProductLanguage" Value="1033"/>
<ROW Property="ProductName" Value="ZeroTier One Virtual Network Port"/>
<ROW Property="ProductVersion" Value="1.0.0" Type="32"/>
+ <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
<ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
<ROW Property="UpgradeCode" Value="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}"/>
<ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
<ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
<ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/>
- <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
<ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1"/>
<ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+ <ROW Directory="TempFolder" Directory_Parent="TARGETDIR" DefaultDir="TEMPFO~1|TempFolder" IsPseudoRoot="1" DirectoryOptions="3"/>
<ROW Directory="zttap300_Dir" Directory_Parent="APPDIR" DefaultDir="zttap300"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
<ROW Component="ProductInformation" ComponentId="{FFBF63D7-E03E-4ACA-966A-824C3DF6DEED}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+ <ROW Component="certutil.exe" ComponentId="{716192EC-038F-4CB7-9E85-689904DFA5A7}" Directory_="zttap300_Dir" Attributes="256" KeyPath="certutil.exe"/>
+ <ROW Component="zttap300.cer" ComponentId="{38822503-75B2-4D81-8F48-76F776CA921D}" Directory_="zttap300_Dir" Attributes="0" KeyPath="zttap300.cer" Type="0"/>
+ <ROW Component="zttap300.cer_1" ComponentId="{E26B4B86-5B0F-45B7-9B07-4AE5D6FF4F13}" Directory_="TempFolder" Attributes="0" KeyPath="zttap300.cer_1" Type="0"/>
<ROW Component="zttap300.inf_1" ComponentId="{07119034-A51F-4871-B503-09D1340FF248}" Directory_="zttap300_Dir" Attributes="0" Condition="(VersionNT &gt;= 500) AND NOT VersionNT64" KeyPath="zttap300.inf"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
- <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation"/>
+ <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation certutil.exe zttap300.cer zttap300.cer_1"/>
<ROW Feature="zttap300" Feature_Parent="MainFeature" Title="zttap300" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="zttap300.inf_1"/>
<ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
- <ROW File="zttap300.cat" Component_="zttap300.inf_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false"/>
+ <ROW File="certutil.exe" Component_="certutil.exe" FileName="certutil.exe" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\certutil.exe" SelfReg="false" NextFile="zttap300.cer" DigSign="true"/>
+ <ROW File="zttap300.cat" Component_="zttap300.inf_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="certutil.exe"/>
+ <ROW File="zttap300.cer" Component_="zttap300.cer" FileName="zttap300.cer" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false" NextFile="zttap300.cer_1"/>
+ <ROW File="zttap300.cer_1" Component_="zttap300.cer_1" FileName="zttap300.cer" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\zttap300.cer" SelfReg="false"/>
<ROW File="zttap300.inf" Component_="zttap300.inf_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="zttap300.sys"/>
<ROW File="zttap300.sys" Component_="zttap300.inf_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.cat"/>
</COMPONENT>
+ <COMPONENT cid="caphyon.advinst.custcomp.AiInstallExecuteSequenceAliasComponent">
+ <ROW AliasRowId="certutil.exe" AliasRowOperation="2" Sequence="4001"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+ <ROW BootstrOptKey="GlobalOptions" GeneralOptions="q" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+ </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
<ROW BuildKey="DefaultBuild" BuildName="DefaultBuild" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTierOne_NDIS6_x86" Languages="en" InstallationType="4" UseLargeSchema="true"/>
- <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
<ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
<ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
- <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="[|ProductName]" SignOptions="7" SignTool="0" Thumbprint="2ad023dc7aa92bf4265b33852a2ed2406d2bee86 Subject: ZeroTier Networks LLC&#10;Issuer: DigiCert High Assurance Code Signing CA-1&#10;Valid from 04/24/2015 to 04/01/2016"/>
+ <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="[|ProductName]" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
<ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
@@ -106,10 +123,13 @@
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
<ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+ <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
<ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
<ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
<ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
<ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
<ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
@@ -117,9 +137,10 @@
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]"/>
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]"/>
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+ <ROW Action="certutil.exe" Type="3154" Source="certutil.exe" Target="-addstore -f TrustedPublisher [#zttap300.cer_1]"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDriverPackagesComponent">
- <ROW InfFileName="zttap300.inf" Flags="6"/>
+ <ROW InfFileName="zttap300.inf" Flags="15"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
<ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
@@ -129,19 +150,26 @@
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
<ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1501"/>
<ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="certutil.exe" Condition="( NOT Installed )" Sequence="6401"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
<ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
- <ROW Action="AI_DpiContentScale" Sequence="51"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_DpiContentScale" Sequence="52"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="101"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
- <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &gt;= 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 2)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &gt;= 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &gt;= 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 2))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 3))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 4))) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 3))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]" DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild"/>
<ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
<ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip
index fdbbeea9..f75edaed 100644
--- a/ext/installfiles/windows/ZeroTier One.aip
+++ b/ext/installfiles/windows/ZeroTier One.aip
@@ -1,386 +1,484 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="12.5.1" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
- <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
- <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;MsiDriverPackagesComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent;MsiAppSearchComponent"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
- <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
- <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
- <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
- <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
- <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
- <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
- <ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
- <ROW Property="AI_UNINSTALLER" Value="msiexec.exe"/>
- <ROW Property="ALLUSERS" Value="1"/>
- <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]."/>
- <ROW Property="ARPCONTACT" Value="contact@zerotier.com"/>
- <ROW Property="ARPHELPLINK" Value="https://www.zerotier.com/"/>
- <ROW Property="ARPNOMODIFY" MultiBuildValue="DefaultBuild:1"/>
- <ROW Property="ARPNOREPAIR" Value="1"/>
- <ROW Property="ARPPRODUCTICON" Value="ZeroTierIcon.exe" Type="8"/>
- <ROW Property="ARPSYSTEMCOMPONENT" Value="1"/>
- <ROW Property="ARPURLINFOABOUT" Value="https://www.zerotier.com/"/>
- <ROW Property="ARPURLUPDATEINFO" Value="https://www.zerotier.com/"/>
- <ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
- <ROW Property="CTRLS" Value="2"/>
- <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
- <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
- <ROW Property="ProductCode" Value="1033:{856F3AA0-1B20-427F-8509-877EBB751BAA} " Type="16"/>
- <ROW Property="ProductLanguage" Value="1033"/>
- <ROW Property="ProductName" Value="ZeroTier One"/>
- <ROW Property="ProductVersion" Value="1.1.12" Type="32"/>
- <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
- <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
- <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND"/>
- <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
- <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows 2000, Windows 2000 Service Pack 1, Windows 2000 Service Pack 2, Windows 2000 Service Pack 3, Windows 2000 Service Pack 4, Windows XP x86, Windows XP x86 Service Pack 1, Windows XP x86 Service Pack 2, Windows XP x86 Service Pack 3, Windows Server 2003 x86, Windows Server 2003 x86 Service Pack 1, Windows Server 2003 x86 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP x64, Windows XP x64 Service Pack 1, Windows XP x64 Service Pack 2, Windows Server 2003 x64, Windows Server 2003 x64 Service Pack 1, Windows Server 2003 x64 Service Pack 2" ValueLocId="-"/>
- <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP x64, Windows Server 2003 x64" ValueLocId="-"/>
- <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows 2000, Windows XP x86, Windows Server 2003 x86" ValueLocId="-"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
- <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="2"/>
- <ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
- <ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
- <ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
- <ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
- <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
- <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
- <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
- <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
- <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
- <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
- <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
- <ROW Component="AI_CustomARPName" ComponentId="{B80291A9-8CEC-406C-BB08-611D3CEF1BF6}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
- <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
- <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
- <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
- <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
- <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
- <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
- <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
- <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zerotierone_x86.exe"/>
- <ROW Component="zttap300.cat" ComponentId="{123CD683-FD99-4F0F-8F9B-0222DF409B09}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.cat_2" Type="0"/>
- <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
- <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/>
- <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
- <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" DigSign="true"/>
- <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
- <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
- <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
- <ROW File="zttap300.cat_2" Component_="zttap300.cat" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
- <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
- <ROW File="zttap300.inf" Component_="zttap300.cat" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
- <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
- <ROW File="zttap300.sys_2" Component_="zttap300.cat" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
- <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
- <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
- <ATTRIBUTE name="CurrentBuild" value="DefaultBuild"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent">
- <ATTRIBUTE name="Enable" value="false"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
- <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="108" InstallCondition="(VersionNT64)" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (VersionNT64))"/>
- <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="108" InstallCondition="(NOT VersionNT64)" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (NOT VersionNT64))"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
- <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
- <ROW FileId="ZeroTierOne_NDIS6_x86.msi" ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x86.msi" Content="..\..\bin\tap-windows-ndis6\x86\ZeroTierOne_NDIS6_x86.msi"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
- <ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
- <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
- <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="45f6b8ef02b543eeaa08d5f0f08ebe72c2a8a2d5 Subject: ZeroTier, Inc.&#10;Issuer: DigiCert SHA2 High Assurance Code Signing CA&#10;Valid from 03/22/2016 to 05/06/2019"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
- <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
- <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
- <ROW Fragment="MaintenanceTypeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceTypeDlg.aip"/>
- <ROW Fragment="MaintenanceWelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceWelcomeDlg.aip"/>
- <ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
- <ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
- <ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
- <ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
- <ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
- <ROW Fragment="VerifyRemoveDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRemoveDlg.aip"/>
- <ROW Fragment="VerifyRepairDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRepairDlg.aip"/>
- <ROW Fragment="WelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\WelcomeDlg.aip"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
- <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_FwConfig" TemplateLocId="ActionText.Template.AI_FwConfig"/>
- <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/>
- <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_FwRemove" TemplateLocId="ActionText.Template.AI_FwRemove"/>
- <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." Template="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" TemplateLocId="ActionText.Template.AI_FwRollback"/>
- <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
- <ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " Template="Commit text file changes." DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
- <ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" Template="Updating text file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
- <ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
- <ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " Template="Rolling back text file changes." DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
- <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." Template="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" TemplateLocId="ActionText.Template.AI_XmlCommit"/>
- <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" Template="Configuring XML file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_XmlConfig" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
- <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
- <ROW Action="AI_XmlRemove" Description="Executing XML file configurations" Template="Configuring XML file: &quot;[1]&quot;" DescriptionLocId="ActionText.Description.AI_XmlRemove" TemplateLocId="ActionText.Template.AI_XmlRemove"/>
- <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." Template="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" TemplateLocId="ActionText.Template.AI_XmlRollback"/>
- <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
- <ROW Name="ExternalUICleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ExternalUICleaner.dll"/>
- <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
- <ROW Name="ShortcutFlags.dll" SourcePath="&lt;AI_CUSTACTS&gt;ShortcutFlags.dll"/>
- <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
- <ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
- <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
- <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
- <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
- <ROW Name="xmlCfg.dll" SourcePath="&lt;AI_CUSTACTS&gt;xmlCfg.dll"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
- <ROW Dialog_="WelcomeDlg" Control="WelcomeDlgDialogInitializer" Type="DialogInitializer" X="0" Y="0" Width="0" Height="0" Attributes="0" Order="-1" TextLocId="-" HelpLocId="-" ExtDataLocId="-"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="1"/>
- <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
- <ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
- <ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_MAINT" Ordering="198"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="202"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="ChangeButton" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="501"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="RemoveButton" Event="NewDialog" Argument="VerifyRemoveDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="601"/>
- <ROW Dialog_="VerifyRemoveDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="1"/>
- <ROW Dialog_="MaintenanceTypeDlg" Control_="RepairButton" Event="NewDialog" Argument="VerifyRepairDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="601"/>
- <ROW Dialog_="VerifyRepairDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="1"/>
- <ROW Dialog_="VerifyRepairDlg" Control_="Repair" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="399" Options="1"/>
- <ROW Dialog_="VerifyRemoveDlg" Control_="Remove" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="299" Options="1"/>
- <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="201"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
- <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
- <ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="2" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="3" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfDiskDlg" Condition="AI_INSTALL AND ( (OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;) )" Ordering="4" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL" Ordering="2" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL" Ordering="3" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
- <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
- <ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
- <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
- <ROW Action="AI_ApplyShortcutFlags" Type="3073" Source="ShortcutFlags.dll" Target="UpdateShortcutFlags" WithoutSeq="true"/>
- <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
- <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
- <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
- <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
- <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/>
- <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
- <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
- <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
- <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
- <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
- <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
- <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
- <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
- <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
- <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
- <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
- <ROW Action="AI_PinShortcuts" Type="1" Source="ShortcutFlags.dll" Target="PinShortcuts"/>
- <ROW Action="AI_PinToTaskbar" Type="1025" Source="ShortcutFlags.dll" Target="PinToTaskbar" WithoutSeq="true"/>
- <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
- <ROW Action="AI_PrepareShortcutFlags" Type="1" Source="ShortcutFlags.dll" Target="PrepareActionData"/>
- <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
- <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
- <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
- <ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
- <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
- <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
- <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
- <ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
- <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
- <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
- <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
- <ROW Action="AI_UnpinFromTaskbar" Type="1025" Source="ShortcutFlags.dll" Target="UnpinFromTaskbar" WithoutSeq="true"/>
- <ROW Action="AI_UnpinShortcuts" Type="1" Source="ShortcutFlags.dll" Target="UnpinShortcuts"/>
- <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
- <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
- <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
- <ROW Action="AI_XmlRemove" Type="11265" Source="xmlCfg.dll" Target="OnXmlRemove" WithoutSeq="true"/>
- <ROW Action="AI_XmlRollback" Type="11521" Source="xmlCfg.dll" Target="OnXmlRollback" WithoutSeq="true"/>
- <ROW Action="AI_XmlUninstall" Type="1" Source="xmlCfg.dll" Target="OnXmlUninstall" AdditionalSeq="AI_DATA_SETTER_1"/>
- <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
- <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
- <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
- <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
- <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
- <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
- <ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
- <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
- <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel &lt;&gt; 5)" Sequence="210"/>
- <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1502"/>
- <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="51"/>
- <ROW Action="AI_PrepareShortcutFlags" Condition="(VersionNT &gt; 501) AND ((NOT Installed) OR (Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;)))" Sequence="4501"/>
- <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
- <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
- <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
- <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
- <ROW Action="InstallFinalize" Sequence="6597" SeqType="0" MsiKey="InstallFinalize"/>
- <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1501"/>
- <ROW Action="AI_GetArpIconPath" Sequence="1401"/>
- <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1601"/>
- <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1602"/>
- <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
- <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
- <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
- <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
- <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
- <ROW Action="AI_DetectSoftware" Sequence="101"/>
- <ROW Action="AI_PinShortcuts" Condition="(VersionNT &gt; 600) AND ((NOT Installed) OR (Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;)))" Sequence="4502"/>
- <ROW Action="AI_UnpinShortcuts" Condition="(VersionNT &gt; 600) AND (REMOVE = &quot;ALL&quot;)" Sequence="3199"/>
- <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
- <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
- <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
- <ROW Action="AI_DpiContentScale" Sequence="51"/>
- <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
- <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="102"/>
- <ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
- <ROW Action="AI_DetectSoftware" Sequence="101"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
- <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &gt;= 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 1)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR (((VersionNT64 = 502) AND (ServicePackLevel &lt;&gt; 2)) OR (MsiNTProductType &lt;&gt; 1))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &gt;= 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 1)))) AND ((VersionNT64 &lt;&gt; 502) OR ((VersionNT64 = 502) AND ((MsiNTProductType = 1) OR (ServicePackLevel &lt;&gt; 2)))) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &gt;= 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 1))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 2))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 3))) AND ((VersionNT &lt;&gt; 500) OR ((VersionNT = 500) AND (ServicePackLevel &lt;&gt; 4))) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) AND (((VersionNT &lt;&gt; 501) OR ((VersionNT = 501) AND (ServicePackLevel &lt;&gt; 3))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &gt;= 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 1))) OR VersionNT64) AND (((VersionNT &lt;&gt; 502) OR ((VersionNT = 502) AND (ServicePackLevel &lt;&gt; 2))) OR VersionNT64) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]" DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT40Display]" DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="AI_DETECTED_DOTNET_VERSION &gt;= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
- <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]" DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
- <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/>
- <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/>
- <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/>
- <ROW Registry="DisplayName" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayName" Value="[AI_PRODUCTNAME_ARP]" Component_="AI_CustomARPName"/>
- <ROW Registry="DisplayVersion" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayVersion" Value="[ProductVersion]" Component_="AI_CustomARPName"/>
- <ROW Registry="HelpLink" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpLink" Value="[ARPHELPLINK]" Component_="AI_CustomARPName"/>
- <ROW Registry="HelpTelephone" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpTelephone" Value="[ARPHELPTELEPHONE]" Component_="AI_CustomARPName"/>
- <ROW Registry="InstallLocation" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="InstallLocation" Value="[APPDIR]" Component_="AI_CustomARPName"/>
- <ROW Registry="ModifyPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="ModifyPath" Value="[AI_UNINSTALLER] /I [ProductCode]" Component_="AI_CustomARPName"/>
- <ROW Registry="NoModify" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoModify" Value="#1" Component_="AI_DisableModify"/>
- <ROW Registry="NoRepair" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoRepair" Value="#1" Component_="AI_CustomARPName"/>
- <ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
- <ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
- <ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
- <ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
- <ROW Registry="UninstallPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallPath" Value="[AI_UNINSTALLER] /x [ProductCode]" Component_="AI_CustomARPName"/>
- <ROW Registry="UninstallString" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallString" Value="[AI_UNINSTALLER] /x [ProductCode]" Component_="AI_CustomARPName"/>
- <ROW Registry="Version" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Version" Value="[ProductVersion]" Component_="ProductInformation"/>
- <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
- <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiRemoveFileComponent">
- <ROW FileKey="devcon.log" Component_="ProductInformation" FileName="devcon.log" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="devcon_x64.exe" Component_="ProductInformation" FileName="devcon_x64.exe" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="devcon_x86.exe" Component_="ProductInformation" FileName="devcon_x86.exe" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="node.log" Component_="ProductInformation" FileName="node.log" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="node.log.old" Component_="ProductInformation" FileName="node.log.old" DirProperty="One_Dir" InstallMode="3"/>
- <ROW FileKey="roottopology" Component_="ProductInformation" FileName="root-topology" DirProperty="One_Dir" InstallMode="3"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
- <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
- <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiServInstComponent">
- <ROW ServiceInstall="zerotierone_x64.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x64.exe" Description="Ethernet Virtualization Service"/>
- <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
- <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR" CustomFlags="1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
- <ATTRIBUTE name="UsedTheme" value="classic"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
- <ROW UpgradeCode="[|UpgradeCode]" VersionMin="0.0.1" VersionMax="[|ProductVersion]" Attributes="257" ActionProperty="OLDPRODUCTS"/>
- <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.SoftwareIdentificationComponent">
- <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
- <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
- <ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
- <ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
- <ROW Key="zerotiercli.bat" Component="zerotierone_x64.exe" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
- <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
- <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
- <ROW XmlAttribute="xmlnsxsi" XmlElement="swidsoftware_identification_tag" Name="xmlns:xsi" Flags="14" Order="2" Value="http://www.w3.org/2001/XMLSchema-instance"/>
- <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
- <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="12"/>
- <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
- <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
- <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="1"/>
- <ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]"/>
- <ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
- <ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
- <ROW XmlElement="swidname_3" ParentElement="swidtag_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
- <ROW XmlElement="swidnumeric" ParentElement="swidproduct_version" Name="swid:numeric" Condition="1" Order="1" Flags="14"/>
- <ROW XmlElement="swidproduct_title" ParentElement="swidsoftware_identification_tag" Name="swid:product_title" Condition="1" Order="1" Flags="14" Text="[ProductName]"/>
- <ROW XmlElement="swidproduct_version" ParentElement="swidsoftware_identification_tag" Name="swid:product_version" Condition="1" Order="2" Flags="14"/>
- <ROW XmlElement="swidregid" ParentElement="swidsoftware_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidregid_1" ParentElement="swidsoftware_licensor" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidregid_2" ParentElement="swidtag_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidreview" ParentElement="swidnumeric" Name="swid:review" Condition="1" Order="3" Flags="14" Text="0"/>
- <ROW XmlElement="swidsoftware_creator" ParentElement="swidsoftware_identification_tag" Name="swid:software_creator" Condition="1" Order="3" Flags="14"/>
- <ROW XmlElement="swidsoftware_id" ParentElement="swidsoftware_identification_tag" Name="swid:software_id" Condition="1" Order="5" Flags="14"/>
- <ROW XmlElement="swidsoftware_identification_tag" Name="swid:software_identification_tag" Condition="1" Order="0" Flags="14"/>
- <ROW XmlElement="swidsoftware_licensor" ParentElement="swidsoftware_identification_tag" Name="swid:software_licensor" Condition="1" Order="4" Flags="14"/>
- <ROW XmlElement="swidtag_creator" ParentElement="swidsoftware_identification_tag" Name="swid:tag_creator" Condition="1" Order="6" Flags="14"/>
- <ROW XmlElement="swidtag_creator_regid" ParentElement="swidsoftware_id" Name="swid:tag_creator_regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
- <ROW XmlElement="swidunique_id" ParentElement="swidsoftware_id" Name="swid:unique_id" Condition="1" Order="0" Flags="14" Text="ZeroTierOne"/>
- </COMPONENT>
- <COMPONENT cid="caphyon.advinst.msicomp.XmlFileComponent">
- <ROW XmlFile="regid.199509.com.example_ProductName.swidtag" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="APPDIR" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
- <ROW XmlFile="regid.199509.com.example_ProductName.swidtag_1" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="regid.201001.com.zerotier_Dir" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
- </COMPONENT>
-</DOCUMENT>
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
+ <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
+ <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;AppXProductDetailsComponent;AppXDependenciesComponent;AppXAppDetailsComponent;AppXVisualAssetsComponent;AppXCapabilitiesComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
+ <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
+ <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
+ <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
+ <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
+ <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
+ <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
+ <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
+ <ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
+ <ROW Property="AI_UNINSTALLER" Value="msiexec.exe"/>
+ <ROW Property="ALLUSERS" Value="1"/>
+ <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]."/>
+ <ROW Property="ARPCONTACT" Value="contact@zerotier.com"/>
+ <ROW Property="ARPHELPLINK" Value="https://www.zerotier.com/"/>
+ <ROW Property="ARPNOMODIFY" MultiBuildValue="DefaultBuild:1"/>
+ <ROW Property="ARPNOREPAIR" Value="1" MultiBuildValue="ExeBuild:1"/>
+ <ROW Property="ARPPRODUCTICON" Value="ZeroTierIcon.exe" Type="8"/>
+ <ROW Property="ARPSYSTEMCOMPONENT" Value="1"/>
+ <ROW Property="ARPURLINFOABOUT" Value="https://www.zerotier.com/"/>
+ <ROW Property="ARPURLUPDATEINFO" Value="https://www.zerotier.com/"/>
+ <ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
+ <ROW Property="CTRLS" Value="2"/>
+ <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
+ <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
+ <ROW Property="ProductCode" Value="1033:{BCCDCFD7-34FA-473E-9E50-20314D1017F6} " Type="16"/>
+ <ROW Property="ProductLanguage" Value="1033"/>
+ <ROW Property="ProductName" Value="ZeroTier One"/>
+ <ROW Property="ProductVersion" Value="1.2.6" Type="32"/>
+ <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
+ <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
+ <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
+ <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
+ <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
+ <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+ <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
+ <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="2"/>
+ <ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
+ <ROW Directory="FontsFolder" Directory_Parent="TARGETDIR" DefaultDir="FONTSF~1|FontsFolder" IsPseudoRoot="1"/>
+ <ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
+ <ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
+ <ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
+ <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+ <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
+ <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
+ <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
+ <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
+ <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
+ <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
+ <ROW Component="AI_CustomARPName" ComponentId="{A4BB4E68-0DD2-4052-BE60-CDCCB9BAC409}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
+ <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
+ <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
+ <ROW Component="Hardcodet.Wpf.TaskbarNotification.dll" ComponentId="{BEA825AF-2555-44AF-BE40-47FFC16DCBA6}" Directory_="APPDIR" Attributes="0" KeyPath="Hardcodet.Wpf.TaskbarNotification.dll"/>
+ <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
+ <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+ <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
+ <ROW Component="copyutil.exe" ComponentId="{9B9E89FB-81CB-4500-978B-11E2FA81B5B4}" Directory_="APPDIR" Attributes="0" KeyPath="copyutil.exe"/>
+ <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
+ <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
+ <ROW Component="segoeui.ttf" ComponentId="{9F415308-A118-419F-AD8A-678DEA856B78}" Directory_="FontsFolder" Attributes="144" Type="0"/>
+ <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
+ <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zerotierone_x86.exe"/>
+ <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
+ <ROW Component="zttap300.inf" ComponentId="{D4839F5E-FB94-41CB-9B1B-177A97ADC904}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.inf"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
+ <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify AI_ExePath Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier segoeui.ttf zerotierone_x64.exe zerotierone_x86.exe zttap300.cat_1"/>
+ <ROW Feature="zttap300" Feature_Parent="ZeroTierOne" Title="zttap300" Description="ZeroTier Virtual Network Port Driver" Display="1" Level="1" Directory_="APPDIR" Attributes="16" Components="zttap300.inf"/>
+ <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
+ <ROW File="Hardcodet.Wpf.TaskbarNotification.dll" Component_="Hardcodet.Wpf.TaskbarNotification.dll" FileName="HARDCO~1.DLL|Hardcodet.Wpf.TaskbarNotification.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Hardcodet.Wpf.TaskbarNotification.dll" SelfReg="false" NextFile="segoeui.ttf" DigSign="true"/>
+ <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" NextFile="copyutil.exe" DigSign="true"/>
+ <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
+ <ROW File="copyutil.exe" Component_="copyutil.exe" FileName="copyutil.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\copyutil\bin\Release\copyutil.exe" SelfReg="false" NextFile="Hardcodet.Wpf.TaskbarNotification.dll" DigSign="true"/>
+ <ROW File="segoeui.ttf" Component_="segoeui.ttf" FileName="segoeui.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeui.ttf" SelfReg="false" NextFile="segoeuib.ttf"/>
+ <ROW File="segoeuib.ttf" Component_="segoeui.ttf" FileName="segoeuib.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuib.ttf" SelfReg="false" NextFile="segoeuii.ttf"/>
+ <ROW File="segoeuii.ttf" Component_="segoeui.ttf" FileName="segoeuii.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuii.ttf" SelfReg="false" NextFile="segoeuiz.ttf"/>
+ <ROW File="segoeuiz.ttf" Component_="segoeui.ttf" FileName="segoeuiz.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuiz.ttf" SelfReg="false"/>
+ <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
+ <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
+ <ROW File="zttap300.cat_2" Component_="zttap300.inf" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
+ <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
+ <ROW File="zttap300.inf" Component_="zttap300.inf" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
+ <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
+ <ROW File="zttap300.sys_2" Component_="zttap300.inf" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
+ <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.AiPersistentDataComponent">
+ <ROW PersistentRow="segoeui.ttf" Type="0" Condition="1"/>
+ <ROW PersistentRow="segoeuib.ttf" Type="0" Condition="1"/>
+ <ROW PersistentRow="segoeuii.ttf" Type="0" Condition="1"/>
+ <ROW PersistentRow="segoeuiz.ttf" Type="0" Condition="1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+ <ROW BootstrOptKey="GlobalOptions" GeneralOptions="qh" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BootstrapperUISequenceComponent">
+ <ROW Action="AI_DetectSoftware" Sequence="101"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
+ <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
+ <ROW BuildKey="ExeBuild" BuildName="update" BuildOrder="2" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="2" CabsLocation="1" CompressCabs="false" UseLzma="true" LzmaMethod="2" LzmaCompressionLevel="4" PackageType="1" FilesInsideExe="true" ExeIconPath="..\..\..\artwork\ZeroTierIcon.ico" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName] [|ProductVersion]\install" MsiCmdLine="/qn" ExtUI="true" UseLargeSchema="true" ExeName="zt1_update_2_1,2_[|ProductVersion]_0"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent">
+ <ATTRIBUTE name="Enable" value="false"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
+ <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="110" InstallCondition="(NOT Installed) AND VersionNT64" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND VersionNT64"/>
+ <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="110" InstallCondition="(NOT Installed) AND (NOT VersionNT64)" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND (NOT VersionNT64)"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
+ <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
+ <ROW FileId="ZeroTierOne_NDIS6_x86.msi" ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x86.msi" Content="..\..\bin\tap-windows-ndis6\x86\ZeroTierOne_NDIS6_x86.msi"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
+ <ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
+ <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
+ <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
+ <ROW FirewallException="ZeroTierOneControl" DisplayName="ZeroTier One TCP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="TCP"/>
+ <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
+ <ROW FirewallException="ZeroTierOnex64Binary" DisplayName="ZeroTier One x64 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="0" AppPath="[#zerotierone_x64.exe]" Port="*" Protocol="ANY"/>
+ <ROW FirewallException="ZeroTierOnex86Binary" DisplayName="ZeroTier One x86 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="0" AppPath="[#zerotierone_x86.exe]" Port="*" Protocol="ANY"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
+ <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
+ <ROW Fragment="MaintenanceTypeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceTypeDlg.aip"/>
+ <ROW Fragment="MaintenanceWelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceWelcomeDlg.aip"/>
+ <ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
+ <ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
+ <ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
+ <ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
+ <ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
+ <ROW Fragment="VerifyRemoveDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRemoveDlg.aip"/>
+ <ROW Fragment="VerifyRepairDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRepairDlg.aip"/>
+ <ROW Fragment="WelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\WelcomeDlg.aip"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
+ <ROW Action="AI_AiBackupImmediate" Description="Preparing backup operation" DescriptionLocId="ActionText.Description.AI_AiBackupImmediate" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupImmediate"/>
+ <ROW Action="AI_AiBackupRollback" Description="Rollback backup" DescriptionLocId="ActionText.Description.AI_AiBackupRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupRollback"/>
+ <ROW Action="AI_AiRestoreDeferred" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
+ <ROW Action="AI_AiRestoreDeferredImpersonate" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
+ <ROW Action="AI_AiRestoreRollback" Description="Rollback restore" DescriptionLocId="ActionText.Description.AI_AiRestoreRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreRollback"/>
+ <ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
+ <ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
+ <ROW Action="AI_ExtractFiles" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
+ <ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
+ <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwConfig" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwConfig"/>
+ <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/>
+ <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwRemove" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwRemove"/>
+ <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" Template="Rolling back Windows Firewall configurations." TemplateLocId="ActionText.Template.AI_FwRollback"/>
+ <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
+ <ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" Template="Commit text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
+ <ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" Template="Updating text file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
+ <ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
+ <ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" Template="Rolling back text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
+ <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" Template="Committing XML file configurations." TemplateLocId="ActionText.Template.AI_XmlCommit"/>
+ <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlConfig" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
+ <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
+ <ROW Action="AI_XmlRemove" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlRemove" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlRemove"/>
+ <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" Template="Rolling back XML file configurations." TemplateLocId="ActionText.Template.AI_XmlRollback"/>
+ <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent">
+ <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="ExeBuild"/>
+ <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="ExeBuild"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
+ <ROW Name="ExternalUICleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ExternalUICleaner.dll"/>
+ <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
+ <ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
+ <ROW Name="ResourceCleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ResourceCleaner.dll"/>
+ <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
+ <ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
+ <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
+ <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
+ <ROW Name="lzmaextractor.dll" SourcePath="&lt;AI_CUSTACTS&gt;lzmaextractor.dll"/>
+ <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
+ <ROW Name="xmlCfg.dll" SourcePath="&lt;AI_CUSTACTS&gt;xmlCfg.dll"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiConditionComponent">
+ <ROW Feature_="zttap300" Level="0" Condition="((VersionNT &lt; 500) OR (NOT VersionNT))"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
+ <ROW Dialog_="WelcomeDlg" Control="WelcomeDlgDialogInitializer" Type="DialogInitializer" X="0" Y="0" Width="0" Height="0" Attributes="0" Order="-1" TextLocId="-" HelpLocId="-" ExtDataLocId="-"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="1"/>
+ <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
+ <ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
+ <ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_MAINT" Ordering="198"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="202"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="ChangeButton" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="501"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="RemoveButton" Event="NewDialog" Argument="VerifyRemoveDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="601"/>
+ <ROW Dialog_="VerifyRemoveDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="1"/>
+ <ROW Dialog_="MaintenanceTypeDlg" Control_="RepairButton" Event="NewDialog" Argument="VerifyRepairDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="601"/>
+ <ROW Dialog_="VerifyRepairDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="1"/>
+ <ROW Dialog_="VerifyRepairDlg" Control_="Repair" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="399" Options="1"/>
+ <ROW Dialog_="VerifyRemoveDlg" Control_="Remove" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="299" Options="1"/>
+ <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="201"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
+ <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
+ <ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="2" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="3" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfDiskDlg" Condition="AI_INSTALL AND ( (OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;) )" Ordering="4" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL" Ordering="2" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL" Ordering="3" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
+ <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
+ <ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="102"/>
+ <ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="101"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
+ <ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
+ <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+ <ROW Action="AI_AiBackupCleanup" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupCleanup" WithoutSeq="true"/>
+ <ROW Action="AI_AiBackupImmediate" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupImmediate"/>
+ <ROW Action="AI_AiBackupRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiBackupRollback"/>
+ <ROW Action="AI_AiRestoreDeferred" Type="11265" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferred"/>
+ <ROW Action="AI_AiRestoreDeferredImpersonate" Type="9217" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferredImpersonate"/>
+ <ROW Action="AI_AiRestoreRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiRestoreRollback" WithoutSeq="true"/>
+ <ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
+ <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
+ <ROW Action="AI_DATA_SETTER_4" Type="51" Source="AI_ExtractFiles" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_DATA_SETTER_6" Type="51" Source="CustomActionData" Target="ZeroTier One.exe"/>
+ <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
+ <ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
+ <ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
+ <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/>
+ <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
+ <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+ <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
+ <ROW Action="AI_EstimateExtractFiles" Type="1" Source="Prereq.dll" Target="EstimateExtractFiles"/>
+ <ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/>
+ <ROW Action="AI_ExtractFiles" Type="1025" Source="Prereq.dll" Target="ExtractSourceFiles" AdditionalSeq="AI_DATA_SETTER_4"/>
+ <ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/>
+ <ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/>
+ <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
+ <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
+ <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
+ <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
+ <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
+ <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
+ <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
+ <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
+ <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+ <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
+ <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
+ <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
+ <ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
+ <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
+ <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
+ <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
+ <ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
+ <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
+ <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
+ <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
+ <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
+ <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
+ <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
+ <ROW Action="AI_XmlRemove" Type="11265" Source="xmlCfg.dll" Target="OnXmlRemove" WithoutSeq="true"/>
+ <ROW Action="AI_XmlRollback" Type="11521" Source="xmlCfg.dll" Target="OnXmlRollback" WithoutSeq="true"/>
+ <ROW Action="AI_XmlUninstall" Type="1" Source="xmlCfg.dll" Target="OnXmlUninstall" AdditionalSeq="AI_DATA_SETTER_1"/>
+ <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
+ <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
+ <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+ <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
+ <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
+ <ROW Action="TerminateUI" Type="65" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_6"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
+ <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
+ <ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiFontsComponent">
+ <ROW File_="segoeui.ttf"/>
+ <ROW File_="segoeuib.ttf"/>
+ <ROW File_="segoeuii.ttf"/>
+ <ROW File_="segoeuiz.ttf"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
+ <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
+ <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel &lt;&gt; 5)" Sequence="210"/>
+ <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
+ <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1503"/>
+ <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
+ <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
+ <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
+ <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
+ <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
+ <ROW Action="InstallFinalize" Sequence="6600" SeqType="0" MsiKey="InstallFinalize"/>
+ <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1502"/>
+ <ROW Action="AI_GetArpIconPath" Sequence="1402"/>
+ <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1603"/>
+ <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/>
+ <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
+ <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
+ <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
+ <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
+ <ROW Action="AI_DetectSoftware" Sequence="103"/>
+ <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99" Builds="ExeBuild"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="102" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="199" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="198" Builds="ExeBuild"/>
+ <ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="197" Builds="ExeBuild"/>
+ <ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="196" Builds="ExeBuild"/>
+ <ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1549" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1548" Builds="ExeBuild"/>
+ <ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="6594" Builds="ExeBuild"/>
+ <ROW Action="AI_ExtractFiles" Sequence="3998" Builds="ExeBuild"/>
+ <ROW Action="AI_DATA_SETTER_4" Sequence="3997"/>
+ <ROW Action="AI_EstimateExtractFiles" Sequence="3999" Builds="ExeBuild"/>
+ <ROW Action="TerminateUI" Sequence="1602"/>
+ <ROW Action="AI_DATA_SETTER_6" Sequence="1601"/>
+ <ROW Action="AI_AiBackupImmediate" Sequence="1401"/>
+ <ROW Action="AI_AiBackupRollback" Sequence="1501"/>
+ <ROW Action="AI_AiRestoreDeferred" Sequence="6595"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_AiRestoreDeferredImpersonate" Sequence="6596"/>
+ <ROW Action="AI_AppSearchEx" Sequence="101"/>
+ <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
+ <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
+ <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+ <ROW Action="AI_DpiContentScale" Sequence="52"/>
+ <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+ <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="103"/>
+ <ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
+ <ROW Action="AI_DetectSoftware" Sequence="102"/>
+ <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+ <ROW Action="AI_AppSearchEx" Sequence="101"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
+ <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on [WindowsTypeNT40Display]." DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ <ROW Condition="AI_DETECTED_DOTNET_VERSION &gt;= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
+ <ROW Condition="SETUPEXEDIR OR (REMOVE=&quot;ALL&quot;)" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/>
+ <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
+ <ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
+ <ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
+ <ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
+ <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/>
+ <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/>
+ <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/>
+ <ROW Registry="DisplayName" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayName" Value="[AI_PRODUCTNAME_ARP]" Component_="AI_CustomARPName"/>
+ <ROW Registry="DisplayVersion" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayVersion" Value="[ProductVersion]" Component_="AI_CustomARPName"/>
+ <ROW Registry="HelpLink" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpLink" Value="[ARPHELPLINK]" Component_="AI_CustomARPName"/>
+ <ROW Registry="HelpTelephone" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpTelephone" Value="[ARPHELPTELEPHONE]" Component_="AI_CustomARPName"/>
+ <ROW Registry="InstallLocation" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="InstallLocation" Value="[APPDIR]" Component_="AI_CustomARPName"/>
+ <ROW Registry="ModifyPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="ModifyPath" Value="[AI_UNINSTALLER] /I [ProductCode]" Component_="AI_CustomARPName"/>
+ <ROW Registry="NoModify" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoModify" Value="#1" Component_="AI_DisableModify"/>
+ <ROW Registry="NoRepair" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoRepair" Value="#1" Component_="AI_CustomARPName"/>
+ <ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
+ <ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
+ <ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
+ <ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
+ <ROW Registry="UninstallPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallPath" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
+ <ROW Registry="UninstallString" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallString" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
+ <ROW Registry="Version" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Version" Value="[ProductVersion]" Component_="ProductInformation"/>
+ <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
+ <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
+ <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
+ <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiServInstComponent">
+ <ROW ServiceInstall="zerotierone_x64.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x64.exe" Description="Ethernet Virtualization Service"/>
+ <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
+ <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
+ <ATTRIBUTE name="UsedTheme" value="classic"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
+ <ROW UpgradeCode="[|UpgradeCode]" VersionMin="0.0.1" VersionMax="[|ProductVersion]" Attributes="257" ActionProperty="OLDPRODUCTS"/>
+ <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.PreReqSearchComponent">
+ <ROW SearchKey="UpgradeCode" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="2" Property="ZTTAP300_X86_INSTALLED"/>
+ <ROW SearchKey="_" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="1" Property="ZTTAP300_X64_INSTALLED"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.SoftwareIdentificationComponent">
+ <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
+ <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
+ <ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
+ <ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
+ <ROW Name="Append/Create" TxtUpdateSet="zerotiercli1.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -i %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -i %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
+ <ROW Name="Replace" TxtUpdateSet="zerotiercli1.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
+ <ROW Key="zerotiercli.bat" Component="regid.201001.com.zerotier" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
+ <ROW Key="zerotiercli1.bat" Component="regid.201001.com.zerotier" FileName="zerotier-idtool.bat" Directory="APPDIR" Options="17"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
+ <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
+ <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
+ <ROW XmlAttribute="xmlnsxsi" XmlElement="swidsoftware_identification_tag" Name="xmlns:xsi" Flags="14" Order="2" Value="http://www.w3.org/2001/XMLSchema-instance"/>
+ <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
+ <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="6"/>
+ <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
+ <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
+ <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="2"/>
+ <ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]"/>
+ <ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+ <ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+ <ROW XmlElement="swidname_3" ParentElement="swidtag_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+ <ROW XmlElement="swidnumeric" ParentElement="swidproduct_version" Name="swid:numeric" Condition="1" Order="1" Flags="14"/>
+ <ROW XmlElement="swidproduct_title" ParentElement="swidsoftware_identification_tag" Name="swid:product_title" Condition="1" Order="1" Flags="14" Text="[ProductName]"/>
+ <ROW XmlElement="swidproduct_version" ParentElement="swidsoftware_identification_tag" Name="swid:product_version" Condition="1" Order="2" Flags="14"/>
+ <ROW XmlElement="swidregid" ParentElement="swidsoftware_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidregid_1" ParentElement="swidsoftware_licensor" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidregid_2" ParentElement="swidtag_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidreview" ParentElement="swidnumeric" Name="swid:review" Condition="1" Order="3" Flags="14" Text="0"/>
+ <ROW XmlElement="swidsoftware_creator" ParentElement="swidsoftware_identification_tag" Name="swid:software_creator" Condition="1" Order="3" Flags="14"/>
+ <ROW XmlElement="swidsoftware_id" ParentElement="swidsoftware_identification_tag" Name="swid:software_id" Condition="1" Order="5" Flags="14"/>
+ <ROW XmlElement="swidsoftware_identification_tag" Name="swid:software_identification_tag" Condition="1" Order="0" Flags="14"/>
+ <ROW XmlElement="swidsoftware_licensor" ParentElement="swidsoftware_identification_tag" Name="swid:software_licensor" Condition="1" Order="4" Flags="14"/>
+ <ROW XmlElement="swidtag_creator" ParentElement="swidsoftware_identification_tag" Name="swid:tag_creator" Condition="1" Order="6" Flags="14"/>
+ <ROW XmlElement="swidtag_creator_regid" ParentElement="swidsoftware_id" Name="swid:tag_creator_regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+ <ROW XmlElement="swidunique_id" ParentElement="swidsoftware_id" Name="swid:unique_id" Condition="1" Order="0" Flags="14" Text="ZeroTierOne"/>
+ </COMPONENT>
+ <COMPONENT cid="caphyon.advinst.msicomp.XmlFileComponent">
+ <ROW XmlFile="regid.199509.com.example_ProductName.swidtag" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="APPDIR" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
+ <ROW XmlFile="regid.199509.com.example_ProductName.swidtag_1" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="regid.201001.com.zerotier_Dir" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
+ </COMPONENT>
+</DOCUMENT>
diff --git a/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1 b/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1
index b29fd99d..9a0f6bac 100644
--- a/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1
+++ b/ext/installfiles/windows/chocolatey/zerotier-one/tools/chocolateyinstall.ps1
@@ -1,7 +1,7 @@
$packageName = 'zerotier-one'
$installerType = 'msi'
-$url = 'https://download.zerotier.com/RELEASES/1.1.12/dist/ZeroTier%20One.msi'
-$url64 = 'https://download.zerotier.com/RELEASES/1.1.12/dist/ZeroTier%20One.msi'
+$url = 'https://download.zerotier.com/RELEASES/1.2.4/dist/ZeroTier%20One.msi'
+$url64 = 'https://download.zerotier.com/RELEASES/1.2.4/dist/ZeroTier%20One.msi'
$silentArgs = '/quiet'
$validExitCodes = @(0,3010)
diff --git a/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec b/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec
index 473007c8..3d090a7b 100644
--- a/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec
+++ b/ext/installfiles/windows/chocolatey/zerotier-one/zerotier-one.nuspec
@@ -8,8 +8,8 @@
This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Reference. Chocolatey uses a special version of NuGet.Core that allows us to do more than was initially possible. As such there are certain things to be aware of:
* the package xmlns schema url may cause issues with nuget.exe
-* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
-* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
+* Any of the following elements can ONLY be used by choco tools - projectSourceUrl, docsUrl, mailingListUrl, bugTrackerUrl, packageSourceUrl, provides, conflicts, replaces
+* nuget.exe can still install packages with those elements but they are ignored. Any authoring tools or commands will error on those elements
-->
<!-- You can embed software files directly into packages, as long as you are not bound by distribution rights. -->
@@ -26,7 +26,7 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
<!-- version should MATCH as closely as possible with the underlying software -->
<!-- Is the version a prerelease of a version? https://docs.nuget.org/create/versioning#creating-prerelease-packages -->
<!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version number is final, that is considered a released version and not a prerelease. -->
- <version>1.1.12</version>
+ <version>1.2.6</version>
<!-- <packageSourceUrl>Where is this Chocolatey package located (think GitHub)? packageSourceUrl is highly recommended for the community feed</packageSourceUrl>-->
<!-- owners is a poor name for maintainers of the package. It sticks around by this name for compatibility reasons. It basically means you. -->
<!--<owners>ZeroTier, Inc.</owners>-->
@@ -51,7 +51,7 @@ This is a nuspec. It mostly adheres to https://docs.nuget.org/create/Nuspec-Refe
<summary>ZeroTier One Virtual Network Endpoint for Windows</summary>
<description>ZeroTier is a smart switch for Earth with VLAN capability. See https://www.zerotier.com/ for more information.</description>
<!-- <releaseNotes>__REPLACE_OR_REMOVE__MarkDown_Okay</releaseNotes> -->
- <!-- =============================== -->
+ <!-- =============================== -->
<!-- Specifying dependencies and version ranges? https://docs.nuget.org/create/versioning#specifying-version-ranges-in-.nuspec-files -->
<!--<dependencies>
diff --git a/ext/json-parser/AUTHORS b/ext/json-parser/AUTHORS
deleted file mode 100644
index 6a5c799f..00000000
--- a/ext/json-parser/AUTHORS
+++ /dev/null
@@ -1,20 +0,0 @@
-All contributors arranged by first commit:
-
-James McLaughlin
-Alex Gartrell
-Peter Scott
-Mathias Kaerlev
-Emiel Mols
-Czarek Tomczak
-Nicholas Braden
-Ivan Kozub
-Árpád Goretity
-Igor Gnatenko
-Haïkel Guémar
-Tobias Waldekranz
-Patrick Donnelly
-Wilmer van der Gaast
-Jin Wei
-François Cartegnie
-Matthijs Boelstra
-
diff --git a/ext/json-parser/LICENSE b/ext/json-parser/LICENSE
deleted file mode 100644
index 1aee375e..00000000
--- a/ext/json-parser/LICENSE
+++ /dev/null
@@ -1,26 +0,0 @@
-
- Copyright (C) 2012, 2013 James McLaughlin et al. 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.
-
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, 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/ext/json-parser/README.md b/ext/json-parser/README.md
deleted file mode 100644
index e0b70b6d..00000000
--- a/ext/json-parser/README.md
+++ /dev/null
@@ -1,97 +0,0 @@
-Very low footprint JSON parser written in portable ANSI C.
-
-* BSD licensed with no dependencies (i.e. just drop the C file into your project)
-* Never recurses or allocates more memory than it needs
-* Very simple API with operator sugar for C++
-
-[![Build Status](https://secure.travis-ci.org/udp/json-parser.png)](http://travis-ci.org/udp/json-parser)
-
-_Want to serialize? Check out [json-builder](https://github.com/udp/json-builder)!_
-
-Installing
-----------
-
-There is now a makefile which will produce a libjsonparser static and dynamic library. However, this
-is _not_ required to build json-parser, and the source files (`json.c` and `json.h`) should be happy
-in any build system you already have in place.
-
-
-API
----
-
- json_value * json_parse (const json_char * json,
- size_t length);
-
- json_value * json_parse_ex (json_settings * settings,
- const json_char * json,
- size_t length,
- char * error);
-
- void json_value_free (json_value *);
-
-The `type` field of `json_value` is one of:
-
-* `json_object` (see `u.object.length`, `u.object.values[x].name`, `u.object.values[x].value`)
-* `json_array` (see `u.array.length`, `u.array.values`)
-* `json_integer` (see `u.integer`)
-* `json_double` (see `u.dbl`)
-* `json_string` (see `u.string.ptr`, `u.string.length`)
-* `json_boolean` (see `u.boolean`)
-* `json_null`
-
-
-Compile-Time Options
---------------------
-
- -DJSON_TRACK_SOURCE
-
-Stores the source location (line and column number) inside each `json_value`.
-
-This is useful for application-level error reporting.
-
-
-Runtime Options
----------------
-
- settings |= json_enable_comments;
-
-Enables C-style `// line` and `/* block */` comments.
-
- size_t value_extra
-
-The amount of space (if any) to allocate at the end of each `json_value`, in
-order to give the application space to add metadata.
-
- void * (* mem_alloc) (size_t, int zero, void * user_data);
- void (* mem_free) (void *, void * user_data);
-
-Custom allocator routines. If NULL, the default `malloc` and `free` will be used.
-
-The `user_data` pointer will be forwarded from `json_settings` to allow application
-context to be passed.
-
-
-Changes in version 1.1.0
-------------------------
-
-* UTF-8 byte order marks are now skipped if present
-
-* Allows cross-compilation by honoring --host if given (@wkz)
-
-* Maximum size for error buffer is now exposed in header (@LB--)
-
-* GCC warning for `static` after `const` fixed (@batrick)
-
-* Optional support for C-style line and block comments added (@Jin-W-FS)
-
-* `name_length` field added to object values
-
-* It is now possible to retrieve the source line/column number of a parsed `json_value` when `JSON_TRACK_SOURCE` is enabled
-
-* The application may now extend `json_value` using the `value_extra` setting
-
-* Un-ambiguate pow call in the case of C++ overloaded pow (@fcartegnie)
-
-* Fix null pointer de-reference when a non-existing array is closed and no root value is present
-
-
diff --git a/ext/json-parser/json.c b/ext/json-parser/json.c
deleted file mode 100644
index 166cdcb6..00000000
--- a/ext/json-parser/json.c
+++ /dev/null
@@ -1,1012 +0,0 @@
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, 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.
- */
-
-#include "json.h"
-
-#ifdef _MSC_VER
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #pragma warning(disable:4996)
-#endif
-
-const struct _json_value json_value_none;
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-
-typedef unsigned int json_uchar;
-
-static unsigned char hex_value (json_char c)
-{
- if (isdigit(c))
- return c - '0';
-
- switch (c) {
- case 'a': case 'A': return 0x0A;
- case 'b': case 'B': return 0x0B;
- case 'c': case 'C': return 0x0C;
- case 'd': case 'D': return 0x0D;
- case 'e': case 'E': return 0x0E;
- case 'f': case 'F': return 0x0F;
- default: return 0xFF;
- }
-}
-
-typedef struct
-{
- unsigned long used_memory;
-
- unsigned int uint_max;
- unsigned long ulong_max;
-
- json_settings settings;
- int first_pass;
-
- const json_char * ptr;
- unsigned int cur_line, cur_col;
-
-} json_state;
-
-static void * default_alloc (size_t size, int zero, void * user_data)
-{
- return zero ? calloc (1, size) : malloc (size);
-}
-
-static void default_free (void * ptr, void * user_data)
-{
- free (ptr);
-}
-
-static void * json_alloc (json_state * state, unsigned long size, int zero)
-{
- if ((state->ulong_max - state->used_memory) < size)
- return 0;
-
- if (state->settings.max_memory
- && (state->used_memory += size) > state->settings.max_memory)
- {
- return 0;
- }
-
- return state->settings.mem_alloc (size, zero, state->settings.user_data);
-}
-
-static int new_value (json_state * state,
- json_value ** top, json_value ** root, json_value ** alloc,
- json_type type)
-{
- json_value * value;
- int values_size;
-
- if (!state->first_pass)
- {
- value = *top = *alloc;
- *alloc = (*alloc)->_reserved.next_alloc;
-
- if (!*root)
- *root = value;
-
- switch (value->type)
- {
- case json_array:
-
- if (value->u.array.length == 0)
- break;
-
- if (! (value->u.array.values = (json_value **) json_alloc
- (state, value->u.array.length * sizeof (json_value *), 0)) )
- {
- return 0;
- }
-
- value->u.array.length = 0;
- break;
-
- case json_object:
-
- if (value->u.object.length == 0)
- break;
-
- values_size = sizeof (*value->u.object.values) * value->u.object.length;
-
- if (! (value->u.object.values = (json_object_entry *) json_alloc
- (state, values_size + ((unsigned long) value->u.object.values), 0)) )
- {
- return 0;
- }
-
- value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size;
-
- value->u.object.length = 0;
- break;
-
- case json_string:
-
- if (! (value->u.string.ptr = (json_char *) json_alloc
- (state, (value->u.string.length + 1) * sizeof (json_char), 0)) )
- {
- return 0;
- }
-
- value->u.string.length = 0;
- break;
-
- default:
- break;
- };
-
- return 1;
- }
-
- if (! (value = (json_value *) json_alloc
- (state, sizeof (json_value) + state->settings.value_extra, 1)))
- {
- return 0;
- }
-
- if (!*root)
- *root = value;
-
- value->type = type;
- value->parent = *top;
-
- #ifdef JSON_TRACK_SOURCE
- value->line = state->cur_line;
- value->col = state->cur_col;
- #endif
-
- if (*alloc)
- (*alloc)->_reserved.next_alloc = value;
-
- *alloc = *top = value;
-
- return 1;
-}
-
-#define whitespace \
- case '\n': ++ state.cur_line; state.cur_col = 0; \
- case ' ': case '\t': case '\r'
-
-#define string_add(b) \
- do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0);
-
-#define line_and_col \
- state.cur_line, state.cur_col
-
-static const long
- flag_next = 1 << 0,
- flag_reproc = 1 << 1,
- flag_need_comma = 1 << 2,
- flag_seek_value = 1 << 3,
- flag_escaped = 1 << 4,
- flag_string = 1 << 5,
- flag_need_colon = 1 << 6,
- flag_done = 1 << 7,
- flag_num_negative = 1 << 8,
- flag_num_zero = 1 << 9,
- flag_num_e = 1 << 10,
- flag_num_e_got_sign = 1 << 11,
- flag_num_e_negative = 1 << 12,
- flag_line_comment = 1 << 13,
- flag_block_comment = 1 << 14;
-
-json_value * json_parse_ex (json_settings * settings,
- const json_char * json,
- size_t length,
- char * error_buf)
-{
- json_char error [json_error_max];
- const json_char * end;
- json_value * top, * root, * alloc = 0;
- json_state state = { 0 };
- long flags;
- long num_digits = 0, num_e = 0;
- json_int_t num_fraction = 0;
-
- /* Skip UTF-8 BOM
- */
- if (length >= 3 && ((unsigned char) json [0]) == 0xEF
- && ((unsigned char) json [1]) == 0xBB
- && ((unsigned char) json [2]) == 0xBF)
- {
- json += 3;
- length -= 3;
- }
-
- error[0] = '\0';
- end = (json + length);
-
- memcpy (&state.settings, settings, sizeof (json_settings));
-
- if (!state.settings.mem_alloc)
- state.settings.mem_alloc = default_alloc;
-
- if (!state.settings.mem_free)
- state.settings.mem_free = default_free;
-
- memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
- memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
-
- state.uint_max -= 8; /* limit of how much can be added before next check */
- state.ulong_max -= 8;
-
- for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
- {
- json_uchar uchar;
- unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
- json_char * string = 0;
- unsigned int string_length = 0;
-
- top = root = 0;
- flags = flag_seek_value;
-
- state.cur_line = 1;
-
- for (state.ptr = json ;; ++ state.ptr)
- {
- json_char b = (state.ptr == end ? 0 : *state.ptr);
-
- if (flags & flag_string)
- {
- if (!b)
- { sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col);
- goto e_failed;
- }
-
- if (string_length > state.uint_max)
- goto e_overflow;
-
- if (flags & flag_escaped)
- {
- flags &= ~ flag_escaped;
-
- switch (b)
- {
- case 'b': string_add ('\b'); break;
- case 'f': string_add ('\f'); break;
- case 'n': string_add ('\n'); break;
- case 'r': string_add ('\r'); break;
- case 't': string_add ('\t'); break;
- case 'u':
-
- if (end - state.ptr < 4 ||
- (uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
- {
- sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
- goto e_failed;
- }
-
- uc_b1 = (uc_b1 << 4) | uc_b2;
- uc_b2 = (uc_b3 << 4) | uc_b4;
- uchar = (uc_b1 << 8) | uc_b2;
-
- if ((uchar & 0xF800) == 0xD800) {
- json_uchar uchar2;
-
- if (end - state.ptr < 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' ||
- (uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
- (uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
- {
- sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
- goto e_failed;
- }
-
- uc_b1 = (uc_b1 << 4) | uc_b2;
- uc_b2 = (uc_b3 << 4) | uc_b4;
- uchar2 = (uc_b1 << 8) | uc_b2;
-
- uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF);
- }
-
- if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F))
- {
- string_add ((json_char) uchar);
- break;
- }
-
- if (uchar <= 0x7FF)
- {
- if (state.first_pass)
- string_length += 2;
- else
- { string [string_length ++] = 0xC0 | (uchar >> 6);
- string [string_length ++] = 0x80 | (uchar & 0x3F);
- }
-
- break;
- }
-
- if (uchar <= 0xFFFF) {
- if (state.first_pass)
- string_length += 3;
- else
- { string [string_length ++] = 0xE0 | (uchar >> 12);
- string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
- string [string_length ++] = 0x80 | (uchar & 0x3F);
- }
-
- break;
- }
-
- if (state.first_pass)
- string_length += 4;
- else
- { string [string_length ++] = 0xF0 | (uchar >> 18);
- string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F);
- string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
- string [string_length ++] = 0x80 | (uchar & 0x3F);
- }
-
- break;
-
- default:
- string_add (b);
- };
-
- continue;
- }
-
- if (b == '\\')
- {
- flags |= flag_escaped;
- continue;
- }
-
- if (b == '"')
- {
- if (!state.first_pass)
- string [string_length] = 0;
-
- flags &= ~ flag_string;
- string = 0;
-
- switch (top->type)
- {
- case json_string:
-
- top->u.string.length = string_length;
- flags |= flag_next;
-
- break;
-
- case json_object:
-
- if (state.first_pass)
- (*(json_char **) &top->u.object.values) += string_length + 1;
- else
- {
- top->u.object.values [top->u.object.length].name
- = (json_char *) top->_reserved.object_mem;
-
- top->u.object.values [top->u.object.length].name_length
- = string_length;
-
- (*(json_char **) &top->_reserved.object_mem) += string_length + 1;
- }
-
- flags |= flag_seek_value | flag_need_colon;
- continue;
-
- default:
- break;
- };
- }
- else
- {
- string_add (b);
- continue;
- }
- }
-
- if (state.settings.settings & json_enable_comments)
- {
- if (flags & (flag_line_comment | flag_block_comment))
- {
- if (flags & flag_line_comment)
- {
- if (b == '\r' || b == '\n' || !b)
- {
- flags &= ~ flag_line_comment;
- -- state.ptr; /* so null can be reproc'd */
- }
-
- continue;
- }
-
- if (flags & flag_block_comment)
- {
- if (!b)
- { sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col);
- goto e_failed;
- }
-
- if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/')
- {
- flags &= ~ flag_block_comment;
- ++ state.ptr; /* skip closing sequence */
- }
-
- continue;
- }
- }
- else if (b == '/')
- {
- if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object)
- { sprintf (error, "%d:%d: Comment not allowed here", line_and_col);
- goto e_failed;
- }
-
- if (++ state.ptr == end)
- { sprintf (error, "%d:%d: EOF unexpected", line_and_col);
- goto e_failed;
- }
-
- switch (b = *state.ptr)
- {
- case '/':
- flags |= flag_line_comment;
- continue;
-
- case '*':
- flags |= flag_block_comment;
- continue;
-
- default:
- sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b);
- goto e_failed;
- };
- }
- }
-
- if (flags & flag_done)
- {
- if (!b)
- break;
-
- switch (b)
- {
- whitespace:
- continue;
-
- default:
-
- sprintf (error, "%d:%d: Trailing garbage: `%c`",
- state.cur_line, state.cur_col, b);
-
- goto e_failed;
- };
- }
-
- if (flags & flag_seek_value)
- {
- switch (b)
- {
- whitespace:
- continue;
-
- case ']':
-
- if (top && top->type == json_array)
- flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
- else
- { sprintf (error, "%d:%d: Unexpected ]", line_and_col);
- goto e_failed;
- }
-
- break;
-
- default:
-
- if (flags & flag_need_comma)
- {
- if (b == ',')
- { flags &= ~ flag_need_comma;
- continue;
- }
- else
- {
- sprintf (error, "%d:%d: Expected , before %c",
- state.cur_line, state.cur_col, b);
-
- goto e_failed;
- }
- }
-
- if (flags & flag_need_colon)
- {
- if (b == ':')
- { flags &= ~ flag_need_colon;
- continue;
- }
- else
- {
- sprintf (error, "%d:%d: Expected : before %c",
- state.cur_line, state.cur_col, b);
-
- goto e_failed;
- }
- }
-
- flags &= ~ flag_seek_value;
-
- switch (b)
- {
- case '{':
-
- if (!new_value (&state, &top, &root, &alloc, json_object))
- goto e_alloc_failure;
-
- continue;
-
- case '[':
-
- if (!new_value (&state, &top, &root, &alloc, json_array))
- goto e_alloc_failure;
-
- flags |= flag_seek_value;
- continue;
-
- case '"':
-
- if (!new_value (&state, &top, &root, &alloc, json_string))
- goto e_alloc_failure;
-
- flags |= flag_string;
-
- string = top->u.string.ptr;
- string_length = 0;
-
- continue;
-
- case 't':
-
- if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' ||
- *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e')
- {
- goto e_unknown_value;
- }
-
- if (!new_value (&state, &top, &root, &alloc, json_boolean))
- goto e_alloc_failure;
-
- top->u.boolean = 1;
-
- flags |= flag_next;
- break;
-
- case 'f':
-
- if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' ||
- *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' ||
- *(++ state.ptr) != 'e')
- {
- goto e_unknown_value;
- }
-
- if (!new_value (&state, &top, &root, &alloc, json_boolean))
- goto e_alloc_failure;
-
- flags |= flag_next;
- break;
-
- case 'n':
-
- if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' ||
- *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l')
- {
- goto e_unknown_value;
- }
-
- if (!new_value (&state, &top, &root, &alloc, json_null))
- goto e_alloc_failure;
-
- flags |= flag_next;
- break;
-
- default:
-
- if (isdigit (b) || b == '-')
- {
- if (!new_value (&state, &top, &root, &alloc, json_integer))
- goto e_alloc_failure;
-
- if (!state.first_pass)
- {
- while (isdigit (b) || b == '+' || b == '-'
- || b == 'e' || b == 'E' || b == '.')
- {
- if ( (++ state.ptr) == end)
- {
- b = 0;
- break;
- }
-
- b = *state.ptr;
- }
-
- flags |= flag_next | flag_reproc;
- break;
- }
-
- flags &= ~ (flag_num_negative | flag_num_e |
- flag_num_e_got_sign | flag_num_e_negative |
- flag_num_zero);
-
- num_digits = 0;
- num_fraction = 0;
- num_e = 0;
-
- if (b != '-')
- {
- flags |= flag_reproc;
- break;
- }
-
- flags |= flag_num_negative;
- continue;
- }
- else
- { sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b);
- goto e_failed;
- }
- };
- };
- }
- else
- {
- switch (top->type)
- {
- case json_object:
-
- switch (b)
- {
- whitespace:
- continue;
-
- case '"':
-
- if (flags & flag_need_comma)
- { sprintf (error, "%d:%d: Expected , before \"", line_and_col);
- goto e_failed;
- }
-
- flags |= flag_string;
-
- string = (json_char *) top->_reserved.object_mem;
- string_length = 0;
-
- break;
-
- case '}':
-
- flags = (flags & ~ flag_need_comma) | flag_next;
- break;
-
- case ',':
-
- if (flags & flag_need_comma)
- {
- flags &= ~ flag_need_comma;
- break;
- }
-
- default:
- sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b);
- goto e_failed;
- };
-
- break;
-
- case json_integer:
- case json_double:
-
- if (isdigit (b))
- {
- ++ num_digits;
-
- if (top->type == json_integer || flags & flag_num_e)
- {
- if (! (flags & flag_num_e))
- {
- if (flags & flag_num_zero)
- { sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b);
- goto e_failed;
- }
-
- if (num_digits == 1 && b == '0')
- flags |= flag_num_zero;
- }
- else
- {
- flags |= flag_num_e_got_sign;
- num_e = (num_e * 10) + (b - '0');
- continue;
- }
-
- top->u.integer = (top->u.integer * 10) + (b - '0');
- continue;
- }
-
- num_fraction = (num_fraction * 10) + (b - '0');
- continue;
- }
-
- if (b == '+' || b == '-')
- {
- if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign))
- {
- flags |= flag_num_e_got_sign;
-
- if (b == '-')
- flags |= flag_num_e_negative;
-
- continue;
- }
- }
- else if (b == '.' && top->type == json_integer)
- {
- if (!num_digits)
- { sprintf (error, "%d:%d: Expected digit before `.`", line_and_col);
- goto e_failed;
- }
-
- top->type = json_double;
- top->u.dbl = (double) top->u.integer;
-
- num_digits = 0;
- continue;
- }
-
- if (! (flags & flag_num_e))
- {
- if (top->type == json_double)
- {
- if (!num_digits)
- { sprintf (error, "%d:%d: Expected digit after `.`", line_and_col);
- goto e_failed;
- }
-
- top->u.dbl += ((double) num_fraction) / (pow (10.0, (double) num_digits));
- }
-
- if (b == 'e' || b == 'E')
- {
- flags |= flag_num_e;
-
- if (top->type == json_integer)
- {
- top->type = json_double;
- top->u.dbl = (double) top->u.integer;
- }
-
- num_digits = 0;
- flags &= ~ flag_num_zero;
-
- continue;
- }
- }
- else
- {
- if (!num_digits)
- { sprintf (error, "%d:%d: Expected digit after `e`", line_and_col);
- goto e_failed;
- }
-
- top->u.dbl *= pow (10.0, (double)
- (flags & flag_num_e_negative ? - num_e : num_e));
- }
-
- if (flags & flag_num_negative)
- {
- if (top->type == json_integer)
- top->u.integer = - top->u.integer;
- else
- top->u.dbl = - top->u.dbl;
- }
-
- flags |= flag_next | flag_reproc;
- break;
-
- default:
- break;
- };
- }
-
- if (flags & flag_reproc)
- {
- flags &= ~ flag_reproc;
- -- state.ptr;
- }
-
- if (flags & flag_next)
- {
- flags = (flags & ~ flag_next) | flag_need_comma;
-
- if (!top->parent)
- {
- /* root value done */
-
- flags |= flag_done;
- continue;
- }
-
- if (top->parent->type == json_array)
- flags |= flag_seek_value;
-
- if (!state.first_pass)
- {
- json_value * parent = top->parent;
-
- switch (parent->type)
- {
- case json_object:
-
- parent->u.object.values
- [parent->u.object.length].value = top;
-
- break;
-
- case json_array:
-
- parent->u.array.values
- [parent->u.array.length] = top;
-
- break;
-
- default:
- break;
- };
- }
-
- if ( (++ top->parent->u.array.length) > state.uint_max)
- goto e_overflow;
-
- top = top->parent;
-
- continue;
- }
- }
-
- alloc = root;
- }
-
- return root;
-
-e_unknown_value:
-
- sprintf (error, "%d:%d: Unknown value", line_and_col);
- goto e_failed;
-
-e_alloc_failure:
-
- strcpy (error, "Memory allocation failure");
- goto e_failed;
-
-e_overflow:
-
- sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col);
- goto e_failed;
-
-e_failed:
-
- if (error_buf)
- {
- if (*error)
- strcpy (error_buf, error);
- else
- strcpy (error_buf, "Unknown error");
- }
-
- if (state.first_pass)
- alloc = root;
-
- while (alloc)
- {
- top = alloc->_reserved.next_alloc;
- state.settings.mem_free (alloc, state.settings.user_data);
- alloc = top;
- }
-
- if (!state.first_pass)
- json_value_free_ex (&state.settings, root);
-
- return 0;
-}
-
-json_value * json_parse (const json_char * json, size_t length)
-{
- json_settings settings = { 0 };
- return json_parse_ex (&settings, json, length, 0);
-}
-
-void json_value_free_ex (json_settings * settings, json_value * value)
-{
- json_value * cur_value;
-
- if (!value)
- return;
-
- value->parent = 0;
-
- while (value)
- {
- switch (value->type)
- {
- case json_array:
-
- if (!value->u.array.length)
- {
- settings->mem_free (value->u.array.values, settings->user_data);
- break;
- }
-
- value = value->u.array.values [-- value->u.array.length];
- continue;
-
- case json_object:
-
- if (!value->u.object.length)
- {
- settings->mem_free (value->u.object.values, settings->user_data);
- break;
- }
-
- value = value->u.object.values [-- value->u.object.length].value;
- continue;
-
- case json_string:
-
- settings->mem_free (value->u.string.ptr, settings->user_data);
- break;
-
- default:
- break;
- };
-
- cur_value = value;
- value = value->parent;
- settings->mem_free (cur_value, settings->user_data);
- }
-}
-
-void json_value_free (json_value * value)
-{
- json_settings settings = { 0 };
- settings.mem_free = default_free;
- json_value_free_ex (&settings, value);
-}
-
diff --git a/ext/json-parser/json.h b/ext/json-parser/json.h
deleted file mode 100644
index f6549ec4..00000000
--- a/ext/json-parser/json.h
+++ /dev/null
@@ -1,283 +0,0 @@
-
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must 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 AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, 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.
- */
-
-#ifndef _JSON_H
-#define _JSON_H
-
-#ifndef json_char
- #define json_char char
-#endif
-
-#ifndef json_int_t
- #ifndef _MSC_VER
- #include <inttypes.h>
- #define json_int_t int64_t
- #else
- #define json_int_t __int64
- #endif
-#endif
-
-#include <stdlib.h>
-
-#ifdef __cplusplus
-
- #include <string.h>
-
- extern "C"
- {
-
-#endif
-
-typedef struct
-{
- unsigned long max_memory;
- int settings;
-
- /* Custom allocator support (leave null to use malloc/free)
- */
-
- void * (* mem_alloc) (size_t, int zero, void * user_data);
- void (* mem_free) (void *, void * user_data);
-
- void * user_data; /* will be passed to mem_alloc and mem_free */
-
- size_t value_extra; /* how much extra space to allocate for values? */
-
-} json_settings;
-
-#define json_enable_comments 0x01
-
-typedef enum
-{
- json_none,
- json_object,
- json_array,
- json_integer,
- json_double,
- json_string,
- json_boolean,
- json_null
-
-} json_type;
-
-extern const struct _json_value json_value_none;
-
-typedef struct _json_object_entry
-{
- json_char * name;
- unsigned int name_length;
-
- struct _json_value * value;
-
-} json_object_entry;
-
-typedef struct _json_value
-{
- struct _json_value * parent;
-
- json_type type;
-
- union
- {
- int boolean;
- json_int_t integer;
- double dbl;
-
- struct
- {
- unsigned int length;
- json_char * ptr; /* null terminated */
-
- } string;
-
- struct
- {
- unsigned int length;
-
- json_object_entry * values;
-
- #if defined(__cplusplus) && __cplusplus >= 201103L
- decltype(values) begin () const
- { return values;
- }
- decltype(values) end () const
- { return values + length;
- }
- #endif
-
- } object;
-
- struct
- {
- unsigned int length;
- struct _json_value ** values;
-
- #if defined(__cplusplus) && __cplusplus >= 201103L
- decltype(values) begin () const
- { return values;
- }
- decltype(values) end () const
- { return values + length;
- }
- #endif
-
- } array;
-
- } u;
-
- union
- {
- struct _json_value * next_alloc;
- void * object_mem;
-
- } _reserved;
-
- #ifdef JSON_TRACK_SOURCE
-
- /* Location of the value in the source JSON
- */
- unsigned int line, col;
-
- #endif
-
-
- /* Some C++ operator sugar */
-
- #ifdef __cplusplus
-
- public:
-
- inline _json_value ()
- { memset (this, 0, sizeof (_json_value));
- }
-
- inline const struct _json_value &operator [] (int index) const
- {
- if (type != json_array || index < 0
- || ((unsigned int) index) >= u.array.length)
- {
- return json_value_none;
- }
-
- return *u.array.values [index];
- }
-
- inline const struct _json_value &operator [] (const char * index) const
- {
- if (type != json_object)
- return json_value_none;
-
- for (unsigned int i = 0; i < u.object.length; ++ i)
- if (!strcmp (u.object.values [i].name, index))
- return *u.object.values [i].value;
-
- return json_value_none;
- }
-
- inline operator const char * () const
- {
- switch (type)
- {
- case json_string:
- return u.string.ptr;
-
- default:
- return "";
- };
- }
-
- inline operator json_int_t () const
- {
- switch (type)
- {
- case json_integer:
- return u.integer;
-
- case json_double:
- return (json_int_t) u.dbl;
-
- default:
- return 0;
- };
- }
-
- inline operator bool () const
- {
- if (type != json_boolean)
- return false;
-
- return u.boolean != 0;
- }
-
- inline operator double () const
- {
- switch (type)
- {
- case json_integer:
- return (double) u.integer;
-
- case json_double:
- return u.dbl;
-
- default:
- return 0;
- };
- }
-
- #endif
-
-} json_value;
-
-json_value * json_parse (const json_char * json,
- size_t length);
-
-#define json_error_max 128
-json_value * json_parse_ex (json_settings * settings,
- const json_char * json,
- size_t length,
- char * error);
-
-void json_value_free (json_value *);
-
-
-/* Not usually necessary, unless you used a custom mem_alloc and now want to
- * use a custom mem_free.
- */
-void json_value_free_ex (json_settings * settings,
- json_value *);
-
-
-#ifdef __cplusplus
- } /* extern "C" */
-#endif
-
-#endif
-
-
diff --git a/ext/json/LICENSE.MIT b/ext/json/LICENSE.MIT
index e2ac4891..00599afe 100644
--- a/ext/json/LICENSE.MIT
+++ b/ext/json/LICENSE.MIT
@@ -1,14 +1,13 @@
-The library is licensed under the MIT License
-<http://opensource.org/licenses/MIT>:
+MIT License
-Copyright (c) 2013-2016 Niels Lohmann
+Copyright (c) 2013-2017 Niels Lohmann
-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:
+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.
diff --git a/ext/json/README.md b/ext/json/README.md
index cb05d744..256409e3 100644
--- a/ext/json/README.md
+++ b/ext/json/README.md
@@ -1,30 +1,53 @@
-![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)
+[![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)](https://github.com/nlohmann/json/releases)
[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
-[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
+[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG)
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=nlohmann/json&amp;utm_campaign=Badge_Grade)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
[![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/nlohmann/json.svg)](http://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue")
+[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289)
+
+- [Design goals](#design-goals)
+- [Integration](#integration)
+- [Examples](#examples)
+ - [JSON as first-class data type](#json-as-first-class-data-type)
+ - [Serialization / Deserialization](#serialization--deserialization)
+ - [STL-like access](#stl-like-access)
+ - [Conversion from STL containers](#conversion-from-stl-containers)
+ - [JSON Pointer and JSON Patch](#json-pointer-and-json-patch)
+ - [Implicit conversions](#implicit-conversions)
+ - [Conversions to/from arbitrary types](#arbitrary-types-conversions)
+ - [Binary formats (CBOR and MessagePack)](#binary-formats-cbor-and-messagepack)
+- [Supported compilers](#supported-compilers)
+- [License](#license)
+- [Contact](#contact)
+- [Thanks](#thanks)
+- [Used third-party tools](#used-third-party-tools)
+- [Projects using JSON for Modern C++](#projects-using-json-for-modern-c)
+- [Notes](#notes)
+- [Execute unit tests](#execute-unit-tests)
## Design goals
There are myriads of [JSON](http://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals:
-- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you know, what I mean.
+- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean.
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
-- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks.
+- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
Other aspects were not so important to us:
- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs.
-- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) with a decent regular expression processor should be even faster (but would consist of more files which makes the integration harder).
+- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set.
See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information.
@@ -44,9 +67,21 @@ to the files you want to use JSON objects. That's it. Do not forget to set the n
:beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`.
+If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repo as a subproject.
+
+If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `jsonformoderncpp/x.y.z@vthiery/stable` to your `conanfile.py`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/vthiery/conan-jsonformoderncpp/issues) if you experience problems with the packages.
+
+If you are using [hunter](https://github.com/ruslo/hunter/) on your project for external dependencies, then you can use the [nlohmann_json package](https://docs.hunter.sh/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging.
+
+If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json). Please see the vcpkg project for any issues regarding the packaging.
+
## Examples
+Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a602f275f0359ab181221384989810604.html#a602f275f0359ab181221384989810604)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).
+
+### JSON as first-class data type
+
Here are some examples to give you an idea how to use the class.
Assume you want to create the JSON object
@@ -123,12 +158,14 @@ json empty_object_implicit = json({});
json empty_object_explicit = json::object();
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
-json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
+json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });
```
### Serialization / Deserialization
+#### To/from strings
+
You can create an object (deserialization) by appending `_json` to a string literal:
```cpp
@@ -142,8 +179,14 @@ auto j2 = R"(
"pi": 3.141
}
)"_json;
+```
+
+Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object.
-// or explicitly
+The above example can also be expressed explicitly using `json::parse()`:
+
+```cpp
+// parse explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
```
@@ -162,6 +205,31 @@ std::cout << j.dump(4) << std::endl;
// }
```
+Note the difference between serialization and assignment:
+
+```cpp
+// store a string in a JSON value
+json j_string = "this is a string";
+
+// retrieve the string value (implicit JSON to std::string conversion)
+std::string cpp_string = j_string;
+// retrieve the string value (explicit JSON to std::string conversion)
+auto cpp_string2 = j_string.get<std::string>();
+
+// retrieve the serialized value (explicit JSON serialization)
+std::string serialized_string = j_string.dump();
+
+// output of original string
+std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::string>() << '\n';
+// output of serialized value
+std::cout << j_string << " == " << serialized_string << std::endl;
+```
+
+`.dump()` always returns the serialized value, and `.get<std::string>()` returns the originally stored string value.
+
+
+#### To/from streams (e.g. files, string streams)
+
You can also use streams to serialize and deserialize:
```cpp
@@ -176,10 +244,37 @@ std::cout << j;
std::cout << std::setw(4) << j << std::endl;
```
-These operators work for any subclasses of `std::istream` or `std::ostream`.
+These operators work for any subclasses of `std::istream` or `std::ostream`. Here is the same example with files:
+
+```cpp
+// read a JSON file
+std::ifstream i("file.json");
+json j;
+i >> j;
+
+// write prettified JSON to another file
+std::ofstream o("pretty.json");
+o << std::setw(4) << j << std::endl;
+```
Please note that setting the exception bit for `failbit` is inappropriate for this use case. It will result in program termination due to the `noexcept` specifier in use.
+#### Read from iterator range
+
+You can also read JSON from an iterator range; that is, from any container accessible by iterators whose content is stored as contiguous byte sequence, for instance a `std::vector<std::uint8_t>`:
+
+```cpp
+std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
+json j = json::parse(v.begin(), v.end());
+```
+
+You may leave the iterators for the range [begin, end):
+
+```cpp
+std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
+json j = json::parse(v);
+```
+
### STL-like access
@@ -192,6 +287,9 @@ j.push_back("foo");
j.push_back(1);
j.push_back(true);
+// also use emplace_back
+j.emplace_back(1.78);
+
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << '\n';
@@ -207,6 +305,9 @@ const std::string tmp = j[0];
j[1] = 42;
bool foo = j.at(2);
+// comparison
+j == "[\"foo\", 1, true]"_json; // true
+
// other stuff
j.size(); // 3 entries
j.empty(); // false
@@ -221,15 +322,15 @@ j.is_object();
j.is_array();
j.is_string();
-// comparison
-j == "[\"foo\", 1, true]"_json; // true
-
// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;
+// also use emplace
+o.emplace("weather", "sunny");
+
// special iterator member functions for objects
for (json::iterator it = o.begin(); it != o.end(); ++it) {
std::cout << it.key() << " : " << it.value() << "\n";
@@ -251,7 +352,7 @@ o.erase("foo");
### Conversion from STL containers
-Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends how the elements are ordered in the respective STL container.
+Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container.
```cpp
std::vector<int> c_vector {1, 2, 3, 4};
@@ -283,15 +384,15 @@ json j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
-json j_mset(c_mset); // only one entry for "one" is used
-// maybe ["one", "two", "four"]
+json j_mset(c_mset); // both entries for "one" are used
+// maybe ["one", "two", "one", "four"]
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
```
-Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys are can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
+Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
```cpp
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
@@ -323,7 +424,7 @@ json j_original = R"({
})"_json;
// access members with a JSON pointer (RFC 6901)
-j_original["/baz/2"_json_pointer];
+j_original["/baz/1"_json_pointer];
// "two"
// a JSON patch (RFC 6902)
@@ -344,8 +445,8 @@ json j_result = j_original.patch(j_patch);
json::diff(j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
-// { "op":"remove","path":"/hello" },
-// { "op":"add","path":"/foo","value":"bar" }
+// { "op": "remove","path": "/hello" },
+// { "op": "add", "path": "/foo", "value": "bar" }
// ]
```
@@ -383,14 +484,266 @@ int vi = jn.get<int>();
// etc.
```
+### Arbitrary types conversions
+
+Every type can be serialized in JSON, not just STL-containers and scalar types. Usually, you would do something along those lines:
+
+```cpp
+namespace ns {
+ // a simple struct to model a person
+ struct person {
+ std::string name;
+ std::string address;
+ int age;
+ };
+}
+
+ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};
+
+// convert to JSON: copy each value into the JSON object
+json j;
+j["name"] = p.name;
+j["address"] = p.address;
+j["age"] = p.age;
+
+// ...
+
+// convert from JSON: copy each value from the JSON object
+ns::person p {
+ j["name"].get<std::string>(),
+ j["address"].get<std::string>(),
+ j["age"].get<int>()
+};
+```
+
+It works, but that's quite a lot of boilerplate... Fortunately, there's a better way:
+
+```cpp
+// create a person
+ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};
+
+// conversion: person -> json
+json j = p;
+
+std::cout << j << std::endl;
+// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}
+
+// conversion: json -> person
+ns::person p2 = j;
+
+// that's it
+assert(p == p2);
+```
+
+#### Basic usage
+
+To make this work with one of your types, you only need to provide two functions:
+
+```cpp
+using nlohmann::json;
+
+namespace ns {
+ void to_json(json& j, const person& p) {
+ j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
+ }
+
+ void from_json(const json& j, person& p) {
+ p.name = j.at("name").get<std::string>();
+ p.address = j.at("address").get<std::string>();
+ p.age = j.at("age").get<int>();
+ }
+} // namespace ns
+```
+
+That's all! When calling the `json` constructor with your type, your custom `to_json` method will be automatically called.
+Likewise, when calling `get<your_type>()`, the `from_json` method will be called.
+
+Some important things:
+
+* Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined).
+* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). (There is a way to bypass this requirement described later.)
+* In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
+* In case your type contains several `operator=` definitions, code like `your_variable = your_json;` [may not compile](https://github.com/nlohmann/json/issues/667). You need to write `your_variable = your_json.get<decltype your_variable>();` instead.
+* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
+* Be careful with the definition order of the `from_json`/`to_json` functions: If a type `B` has a member of type `A`, you **MUST** define `to_json(A)` before `to_json(B)`. Look at [issue 561](https://github.com/nlohmann/json/issues/561) for more details.
+
+
+#### How do I convert third-party types?
+
+This requires a bit more advanced technique. But first, let's see how this conversion mechanism works:
+
+The library uses **JSON Serializers** to convert types to json.
+The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](http://en.cppreference.com/w/cpp/language/adl)).
+
+It is implemented like this (simplified):
+
+```cpp
+template <typename T>
+struct adl_serializer {
+ static void to_json(json& j, const T& value) {
+ // calls the "to_json" method in T's namespace
+ }
+
+ static void from_json(const json& j, T& value) {
+ // same thing, but with the "from_json" method
+ }
+};
+```
+
+This serializer works fine when you have control over the type's namespace. However, what about `boost::optional`, or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`...
+
+To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example:
+
+```cpp
+// partial specialization (full specialization works too)
+namespace nlohmann {
+ template <typename T>
+ struct adl_serializer<boost::optional<T>> {
+ static void to_json(json& j, const boost::optional<T>& opt) {
+ if (opt == boost::none) {
+ j = nullptr;
+ } else {
+ j = *opt; // this will call adl_serializer<T>::to_json which will
+ // find the free function to_json in T's namespace!
+ }
+ }
+
+ static void from_json(const json& j, boost::optional<T>& opt) {
+ if (j.is_null()) {
+ opt = boost::none;
+ } else {
+ opt = j.get<T>(); // same as above, but with
+ // adl_serializer<T>::from_json
+ }
+ }
+ };
+}
+```
+
+#### How can I use `get()` for non-default constructible/non-copyable types?
+
+There is a way, if your type is [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload:
+
+```cpp
+struct move_only_type {
+ move_only_type() = delete;
+ move_only_type(int ii): i(ii) {}
+ move_only_type(const move_only_type&) = delete;
+ move_only_type(move_only_type&&) = default;
+
+ int i;
+};
+
+namespace nlohmann {
+ template <>
+ struct adl_serializer<move_only_type> {
+ // note: the return type is no longer 'void', and the method only takes
+ // one argument
+ static move_only_type from_json(const json& j) {
+ return {j.get<int>()};
+ }
+
+ // Here's the catch! You must provide a to_json method! Otherwise you
+ // will not be able to convert move_only_type to json, since you fully
+ // specialized adl_serializer on that type
+ static void to_json(json& j, move_only_type t) {
+ j = t.i;
+ }
+ };
+}
+```
+
+#### Can I write my own serializer? (Advanced use)
+
+Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/test/src/unit-udt.cpp) in the test suite, to see a few examples.
+
+If you write your own serializer, you'll need to do a few things:
+
+* use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`)
+* use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods
+* use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL
+
+Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL.
+
+```cpp
+// You should use void as a second template argument
+// if you don't need compile-time checks on T
+template<typename T, typename SFINAE = typename std::enable_if<sizeof(T) <= 32>::type>
+struct less_than_32_serializer {
+ template <typename BasicJsonType>
+ static void to_json(BasicJsonType& j, T value) {
+ // we want to use ADL, and call the correct to_json overload
+ using nlohmann::to_json; // this method is called by adl_serializer,
+ // this is where the magic happens
+ to_json(j, value);
+ }
+
+ template <typename BasicJsonType>
+ static void from_json(const BasicJsonType& j, T& value) {
+ // same thing here
+ using nlohmann::from_json;
+ from_json(j, value);
+ }
+};
+```
+
+Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention:
+
+```cpp
+template <typename T, void>
+struct bad_serializer
+{
+ template <typename BasicJsonType>
+ static void to_json(BasicJsonType& j, const T& value) {
+ // this calls BasicJsonType::json_serializer<T>::to_json(j, value);
+ // if BasicJsonType::json_serializer == bad_serializer ... oops!
+ j = value;
+ }
+
+ template <typename BasicJsonType>
+ static void to_json(const BasicJsonType& j, T& value) {
+ // this calls BasicJsonType::json_serializer<T>::from_json(j, value);
+ // if BasicJsonType::json_serializer == bad_serializer ... oops!
+ value = j.template get<T>(); // oops!
+ }
+};
+```
+
+### Binary formats (CBOR and MessagePack)
+
+Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [CBOR](http://cbor.io) (Concise Binary Object Representation) and [MessagePack](http://msgpack.org) to efficiently encode JSON values to byte vectors and to decode such vectors.
+
+```cpp
+// create a JSON value
+json j = R"({"compact": true, "schema": 0})"_json;
+
+// serialize to CBOR
+std::vector<std::uint8_t> v_cbor = json::to_cbor(j);
+
+// 0xa2, 0x67, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xf5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00
+
+// roundtrip
+json j_from_cbor = json::from_cbor(v_cbor);
+
+// serialize to MessagePack
+std::vector<std::uint8_t> v_msgpack = json::to_msgpack(j);
+
+// 0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00
+
+// roundtrip
+json j_from_msgpack = json::from_msgpack(v_msgpack);
+```
+
## Supported compilers
-Though it's 2016 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
+Though it's 2017 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
-- GCC 4.9 - 6.0 (and possibly later)
-- Clang 3.4 - 3.9 (and possibly later)
-- Microsoft Visual C++ 2015 / 14.0 (and possibly later)
+- GCC 4.9 - 7.2 (and possibly later)
+- Clang 3.4 - 5.0 (and possibly later)
+- Intel C++ Compiler 17.0.2 (and possibly later)
+- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
+- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
I would be happy to learn about other compilers/versions.
@@ -413,20 +766,28 @@ The following compilers are currently used in continuous integration at [Travis]
| Compiler | Operating System | Version String |
|-----------------|------------------------------|----------------|
-| GCC 4.9.3 | Ubuntu 14.04.4 LTS | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 |
-| GCC 5.3.0 | Ubuntu 14.04.4 LTS | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 |
-| GCC 6.1.1 | Ubuntu 14.04.4 LTS | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 |
-| Clang 3.8.0 | Ubuntu 14.04.4 LTS | clang version 3.8.0 (tags/RELEASE_380/final) |
-| Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) |
-| Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) |
-| Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) |
+| GCC 4.9.4 | Ubuntu 14.04.5 LTS | g++-4.9 (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4 |
+| GCC 5.4.1 | Ubuntu 14.04.5 LTS | g++-5 (Ubuntu 5.4.1-2ubuntu1~14.04) 5.4.1 20160904 |
+| GCC 6.3.0 | Ubuntu 14.04.5 LTS | g++-6 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04) 6.3.0 20170519 |
+| GCC 7.1.0 | Ubuntu 14.04.5 LTS | g++-7 (Ubuntu 7.1.0-5ubuntu2~14.04) 7.1.0
+| Clang 3.5.0 | Ubuntu 14.04.5 LTS | clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) |
+| Clang 3.6.2 | Ubuntu 14.04.5 LTS | clang version 3.6.2-svn240577-1~exp1 (branches/release_36) |
+| Clang 3.7.1 | Ubuntu 14.04.5 LTS | clang version 3.7.1-svn253571-1~exp1 (branches/release_37) |
+| Clang 3.8.0 | Ubuntu 14.04.5 LTS | clang version 3.8.0-2ubuntu3~trusty5 (tags/RELEASE_380/final) |
+| Clang 3.9.1 | Ubuntu 14.04.5 LTS | clang version 3.9.1-4ubuntu3~14.04.2 (tags/RELEASE_391/rc2) |
+| Clang 4.0.1 | Ubuntu 14.04.5 LTS | clang version 4.0.1-svn305264-1~exp1 (branches/release_40) |
+| Clang 5.0.0 | Ubuntu 14.04.5 LTS | clang version 5.0.0-svn310902-1~exp1 (branches/release_50) |
| Clang Xcode 6.4 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) |
-| Clang Xcode 7.1 | Darwin Kernel Version 14.5.0 (OSX 10.10.5) | Apple LLVM version 7.0.0 (clang-700.1.76) |
-| Clang Xcode 7.2 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.0.2 (clang-700.1.81) |
| Clang Xcode 7.3 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.3.0 (clang-703.0.29) |
-| Clang Xcode 8.0 | Darwin Kernel Version 15.5.0 (OSX 10.11.5) | Apple LLVM version 8.0.0 (clang-800.0.24.1) |
-| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 |
-
+| Clang Xcode 8.0 | Darwin Kernel Version 15.6.0 | Apple LLVM version 8.0.0 (clang-800.0.38) |
+| Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
+| Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
+| Clang Xcode 8.3 | Darwin Kernel Version 16.5.0 (macOS 10.12.4) | Apple LLVM version 8.1.0 (clang-802.0.38) |
+| Clang Xcode 9.0 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.37) |
+| Clang Xcode 9.1 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.38) |
+| Clang Xcode 9.2 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 8.1.0 (clang-900.0.39.2) |
+| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 |
+| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.5.180.51428, MSVC 19.12.25830.2 |
## License
@@ -434,7 +795,7 @@ The following compilers are currently used in continuous integration at [Travis]
The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
-Copyright &copy; 2013-2016 [Niels Lohmann](http://nlohmann.me)
+Copyright &copy; 2013-2017 [Niels Lohmann](http://nlohmann.me)
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:
@@ -442,11 +803,23 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+* * *
+
+The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
+
+## Contact
+
+If you have questions regarding the library, I would like to invite you to [open an issue at Github](https://github.com/nlohmann/json/issues/new). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at Github allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases.
+
+Only if your request would contain confidential information, please [send me an email](mailto:mail@nlohmann.me). For encrypted messages, please use [this key](https://keybase.io/nlohmann/pgp_keys.asc).
+
## Thanks
I deeply appreciate the help of the following people.
+![Contributors](https://raw.githubusercontent.com/nlohmann/json/develop/doc/avatars.png)
+
- [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization.
- [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes.
- [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries.
@@ -459,7 +832,7 @@ I deeply appreciate the help of the following people.
- [Eric Cornelius](https://github.com/EricMCornelius) pointed out a bug in the handling with NaN and infinity values. He also improved the performance of the string escaping.
- [易思龙](https://github.com/likebeta) implemented a conversion from anonymous enums.
- [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual studio.
-- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements.
+- [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types.
- [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling.
- [dariomt](https://github.com/dariomt) fixed some typos in the examples.
- [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation.
@@ -483,14 +856,117 @@ I deeply appreciate the help of the following people.
- [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake.
- [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files.
- [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal).
-
-Thanks a lot for helping out!
+- [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
+- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
+- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
+- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
+- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types.
+- [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
+- [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
+- [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
+- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix.
+- [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
+- [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
+- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
+- [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan.
+- [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning.
+- [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
+- [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one.
+- [Daniel599](https://github.com/Daniel599) helped to get Travis execute the tests with Clang's sanitizers.
+- [Jonathan Lee](https://github.com/vjon) fixed an example in the README file.
+- [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types.
+- [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio.
+- [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types.
+- [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
+- [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
+- [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section.
+- [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README.
+- [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
+- [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
+- [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings.
+- [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible.
+- [ftillier](https://github.com/ftillier) fixed a compiler warning.
+- [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped.
+- [Fytch](https://github.com/Fytch) found a bug in the documentation.
+- [Jay Sistar](https://github.com/Type1J) implemented a Meson build description.
+- [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation.
+- [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager.
+- [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`.
+- [Mike Tzou](https://github.com/Chocobo1) fixed some typos.
+- [amrcode](https://github.com/amrcode) noted a missleading documentation about comparison of floats.
+- [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`.
+- [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library.
+- [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists.
+- [Greg Hurrell](https://github.com/wincent) fixed a typo.
+- [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo.
+- [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler.
+- [Markus Werle](https://github.com/daixtrose) fixed a typo.
+- [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check.
+- [Alex](https://github.com/leha-bot) noted an error in a code sample.
+- [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped fixing them.
+- [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams.
+- [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error.
+- [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings.
+- [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file.
+- [pvleuven](https://github.com/pvleuven) helped fixing a warning in ICC.
+- [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC.
+- [Jamie Seward](https://github.com/jseward) avoided unneccessary string copies in `find()` and `count()`.
+- [Mitja](https://github.com/Itja) fixed some typos.
+- [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links.
+- [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view.
+- [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
+- [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
+
+
+Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
+
+
+## Used third-party tools
+
+The library itself contains of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot!
+
+- [**American fuzzy lop**](http://lcamtuf.coredump.cx/afl/) for fuzz testing
+- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
+- [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code identation
+- [**benchpress**](https://github.com/sbs-ableton/benchpress) to benchmark the code
+- [**Catch**](https://github.com/philsquared/Catch) for the unit tests
+- [**Clang**](http://clang.llvm.org) for compilation with code sanitizers
+- [**Cmake**](https://cmake.org) for build automation
+- [**Codacity**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json)
+- [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
+- [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
+- [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
+- [**cxxopts**](https://github.com/jarro2783/cxxopts) to let benchpress parse command-line parameters
+- [**Doxygen**](http://www.stack.nl/~dimitri/doxygen/) to generate [documentation](https://nlohmann.github.io/json/)
+- [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages
+- [**Github Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
+- [**libFuzzer**](http://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
+- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library
+- [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
+- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox)
+- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
+- [**Valgrind**](http://valgrind.org) to check for correct memory management
+- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
+
+
+## Projects using JSON for Modern C++
+
+The library is currently used in Apple macOS Sierra and iOS 10. I am not sure what they are using the library for, but I am happy that it runs on so many devices.
## Notes
-- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert).
+- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726).
- As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
+- The library supports **Unicode input** as follows:
+ - Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1).
+ - Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse or serialization errors.
+ - [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
+ - Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
+ - The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
+- The code can be compiled without C++ **runtime type identification** features; that is, you can use the `-fno-rtti` compiler flag.
+- **Exceptions** are used widely within the library. They can, however, be switched off with either using the compiler flag `-fno-exceptions` or by defining the symbol `JSON_NOEXCEPTION`. In this case, exceptions are replaced by an `abort()` call.
+- By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc7159.html) defines objects as "an unordered collection of zero or more name/value pairs". If you do want to preserve the insertion order, you can specialize the object type with containers like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
## Execute unit tests
@@ -498,11 +974,11 @@ Thanks a lot for helping out!
To compile and run the tests, you need to execute
```sh
-$ make
-$ ./json_unit "*"
-
-===============================================================================
-All tests passed (5568721 assertions in 32 test cases)
+$ mkdir build
+$ cd build
+$ cmake ..
+$ cmake --build .
+$ ctest --output-on-failure
```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/ext/json/json.hpp b/ext/json/json.hpp
index 9d6687dd..5b0b0ea5 100644
--- a/ext/json/json.hpp
+++ b/ext/json/json.hpp
@@ -1,11 +1,11 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.0.0
+| | |__ | | | | | | version 3.0.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
-Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
+Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -29,29 +29,44 @@ SOFTWARE.
#ifndef NLOHMANN_JSON_HPP
#define NLOHMANN_JSON_HPP
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cerrno>
-#include <ciso646>
-#include <cmath>
-#include <cstddef>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-#include <initializer_list>
-#include <iomanip>
-#include <iostream>
-#include <iterator>
-#include <limits>
-#include <map>
-#include <memory>
-#include <sstream>
-#include <stdexcept>
-#include <string>
-#include <type_traits>
-#include <utility>
-#include <vector>
+#include <algorithm> // all_of, copy, fill, find, for_each, generate_n, none_of, remove, reverse, transform
+#include <array> // array
+#include <cassert> // assert
+#include <ciso646> // and, not, or
+#include <clocale> // lconv, localeconv
+#include <cmath> // isfinite, labs, ldexp, signbit
+#include <cstddef> // nullptr_t, ptrdiff_t, size_t
+#include <cstdint> // int64_t, uint64_t
+#include <cstdlib> // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull
+#include <cstring> // memcpy, strlen
+#include <forward_list> // forward_list
+#include <functional> // function, hash, less
+#include <initializer_list> // initializer_list
+#include <iomanip> // hex
+#include <iosfwd> // istream, ostream
+#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
+#include <limits> // numeric_limits
+#include <locale> // locale
+#include <map> // map
+#include <memory> // addressof, allocator, allocator_traits, unique_ptr
+#include <numeric> // accumulate
+#include <sstream> // stringstream
+#include <string> // getline, stoi, string, to_string
+#include <type_traits> // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type
+#include <utility> // declval, forward, make_pair, move, pair, swap
+#include <valarray> // valarray
+#include <vector> // vector
+
+// exclude unsupported compilers
+#if defined(__clang__)
+ #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
+ #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+ #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
+ #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+#endif
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
@@ -59,6 +74,49 @@ SOFTWARE.
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
+// disable documentation warnings on clang
+#if defined(__clang__)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdocumentation"
+#endif
+
+// allow for portable deprecation warnings
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+ #define JSON_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+ #define JSON_DEPRECATED __declspec(deprecated)
+#else
+ #define JSON_DEPRECATED
+#endif
+
+// allow to disable exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION)
+ #define JSON_THROW(exception) throw exception
+ #define JSON_TRY try
+ #define JSON_CATCH(exception) catch(exception)
+#else
+ #define JSON_THROW(exception) std::abort()
+ #define JSON_TRY if(true)
+ #define JSON_CATCH(exception) if(false)
+#endif
+
+// manual branch prediction
+#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
+ #define JSON_LIKELY(x) __builtin_expect(!!(x), 1)
+ #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+ #define JSON_LIKELY(x) x
+ #define JSON_UNLIKELY(x) x
+#endif
+
+// C++ language standard detection
+#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+ #define JSON_HAS_CPP_14
+#endif
+
/*!
@brief namespace for Niels Lohmann
@see https://github.com/nlohmann
@@ -66,43 +124,7163 @@ SOFTWARE.
*/
namespace nlohmann
{
+template<typename = void, typename = void>
+struct adl_serializer;
+
+// forward declaration of basic_json (required to split the class)
+template<template<typename, typename, typename...> class ObjectType = std::map,
+ template<typename, typename...> class ArrayType = std::vector,
+ class StringType = std::string, class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename> class AllocatorType = std::allocator,
+ template<typename, typename = void> class JSONSerializer = adl_serializer>
+class basic_json;
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
+ template<template<typename, typename, typename...> class ObjectType, \
+ template<typename, typename...> class ArrayType, \
+ class StringType, class BooleanType, class NumberIntegerType, \
+ class NumberUnsignedType, class NumberFloatType, \
+ template<typename> class AllocatorType, \
+ template<typename, typename = void> class JSONSerializer>
+
+#define NLOHMANN_BASIC_JSON_TPL \
+ basic_json<ObjectType, ArrayType, StringType, BooleanType, \
+ NumberIntegerType, NumberUnsignedType, NumberFloatType, \
+ AllocatorType, JSONSerializer>
/*!
@brief unnamed namespace with internal helper functions
+
+This namespace collects some functions that could not be defined inside the
+@ref basic_json class.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+////////////////
+// exceptions //
+////////////////
+
+/*!
+@brief general exception of the @ref basic_json class
+
+This class is an extension of `std::exception` objects with a member @a id for
+exception ids. It is used as the base class for all exceptions thrown by the
+@ref basic_json class. This class can hence be used as "wildcard" to catch
+exceptions.
+
+Subclasses:
+- @ref parse_error for exceptions indicating a parse error
+- @ref invalid_iterator for exceptions indicating errors with iterators
+- @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+- @ref out_of_range for exceptions indicating access out of the defined range
+- @ref other_error for exceptions indicating other library errors
+
+@internal
+@note To have nothrow-copy-constructible exceptions, we internally use
+ `std::runtime_error` which can cope with arbitrary-length error messages.
+ Intermediate strings are built with static functions and then passed to
+ the actual constructor.
+@endinternal
+
+@liveexample{The following code shows how arbitrary library exceptions can be
+caught.,exception}
+
+@since version 3.0.0
+*/
+class exception : public std::exception
+{
+ public:
+ /// returns the explanatory string
+ const char* what() const noexcept override
+ {
+ return m.what();
+ }
+
+ /// the id of the exception
+ const int id;
+
+ protected:
+ exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
+
+ static std::string name(const std::string& ename, int id_)
+ {
+ return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
+ }
+
+ private:
+ /// an exception object as storage for error messages
+ std::runtime_error m;
+};
+
+/*!
+@brief exception indicating a parse error
+
+This exception is thrown by the library when a parse error occurs. Parse errors
+can occur during the deserialization of JSON text, CBOR, MessagePack, as well
+as when using JSON Patch.
+
+Member @a byte holds the byte index of the last read character in the input
+file.
+
+Exceptions have ids 1xx.
+
+name / id | example message | description
+------------------------------ | --------------- | -------------------------
+json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
+json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
+json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
+json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
+json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
+json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
+json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
+json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
+json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
+json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
+json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
+json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
+
+@note For an input with n bytes, 1 is the index of the first character and n+1
+ is the index of the terminating null byte or the end of file. This also
+ holds true when reading a byte vector (CBOR or MessagePack).
+
+@liveexample{The following code shows how a `parse_error` exception can be
+caught.,parse_error}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class parse_error : public exception
+{
+ public:
+ /*!
+ @brief create a parse error exception
+ @param[in] id_ the id of the exception
+ @param[in] byte_ the byte index where the error occurred (or 0 if the
+ position cannot be determined)
+ @param[in] what_arg the explanatory string
+ @return parse_error object
+ */
+ static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
+ {
+ std::string w = exception::name("parse_error", id_) + "parse error" +
+ (byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
+ ": " + what_arg;
+ return parse_error(id_, byte_, w.c_str());
+ }
+
+ /*!
+ @brief byte index of the parse error
+
+ The byte index of the last read character in the input file.
+
+ @note For an input with n bytes, 1 is the index of the first character and
+ n+1 is the index of the terminating null byte or the end of file.
+ This also holds true when reading a byte vector (CBOR or MessagePack).
+ */
+ const std::size_t byte;
+
+ private:
+ parse_error(int id_, std::size_t byte_, const char* what_arg)
+ : exception(id_, what_arg), byte(byte_) {}
+};
+
+/*!
+@brief exception indicating errors with iterators
+
+This exception is thrown if iterators passed to a library function do not match
+the expected semantics.
+
+Exceptions have ids 2xx.
+
+name / id | example message | description
+----------------------------------- | --------------- | -------------------------
+json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
+json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
+json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
+json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
+json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
+json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
+json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
+json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
+json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
+json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
+
+@liveexample{The following code shows how an `invalid_iterator` exception can be
+caught.,invalid_iterator}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class invalid_iterator : public exception
+{
+ public:
+ static invalid_iterator create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("invalid_iterator", id_) + what_arg;
+ return invalid_iterator(id_, w.c_str());
+ }
+
+ private:
+ invalid_iterator(int id_, const char* what_arg)
+ : exception(id_, what_arg) {}
+};
+
+/*!
+@brief exception indicating executing a member function with a wrong type
+
+This exception is thrown in case of a type error; that is, a library function is
+executed on a JSON value whose type does not match the expected semantics.
+
+Exceptions have ids 3xx.
+
+name / id | example message | description
+----------------------------- | --------------- | -------------------------
+json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
+json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
+json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.
+json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
+json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
+json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
+json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
+json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
+json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
+json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
+json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
+json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
+json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
+json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
+json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
+json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
+
+@liveexample{The following code shows how a `type_error` exception can be
+caught.,type_error}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class type_error : public exception
+{
+ public:
+ static type_error create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("type_error", id_) + what_arg;
+ return type_error(id_, w.c_str());
+ }
+
+ private:
+ type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/*!
+@brief exception indicating access out of the defined range
+
+This exception is thrown in case a library function is called on an input
+parameter that exceeds the expected range, for instance in case of array
+indices or nonexisting object keys.
+
+Exceptions have ids 4xx.
+
+name / id | example message | description
+------------------------------- | --------------- | -------------------------
+json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
+json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
+json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
+json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
+json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
+json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
+
+@liveexample{The following code shows how an `out_of_range` exception can be
+caught.,out_of_range}
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref other_error for exceptions indicating other library errors
+
+@since version 3.0.0
+*/
+class out_of_range : public exception
+{
+ public:
+ static out_of_range create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("out_of_range", id_) + what_arg;
+ return out_of_range(id_, w.c_str());
+ }
+
+ private:
+ out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/*!
+@brief exception indicating other library errors
+
+This exception is thrown in case of errors that cannot be classified with the
+other exception types.
+
+Exceptions have ids 5xx.
+
+name / id | example message | description
+------------------------------ | --------------- | -------------------------
+json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
+
+@sa @ref exception for the base class of the library exceptions
+@sa @ref parse_error for exceptions indicating a parse error
+@sa @ref invalid_iterator for exceptions indicating errors with iterators
+@sa @ref type_error for exceptions indicating executing a member function with
+ a wrong type
+@sa @ref out_of_range for exceptions indicating access out of the defined range
+
+@liveexample{The following code shows how an `other_error` exception can be
+caught.,other_error}
+
+@since version 3.0.0
+*/
+class other_error : public exception
+{
+ public:
+ static other_error create(int id_, const std::string& what_arg)
+ {
+ std::string w = exception::name("other_error", id_) + what_arg;
+ return other_error(id_, w.c_str());
+ }
+
+ private:
+ other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+
+
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
@since version 1.0.0
*/
-namespace
+enum class value_t : uint8_t
{
+ null, ///< null value
+ object, ///< object (unordered set of name/value pairs)
+ array, ///< array (ordered collection of values)
+ string, ///< string value
+ boolean, ///< boolean value
+ number_integer, ///< number value (signed integer)
+ number_unsigned, ///< number value (unsigned integer)
+ number_float, ///< number value (floating-point)
+ discarded ///< discarded by the the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+
+@since version 1.0.0
+*/
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+ static constexpr std::array<uint8_t, 8> order = {{
+ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+ 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
+ }
+ };
+
+ const auto l_index = static_cast<std::size_t>(lhs);
+ const auto r_index = static_cast<std::size_t>(rhs);
+ return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
+}
+
+
+/////////////
+// helpers //
+/////////////
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+// implementation of C++14 index_sequence and affiliates
+// source: https://stackoverflow.com/a/32223343
+template<std::size_t... Ints>
+struct index_sequence
+{
+ using type = index_sequence;
+ using value_type = std::size_t;
+ static constexpr std::size_t size() noexcept
+ {
+ return sizeof...(Ints);
+ }
+};
+
+template<class Sequence1, class Sequence2>
+struct merge_and_renumber;
+
+template<std::size_t... I1, std::size_t... I2>
+struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
+ : index_sequence < I1..., (sizeof...(I1) + I2)... > {};
+
+template<std::size_t N>
+struct make_index_sequence
+ : merge_and_renumber < typename make_index_sequence < N / 2 >::type,
+ typename make_index_sequence < N - N / 2 >::type > {};
+
+template<> struct make_index_sequence<0> : index_sequence<> {};
+template<> struct make_index_sequence<1> : index_sequence<0> {};
+
+template<typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+/*
+Implementation of two C++17 constructs: conjunction, negation. This is needed
+to avoid evaluating all the traits in a condition
+
+For example: not std::is_same<void, T>::value and has_value_type<T>::value
+will not compile when T = void (on MSVC at least). Whereas
+conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
+stop evaluating if negation<...>::value == false
+
+Please note that those constructs must be used with caution, since symbols can
+become very long quickly (which can slow down compilation and cause MSVC
+internal compiler errors). Only use it when you have to (see example ahead).
+*/
+template<class...> struct conjunction : std::true_type {};
+template<class B1> struct conjunction<B1> : B1 {};
+template<class B1, class... Bn>
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
+
+template<class B> struct negation : std::integral_constant<bool, not B::value> {};
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+
+//////////////////
+// constructors //
+//////////////////
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+ {
+ j.m_type = value_t::boolean;
+ j.m_value = b;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+ {
+ j.m_type = value_t::string;
+ j.m_value = s;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+ {
+ j.m_type = value_t::string;
+ j.m_value = std::move(s);
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+ {
+ j.m_type = value_t::number_float;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+ {
+ j.m_type = value_t::number_unsigned;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+ {
+ j.m_type = value_t::number_integer;
+ j.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = arr;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = std::move(arr);
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+ int> = 0>
+ static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+ {
+ using std::begin;
+ using std::end;
+ j.m_type = value_t::array;
+ j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = value_t::array;
+ j.m_value.array->reserve(arr.size());
+ for (const bool x : arr)
+ {
+ j.m_value.array->push_back(x);
+ }
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+ static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+ {
+ j.m_type = value_t::array;
+ j.m_value = value_t::array;
+ j.m_value.array->resize(arr.size());
+ std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+ {
+ j.m_type = value_t::object;
+ j.m_value = obj;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+ {
+ j.m_type = value_t::object;
+ j.m_value = std::move(obj);
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
+ static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+ {
+ using std::begin;
+ using std::end;
+
+ j.m_type = value_t::object;
+ j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+ j.assert_invariant();
+ }
+};
+
+
+////////////////////////
+// has_/is_ functions //
+////////////////////////
+
/*!
@brief Helper to determine whether there's a key_type for T.
+
+This helper is used to tell associative containers apart from other containers
+such as sequence containers. For instance, `std::map` passes the test as it
+contains a `mapped_type`, whereas `std::vector` fails the test.
+
@sa http://stackoverflow.com/a/7728728/266378
+@since version 1.0.0, overworked in version 2.0.6
*/
+#define NLOHMANN_JSON_HAS_HELPER(type) \
+ template<typename T> struct has_##type { \
+ private: \
+ template<typename U, typename = typename U::type> \
+ static int detect(U &&); \
+ static void detect(...); \
+ public: \
+ static constexpr bool value = \
+ std::is_integral<decltype(detect(std::declval<T>()))>::value; \
+ }
+
+NLOHMANN_JSON_HAS_HELPER(mapped_type);
+NLOHMANN_JSON_HAS_HELPER(key_type);
+NLOHMANN_JSON_HAS_HELPER(value_type);
+NLOHMANN_JSON_HAS_HELPER(iterator);
+
+#undef NLOHMANN_JSON_HAS_HELPER
+
+
+template<bool B, class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<class RealType, class CompatibleObjectType>
+struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
+{
+ static constexpr auto value =
+ std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and
+ std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<class BasicJsonType, class CompatibleObjectType>
+struct is_compatible_object_type
+{
+ static auto constexpr value = is_compatible_object_type_impl <
+ conjunction<negation<std::is_same<void, CompatibleObjectType>>,
+ has_mapped_type<CompatibleObjectType>,
+ has_key_type<CompatibleObjectType>>::value,
+ typename BasicJsonType::object_t, CompatibleObjectType >::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct is_basic_json_nested_type
+{
+ static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
+ std::is_same<T, typename BasicJsonType::const_iterator>::value or
+ std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
+ std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
+};
+
+template<class BasicJsonType, class CompatibleArrayType>
+struct is_compatible_array_type
+{
+ static auto constexpr value =
+ conjunction<negation<std::is_same<void, CompatibleArrayType>>,
+ negation<is_compatible_object_type<
+ BasicJsonType, CompatibleArrayType>>,
+ negation<std::is_constructible<typename BasicJsonType::string_t,
+ CompatibleArrayType>>,
+ negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
+ has_value_type<CompatibleArrayType>,
+ has_iterator<CompatibleArrayType>>::value;
+};
+
+template<bool, typename, typename>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
+{
+ // is there an assert somewhere on overflows?
+ using RealLimits = std::numeric_limits<RealIntegerType>;
+ using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+ static constexpr auto value =
+ std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
+ CompatibleLimits::is_integer and
+ RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+{
+ static constexpr auto value =
+ is_compatible_integer_type_impl <
+ std::is_integral<CompatibleNumberIntegerType>::value and
+ not std::is_same<bool, CompatibleNumberIntegerType>::value,
+ RealIntegerType, CompatibleNumberIntegerType >::value;
+};
+
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T>
+struct has_from_json
+{
+ private:
+ // also check the return type of from_json
+ template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
+ std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(
+ detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json
+{
+ private:
+ template<typename U, typename =
+ enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value>>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(detect(
+ std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+template<typename BasicJsonType, typename T>
+struct has_to_json
+{
+ private:
+ template<typename U, typename = decltype(uncvref_t<U>::to_json(
+ std::declval<BasicJsonType&>(), std::declval<T>()))>
+ static int detect(U&&);
+ static void detect(...);
+
+ public:
+ static constexpr bool value = std::is_integral<decltype(detect(
+ std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
+};
+
+
+/////////////
+// to_json //
+/////////////
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+void to_json(BasicJsonType& j, T b) noexcept
+{
+ external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template<typename BasicJsonType, typename CompatibleString,
+ enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
+void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+ external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+{
+ external_constructor<value_t::string>::construct(j, std::move(s));
+}
+
+template<typename BasicJsonType, typename FloatType,
+ enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+ external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
+void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+ external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberIntegerType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
+void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+ external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+void to_json(BasicJsonType& j, EnumType e) noexcept
+{
+ using underlying_type = typename std::underlying_type<EnumType>::type;
+ external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+ external_constructor<value_t::array>::construct(j, e);
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
+ std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
+ int> = 0>
+void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+void to_json(BasicJsonType& j, std::valarray<T> arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+ external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+ external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template<typename BasicJsonType, typename T, std::size_t N,
+ enable_if_t<not std::is_constructible<typename BasicJsonType::string_t, T (&)[N]>::value, int> = 0>
+void to_json(BasicJsonType& j, T (&arr)[N])
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType, typename... Args>
+void to_json(BasicJsonType& j, const std::pair<Args...>& p)
+{
+ j = {p.first, p.second};
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...>)
+{
+ j = {std::get<Idx>(t)...};
+}
+
+template<typename BasicJsonType, typename... Args>
+void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
+{
+ to_json_tuple_impl(j, t, index_sequence_for<Args...> {});
+}
+
+///////////////
+// from_json //
+///////////////
+
+// overloads for basic_json template parameters
+template<typename BasicJsonType, typename ArithmeticType,
+ enable_if_t<std::is_arithmetic<ArithmeticType>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int> = 0>
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+
+ default:
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ }
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+ if (JSON_UNLIKELY(not j.is_boolean()))
+ {
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
+ }
+ b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+ if (JSON_UNLIKELY(not j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
+ }
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+void from_json(const BasicJsonType& j, EnumType& e)
+{
+ typename std::underlying_type<EnumType>::type val;
+ get_arithmetic_value(j, val);
+ e = static_cast<EnumType>(val);
+}
+
+template<typename BasicJsonType>
+void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+ enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ std::transform(j.rbegin(), j.rend(),
+ std::front_inserter(l), [](const BasicJsonType & i)
+ {
+ return i.template get<T>();
+ });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
+void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+ l.resize(j.size());
+ std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
+{
+ using std::end;
+
+ std::transform(j.begin(), j.end(),
+ std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename CompatibleArrayType::value_type>();
+ });
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+ arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
+ void())
+{
+ using std::end;
+
+ arr.reserve(j.size());
+ std::transform(j.begin(), j.end(),
+ std::inserter(arr, end(arr)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename CompatibleArrayType::value_type>();
+ });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
+ std::is_convertible<BasicJsonType, typename CompatibleArrayType::value_type>::value and
+ not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
+{
+ if (JSON_UNLIKELY(not j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
+ }
+
+ from_json_array_impl(j, arr, priority_tag<2> {});
+}
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
+void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
+{
+ if (JSON_UNLIKELY(not j.is_object()))
+ {
+ JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
+ }
+
+ auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+ using value_type = typename CompatibleObjectType::value_type;
+ std::transform(
+ inner_object->begin(), inner_object->end(),
+ std::inserter(obj, obj.begin()),
+ [](typename BasicJsonType::object_t::value_type const & p)
+ {
+ return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>());
+ });
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template<typename BasicJsonType, typename ArithmeticType,
+ enable_if_t <
+ std::is_arithmetic<ArithmeticType>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
+ not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int> = 0>
+void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+ case value_t::boolean:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+ break;
+ }
+
+ default:
+ JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
+ }
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
+{
+ p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>)
+{
+ t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
+}
+
+template<typename BasicJsonType, typename... Args>
+void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
+{
+ from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
+}
+
+struct to_json_fn
+{
+ private:
+ template<typename BasicJsonType, typename T>
+ auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+ -> decltype(to_json(j, std::forward<T>(val)), void())
+ {
+ return to_json(j, std::forward<T>(val));
+ }
+
+ template<typename BasicJsonType, typename T>
+ void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept
+ {
+ static_assert(sizeof(BasicJsonType) == 0,
+ "could not find to_json() method in T's namespace");
+
+#ifdef _MSC_VER
+ // MSVC does not show a stacktrace for the above assert
+ using decayed = uncvref_t<T>;
+ static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
+ "forcing MSVC stacktrace to show which T we're talking about.");
+#endif
+ }
+
+ public:
+ template<typename BasicJsonType, typename T>
+ void operator()(BasicJsonType& j, T&& val) const
+ noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
+ {
+ return call(j, std::forward<T>(val), priority_tag<1> {});
+ }
+};
+
+struct from_json_fn
+{
+ private:
+ template<typename BasicJsonType, typename T>
+ auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
+ noexcept(noexcept(from_json(j, val)))
+ -> decltype(from_json(j, val), void())
+ {
+ return from_json(j, val);
+ }
+
+ template<typename BasicJsonType, typename T>
+ void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
+ {
+ static_assert(sizeof(BasicJsonType) == 0,
+ "could not find from_json() method in T's namespace");
+#ifdef _MSC_VER
+ // MSVC does not show a stacktrace for the above assert
+ using decayed = uncvref_t<T>;
+ static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
+ "forcing MSVC stacktrace to show which T we're talking about.");
+#endif
+ }
+
+ public:
+ template<typename BasicJsonType, typename T>
+ void operator()(const BasicJsonType& j, T& val) const
+ noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
+ {
+ return call(j, val, priority_tag<1> {});
+ }
+};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+ static constexpr T value{};
+};
+
template<typename T>
-struct has_mapped_type
+constexpr T static_const<T>::value;
+
+////////////////////
+// input adapters //
+////////////////////
+
+/*!
+@brief abstract input adapter interface
+
+Produces a stream of std::char_traits<char>::int_type characters from a
+std::istream, a buffer, or some other input type. Accepts the return of exactly
+one non-EOF character for future input. The int_type characters returned
+consist of all valid char values as positive values (typically unsigned char),
+plus an EOF value outside that range, specified by the value of the function
+std::char_traits<char>::eof(). This value is typically -1, but could be any
+arbitrary value which is not a valid char value.
+*/
+struct input_adapter_protocol
+{
+ /// get a character [0,255] or std::char_traits<char>::eof().
+ virtual std::char_traits<char>::int_type get_character() = 0;
+ /// restore the last non-eof() character to input
+ virtual void unget_character() = 0;
+ virtual ~input_adapter_protocol() = default;
+};
+
+/// a type to simplify interfaces
+using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input. Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter : public input_adapter_protocol
+{
+ public:
+ ~input_stream_adapter() override
+ {
+ // clear stream flags; we use underlying streambuf I/O, do not
+ // maintain ifstream flags
+ is.clear();
+ }
+
+ explicit input_stream_adapter(std::istream& i)
+ : is(i), sb(*i.rdbuf())
+ {
+ // skip byte order mark
+ std::char_traits<char>::int_type c;
+ if ((c = get_character()) == 0xEF)
+ {
+ if ((c = get_character()) == 0xBB)
+ {
+ if ((c = get_character()) == 0xBF)
+ {
+ return; // Ignore BOM
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ is.unget();
+ }
+ is.putback('\xBB');
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ is.unget();
+ }
+ is.putback('\xEF');
+ }
+ else if (c != std::char_traits<char>::eof())
+ {
+ is.unget(); // no byte order mark; process as usual
+ }
+ }
+
+ // delete because of pointer members
+ input_stream_adapter(const input_stream_adapter&) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&) = delete;
+
+ // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+ // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+ // end up as the same value, eg. 0xFFFFFFFF.
+ std::char_traits<char>::int_type get_character() override
+ {
+ return sb.sbumpc();
+ }
+
+ void unget_character() override
+ {
+ sb.sungetc(); // is.unget() avoided for performance
+ }
+
+ private:
+ /// the associated input stream
+ std::istream& is;
+ std::streambuf& sb;
+};
+
+/// input adapter for buffer input
+class input_buffer_adapter : public input_adapter_protocol
{
+ public:
+ input_buffer_adapter(const char* b, const std::size_t l)
+ : cursor(b), limit(b + l), start(b)
+ {
+ // skip byte order mark
+ if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF')
+ {
+ cursor += 3;
+ }
+ }
+
+ // delete because of pointer members
+ input_buffer_adapter(const input_buffer_adapter&) = delete;
+ input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
+
+ std::char_traits<char>::int_type get_character() noexcept override
+ {
+ if (JSON_LIKELY(cursor < limit))
+ {
+ return std::char_traits<char>::to_int_type(*(cursor++));
+ }
+
+ return std::char_traits<char>::eof();
+ }
+
+ void unget_character() noexcept override
+ {
+ if (JSON_LIKELY(cursor > start))
+ {
+ --cursor;
+ }
+ }
+
private:
- template<typename C> static char test(typename C::mapped_type*);
- template<typename C> static char (&test(...))[2];
+ /// pointer to the current character
+ const char* cursor;
+ /// pointer past the last character
+ const char* limit;
+ /// pointer to the first character
+ const char* start;
+};
+
+class input_adapter
+{
public:
- static constexpr bool value = sizeof(test<T>(0)) == 1;
+ // native support
+
+ /// input adapter for input stream
+ input_adapter(std::istream& i)
+ : ia(std::make_shared<input_stream_adapter>(i)) {}
+
+ /// input adapter for input stream
+ input_adapter(std::istream&& i)
+ : ia(std::make_shared<input_stream_adapter>(i)) {}
+
+ /// input adapter for buffer
+ template<typename CharT,
+ typename std::enable_if<
+ std::is_pointer<CharT>::value and
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value and
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int>::type = 0>
+ input_adapter(CharT b, std::size_t l)
+ : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}
+
+ // derived support
+
+ /// input adapter for string literal
+ template<typename CharT,
+ typename std::enable_if<
+ std::is_pointer<CharT>::value and
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value and
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int>::type = 0>
+ input_adapter(CharT b)
+ : input_adapter(reinterpret_cast<const char*>(b),
+ std::strlen(reinterpret_cast<const char*>(b))) {}
+
+ /// input adapter for iterator range with contiguous storage
+ template<class IteratorType,
+ typename std::enable_if<
+ std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+ int>::type = 0>
+ input_adapter(IteratorType first, IteratorType last)
+ {
+ // assertion to check that the iterator range is indeed contiguous,
+ // see http://stackoverflow.com/a/35008842/266378 for more discussion
+ assert(std::accumulate(
+ first, last, std::pair<bool, int>(true, 0),
+ [&first](std::pair<bool, int> res, decltype(*first) val)
+ {
+ res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
+ return res;
+ }).first);
+
+ // assertion to check that each element is 1 byte long
+ static_assert(
+ sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
+ "each element in the iterator range must have the size of 1 byte");
+
+ const auto len = static_cast<size_t>(std::distance(first, last));
+ if (JSON_LIKELY(len > 0))
+ {
+ // there is at least one element: use the address of first
+ ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);
+ }
+ else
+ {
+ // the address of first cannot be used: use nullptr
+ ia = std::make_shared<input_buffer_adapter>(nullptr, len);
+ }
+ }
+
+ /// input adapter for array
+ template<class T, std::size_t N>
+ input_adapter(T (&array)[N])
+ : input_adapter(std::begin(array), std::end(array)) {}
+
+ /// input adapter for contiguous container
+ template<class ContiguousContainer, typename
+ std::enable_if<not std::is_pointer<ContiguousContainer>::value and
+ std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
+ int>::type = 0>
+ input_adapter(const ContiguousContainer& c)
+ : input_adapter(std::begin(c), std::end(c)) {}
+
+ operator input_adapter_t()
+ {
+ return ia;
+ }
+
+ private:
+ /// the actual adapter
+ input_adapter_t ia = nullptr;
};
+//////////////////////
+// lexer and parser //
+//////////////////////
+
/*!
-@brief helper class to create locales with decimal point
-@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
*/
-class DecimalSeparator : public std::numpunct<char>
+template<typename BasicJsonType>
+class lexer
{
- protected:
- char do_decimal_point() const
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+
+ public:
+ /// token types for the parser
+ enum class token_type
+ {
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the `true` literal
+ literal_false, ///< the `false` literal
+ literal_null, ///< the `null` literal
+ value_string, ///< a string -- use get_string() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
+ value_integer, ///< a signed integer -- use get_number_integer() for actual value
+ value_float, ///< an floating point number -- use get_number_float() for actual value
+ begin_array, ///< the character for array begin `[`
+ begin_object, ///< the character for object begin `{`
+ end_array, ///< the character for array end `]`
+ end_object, ///< the character for object end `}`
+ name_separator, ///< the name separator `:`
+ value_separator, ///< the value separator `,`
+ parse_error, ///< indicating a parse error
+ end_of_input, ///< indicating the end of the input buffer
+ literal_or_value ///< a literal or the begin of a value (only for diagnostics)
+ };
+
+ /// return name of values of type token_type (only used for errors)
+ static const char* token_type_name(const token_type t) noexcept
+ {
+ switch (t)
+ {
+ case token_type::uninitialized:
+ return "<uninitialized>";
+ case token_type::literal_true:
+ return "true literal";
+ case token_type::literal_false:
+ return "false literal";
+ case token_type::literal_null:
+ return "null literal";
+ case token_type::value_string:
+ return "string literal";
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
+ return "number literal";
+ case token_type::begin_array:
+ return "'['";
+ case token_type::begin_object:
+ return "'{'";
+ case token_type::end_array:
+ return "']'";
+ case token_type::end_object:
+ return "'}'";
+ case token_type::name_separator:
+ return "':'";
+ case token_type::value_separator:
+ return "','";
+ case token_type::parse_error:
+ return "<parse error>";
+ case token_type::end_of_input:
+ return "end of input";
+ case token_type::literal_or_value:
+ return "'[', '{', or a literal";
+ default: // catch non-enum values
+ return "unknown token"; // LCOV_EXCL_LINE
+ }
+ }
+
+ explicit lexer(detail::input_adapter_t adapter)
+ : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {}
+
+ // delete because of pointer members
+ lexer(const lexer&) = delete;
+ lexer& operator=(lexer&) = delete;
+
+ private:
+ /////////////////////
+ // locales
+ /////////////////////
+
+ /// return the locale-dependent decimal point
+ static char get_decimal_point() noexcept
+ {
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+ }
+
+ /////////////////////
+ // scan functions
+ /////////////////////
+
+ /*!
+ @brief get codepoint from 4 hex characters following `\u`
+
+ For input "\u c1 c2 c3 c4" the codepoint is:
+ (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+ = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+ Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+ must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+ conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+ between the ASCII value of the character and the desired integer value.
+
+ @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+ non-hex character)
+ */
+ int get_codepoint()
+ {
+ // this function only makes sense after reading `\u`
+ assert(current == 'u');
+ int codepoint = 0;
+
+ const auto factors = { 12, 8, 4, 0 };
+ for (const auto factor : factors)
+ {
+ get();
+
+ if (current >= '0' and current <= '9')
+ {
+ codepoint += ((current - 0x30) << factor);
+ }
+ else if (current >= 'A' and current <= 'F')
+ {
+ codepoint += ((current - 0x37) << factor);
+ }
+ else if (current >= 'a' and current <= 'f')
+ {
+ codepoint += ((current - 0x57) << factor);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ assert(0x0000 <= codepoint and codepoint <= 0xFFFF);
+ return codepoint;
+ }
+
+ /*!
+ @brief check if the next byte(s) are inside a given range
+
+ Adds the current byte and, for each passed range, reads a new byte and
+ checks if it is inside the range. If a violation was detected, set up an
+ error message and return false. Otherwise, return true.
+
+ @param[in] ranges list of integers; interpreted as list of pairs of
+ inclusive lower and upper bound, respectively
+
+ @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+ 1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+ @return true if and only if no range violation was detected
+ */
+ bool next_byte_in_range(std::initializer_list<int> ranges)
+ {
+ assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);
+ add(current);
+
+ for (auto range = ranges.begin(); range != ranges.end(); ++range)
+ {
+ get();
+ if (JSON_LIKELY(*range <= current and current <= *(++range)))
+ {
+ add(current);
+ }
+ else
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief scan a string literal
+
+ This function scans a string according to Sect. 7 of RFC 7159. While
+ scanning, bytes are escaped and copied into buffer yytext. Then the function
+ returns successfully, yytext is *not* null-terminated (as it may contain \0
+ bytes), and yytext.size() is the number of bytes in the string.
+
+ @return token_type::value_string if string could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note In case of errors, variable error_message contains a textual
+ description.
+ */
+ token_type scan_string()
+ {
+ // reset yytext (ignore opening quote)
+ reset();
+
+ // we entered the function by reading an open quote
+ assert(current == '\"');
+
+ while (true)
+ {
+ // get next character
+ switch (get())
+ {
+ // end of file while parsing string
+ case std::char_traits<char>::eof():
+ {
+ error_message = "invalid string: missing closing quote";
+ return token_type::parse_error;
+ }
+
+ // closing quote
+ case '\"':
+ {
+ return token_type::value_string;
+ }
+
+ // escapes
+ case '\\':
+ {
+ switch (get())
+ {
+ // quotation mark
+ case '\"':
+ add('\"');
+ break;
+ // reverse solidus
+ case '\\':
+ add('\\');
+ break;
+ // solidus
+ case '/':
+ add('/');
+ break;
+ // backspace
+ case 'b':
+ add('\b');
+ break;
+ // form feed
+ case 'f':
+ add('\f');
+ break;
+ // line feed
+ case 'n':
+ add('\n');
+ break;
+ // carriage return
+ case 'r':
+ add('\r');
+ break;
+ // tab
+ case 't':
+ add('\t');
+ break;
+
+ // unicode escapes
+ case 'u':
+ {
+ const int codepoint1 = get_codepoint();
+ int codepoint = codepoint1; // start with codepoint1
+
+ if (JSON_UNLIKELY(codepoint1 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if code point is a high surrogate
+ if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)
+ {
+ // expect next \uxxxx entry
+ if (JSON_LIKELY(get() == '\\' and get() == 'u'))
+ {
+ const int codepoint2 = get_codepoint();
+
+ if (JSON_UNLIKELY(codepoint2 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if codepoint2 is a low surrogate
+ if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))
+ {
+ // overwrite codepoint
+ codepoint =
+ // high surrogate occupies the most significant 22 bits
+ (codepoint1 << 10)
+ // low surrogate occupies the least significant 15 bits
+ + codepoint2
+ // there is still the 0xD800, 0xDC00 and 0x10000 noise
+ // in the result so we have to subtract with:
+ // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+ - 0x35FDC00;
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+ return token_type::parse_error;
+ }
+ }
+
+ // result of the above calculation yields a proper codepoint
+ assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+
+ // translate codepoint into bytes
+ if (codepoint < 0x80)
+ {
+ // 1-byte characters: 0xxxxxxx (ASCII)
+ add(codepoint);
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ // 2-byte characters: 110xxxxx 10xxxxxx
+ add(0xC0 | (codepoint >> 6));
+ add(0x80 | (codepoint & 0x3F));
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+ add(0xE0 | (codepoint >> 12));
+ add(0x80 | ((codepoint >> 6) & 0x3F));
+ add(0x80 | (codepoint & 0x3F));
+ }
+ else
+ {
+ // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ add(0xF0 | (codepoint >> 18));
+ add(0x80 | ((codepoint >> 12) & 0x3F));
+ add(0x80 | ((codepoint >> 6) & 0x3F));
+ add(0x80 | (codepoint & 0x3F));
+ }
+
+ break;
+ }
+
+ // other characters after escape
+ default:
+ error_message = "invalid string: forbidden character after backslash";
+ return token_type::parse_error;
+ }
+
+ break;
+ }
+
+ // invalid control characters
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ {
+ error_message = "invalid string: control character must be escaped";
+ return token_type::parse_error;
+ }
+
+ // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+ case 0x20:
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ {
+ add(current);
+ break;
+ }
+
+ // U+0080..U+07FF: bytes C2..DF 80..BF
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD5:
+ case 0xD6:
+ case 0xD7:
+ case 0xD8:
+ case 0xD9:
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xDE:
+ case 0xDF:
+ {
+ if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+ case 0xE0:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+ // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xEE:
+ case 0xEF:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+ case 0xED:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ case 0xF0:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ case 0xF4:
+ {
+ if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // remaining bytes (80..C1 and F5..FF) are ill-formed
+ default:
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return token_type::parse_error;
+ }
+ }
+ }
+ }
+
+ static void strtof(float& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ static void strtof(double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ static void strtof(long double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ /*!
+ @brief scan a number literal
+
+ This function scans a string according to Sect. 6 of RFC 7159.
+
+ The function is realized with a deterministic finite state machine derived
+ from the grammar described in RFC 7159. Starting in state "init", the
+ input is read and used to determined the next state. Only state "done"
+ accepts the number. State "error" is a trap state to model errors. In the
+ table below, "anything" means any character but the ones listed before.
+
+ state | 0 | 1-9 | e E | + | - | . | anything
+ ---------|----------|----------|----------|---------|---------|----------|-----------
+ init | zero | any1 | [error] | [error] | minus | [error] | [error]
+ minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
+ zero | done | done | exponent | done | done | decimal1 | done
+ any1 | any1 | any1 | exponent | done | done | decimal1 | done
+ decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error]
+ decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
+ exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
+ sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
+ any2 | any2 | any2 | done | done | done | done | done
+
+ The state machine is realized with one label per state (prefixed with
+ "scan_number_") and `goto` statements between them. The state machine
+ contains cycles, but any cycle can be left when EOF is read. Therefore,
+ the function is guaranteed to terminate.
+
+ During scanning, the read bytes are stored in yytext. This string is
+ then converted to a signed integer, an unsigned integer, or a
+ floating-point number.
+
+ @return token_type::value_unsigned, token_type::value_integer, or
+ token_type::value_float if number could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note The scanner is independent of the current locale. Internally, the
+ locale's decimal point is used instead of `.` to work with the
+ locale-dependent converters.
+ */
+ token_type scan_number()
+ {
+ // reset yytext to store the number's bytes
+ reset();
+
+ // the type of the parsed number; initially set to unsigned; will be
+ // changed if minus sign, decimal point or exponent is read
+ token_type number_type = token_type::value_unsigned;
+
+ // state (init): we just found out we need to scan a number
+ switch (current)
+ {
+ case '-':
+ {
+ add(current);
+ goto scan_number_minus;
+ }
+
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ // all other characters are rejected outside scan_number()
+ assert(false); // LCOV_EXCL_LINE
+ }
+ }
+
+scan_number_minus:
+ // state: we just parsed a leading minus sign
+ number_type = token_type::value_integer;
+ switch (get())
+ {
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '-'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_zero:
+ // state: we just parse a zero (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_any1:
+ // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ case '.':
+ {
+ add(decimal_point_char);
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_decimal1:
+ // state: we just parsed a decimal point
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '.'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_decimal2:
+ // we just parsed at least one number after a decimal point
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_exponent:
+ // we just parsed an exponent
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '+':
+ case '-':
+ {
+ add(current);
+ goto scan_number_sign;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message =
+ "invalid number; expected '+', '-', or digit after exponent";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_sign:
+ // we just parsed an exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after exponent sign";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_any2:
+ // we just parsed a number after the exponent or exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_done:
+ // unget the character after the number (we only read it to know that
+ // we are done scanning a number)
+ unget();
+
+ char* endptr = nullptr;
+ errno = 0;
+
+ // try to parse integers first and fall back to floats
+ if (number_type == token_type::value_unsigned)
+ {
+ const auto x = std::strtoull(yytext.data(), &endptr, 10);
+
+ // we checked the number format before
+ assert(endptr == yytext.data() + yytext.size());
+
+ if (errno == 0)
+ {
+ value_unsigned = static_cast<number_unsigned_t>(x);
+ if (value_unsigned == x)
+ {
+ return token_type::value_unsigned;
+ }
+ }
+ }
+ else if (number_type == token_type::value_integer)
+ {
+ const auto x = std::strtoll(yytext.data(), &endptr, 10);
+
+ // we checked the number format before
+ assert(endptr == yytext.data() + yytext.size());
+
+ if (errno == 0)
+ {
+ value_integer = static_cast<number_integer_t>(x);
+ if (value_integer == x)
+ {
+ return token_type::value_integer;
+ }
+ }
+ }
+
+ // this code is reached if we parse a floating-point number or if an
+ // integer conversion above failed
+ strtof(value_float, yytext.data(), &endptr);
+
+ // we checked the number format before
+ assert(endptr == yytext.data() + yytext.size());
+
+ return token_type::value_float;
+ }
+
+ /*!
+ @param[in] literal_text the literal text to expect
+ @param[in] length the length of the passed literal text
+ @param[in] return_type the token type to return on success
+ */
+ token_type scan_literal(const char* literal_text, const std::size_t length,
+ token_type return_type)
+ {
+ assert(current == literal_text[0]);
+ for (std::size_t i = 1; i < length; ++i)
+ {
+ if (JSON_UNLIKELY(get() != literal_text[i]))
+ {
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+ return return_type;
+ }
+
+ /////////////////////
+ // input management
+ /////////////////////
+
+ /// reset yytext; current character is beginning of token
+ void reset() noexcept
+ {
+ yytext.clear();
+ token_string.clear();
+ token_string.push_back(std::char_traits<char>::to_char_type(current));
+ }
+
+ /*
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a
+ `std::char_traits<char>::eof()` in that case. Stores the scanned characters
+ for use in error messages.
+
+ @return character read from the input
+ */
+ std::char_traits<char>::int_type get()
+ {
+ ++chars_read;
+ current = ia->get_character();
+ if (JSON_LIKELY(current != std::char_traits<char>::eof()))
+ {
+ token_string.push_back(std::char_traits<char>::to_char_type(current));
+ }
+ return current;
+ }
+
+ /// unget current character (return it again on next get)
+ void unget()
+ {
+ --chars_read;
+ if (JSON_LIKELY(current != std::char_traits<char>::eof()))
+ {
+ ia->unget_character();
+ assert(token_string.size() != 0);
+ token_string.pop_back();
+ }
+ }
+
+ /// add a character to yytext
+ void add(int c)
+ {
+ yytext.push_back(std::char_traits<char>::to_char_type(c));
+ }
+
+ public:
+ /////////////////////
+ // value getters
+ /////////////////////
+
+ /// return integer value
+ constexpr number_integer_t get_number_integer() const noexcept
+ {
+ return value_integer;
+ }
+
+ /// return unsigned integer value
+ constexpr number_unsigned_t get_number_unsigned() const noexcept
+ {
+ return value_unsigned;
+ }
+
+ /// return floating-point value
+ constexpr number_float_t get_number_float() const noexcept
+ {
+ return value_float;
+ }
+
+ /// return current string value (implicitly resets the token; useful only once)
+ std::string move_string()
+ {
+ return std::move(yytext);
+ }
+
+ /////////////////////
+ // diagnostics
+ /////////////////////
+
+ /// return position of last read token
+ constexpr std::size_t get_position() const noexcept
+ {
+ return chars_read;
+ }
+
+ /// return the last read token (for errors only). Will never contain EOF
+ /// (an arbitrary value that is not a valid char value, often -1), because
+ /// 255 may legitimately occur. May contain NUL, which should be escaped.
+ std::string get_token_string() const
+ {
+ // escape control characters
+ std::string result;
+ for (const auto c : token_string)
+ {
+ if ('\x00' <= c and c <= '\x1F')
+ {
+ // escape control characters
+ std::stringstream ss;
+ ss << "<U+" << std::setw(4) << std::uppercase << std::setfill('0')
+ << std::hex << static_cast<int>(c) << ">";
+ result += ss.str();
+ }
+ else
+ {
+ // add character as is
+ result.push_back(c);
+ }
+ }
+
+ return result;
+ }
+
+ /// return syntax error message
+ constexpr const char* get_error_message() const noexcept
+ {
+ return error_message;
+ }
+
+ /////////////////////
+ // actual scanner
+ /////////////////////
+
+ token_type scan()
+ {
+ // read next character and ignore whitespace
+ do
+ {
+ get();
+ }
+ while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
+
+ switch (current)
+ {
+ // structural characters
+ case '[':
+ return token_type::begin_array;
+ case ']':
+ return token_type::end_array;
+ case '{':
+ return token_type::begin_object;
+ case '}':
+ return token_type::end_object;
+ case ':':
+ return token_type::name_separator;
+ case ',':
+ return token_type::value_separator;
+
+ // literals
+ case 't':
+ return scan_literal("true", 4, token_type::literal_true);
+ case 'f':
+ return scan_literal("false", 5, token_type::literal_false);
+ case 'n':
+ return scan_literal("null", 4, token_type::literal_null);
+
+ // string
+ case '\"':
+ return scan_string();
+
+ // number
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scan_number();
+
+ // end of input (the null byte is needed when parsing from
+ // string literals)
+ case '\0':
+ case std::char_traits<char>::eof():
+ return token_type::end_of_input;
+
+ // error
+ default:
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+
+ private:
+ /// input adapter
+ detail::input_adapter_t ia = nullptr;
+
+ /// the current character
+ std::char_traits<char>::int_type current = std::char_traits<char>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// raw input token string (for error messages)
+ std::vector<char> token_string {};
+
+ /// buffer for variable-length tokens (numbers, strings)
+ std::string yytext {};
+
+ /// a description of occurred lexer errors
+ const char* error_message = "";
+
+ // number values
+ number_integer_t value_integer = 0;
+ number_unsigned_t value_unsigned = 0;
+ number_float_t value_float = 0;
+
+ /// the decimal point
+ const char decimal_point_char = '.';
+};
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive decent parser.
+*/
+template<typename BasicJsonType>
+class parser
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using lexer_t = lexer<BasicJsonType>;
+ using token_type = typename lexer_t::token_type;
+
+ public:
+ enum class parse_event_t : uint8_t
+ {
+ /// the parser read `{` and started to process a JSON object
+ object_start,
+ /// the parser read `}` and finished processing a JSON object
+ object_end,
+ /// the parser read `[` and started to process a JSON array
+ array_start,
+ /// the parser read `]` and finished processing a JSON array
+ array_end,
+ /// the parser read a key of a value in an object
+ key,
+ /// the parser finished reading a JSON value
+ value
+ };
+
+ using parser_callback_t =
+ std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
+
+ /// a parser reading from an input adapter
+ explicit parser(detail::input_adapter_t adapter,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions_ = true)
+ : callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_)
+ {}
+
+ /*!
+ @brief public parser interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @param[in,out] result parsed JSON value
+
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse(const bool strict, BasicJsonType& result)
+ {
+ // read first token
+ get_token();
+
+ parse_internal(true, result);
+ result.assert_invariant();
+
+ // in strict mode, input must be completely read
+ if (strict)
+ {
+ get_token();
+ expect(token_type::end_of_input);
+ }
+
+ // in case of an error, return discarded value
+ if (errored)
+ {
+ result = value_t::discarded;
+ return;
+ }
+
+ // set top-level value to null if it was discarded by the callback
+ // function
+ if (result.is_discarded())
+ {
+ result = nullptr;
+ }
+ }
+
+ /*!
+ @brief public accept interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @return whether the input is a proper JSON text
+ */
+ bool accept(const bool strict = true)
+ {
+ // read first token
+ get_token();
+
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // strict => last token must be EOF
+ return not strict or (get_token() == token_type::end_of_input);
+ }
+
+ private:
+ /*!
+ @brief the actual parser
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse_internal(bool keep, BasicJsonType& result)
+ {
+ // never parse after a parse error was detected
+ assert(not errored);
+
+ // start with a discarded value
+ if (not result.is_discarded())
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ if (keep)
+ {
+ if (callback)
+ {
+ keep = callback(depth++, parse_event_t::object_start, result);
+ }
+
+ if (not callback or keep)
+ {
+ // explicitly set result to object to cope with {}
+ result.m_type = value_t::object;
+ result.m_value = value_t::object;
+ }
+ }
+
+ // read next token
+ get_token();
+
+ // closing } -> we are done
+ if (last_token == token_type::end_object)
+ {
+ if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ // parse values
+ std::string key;
+ BasicJsonType value;
+ while (true)
+ {
+ // store key
+ if (not expect(token_type::value_string))
+ {
+ return;
+ }
+ key = m_lexer.move_string();
+
+ bool keep_tag = false;
+ if (keep)
+ {
+ if (callback)
+ {
+ BasicJsonType k(key);
+ keep_tag = callback(depth, parse_event_t::key, k);
+ }
+ else
+ {
+ keep_tag = true;
+ }
+ }
+
+ // parse separator (:)
+ get_token();
+ if (not expect(token_type::name_separator))
+ {
+ return;
+ }
+
+ // parse and add value
+ get_token();
+ value.m_value.destroy(value.m_type);
+ value.m_type = value_t::discarded;
+ parse_internal(keep, value);
+
+ if (JSON_UNLIKELY(errored))
+ {
+ return;
+ }
+
+ if (keep and keep_tag and not value.is_discarded())
+ {
+ result.m_value.object->emplace(std::move(key), std::move(value));
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing }
+ if (not expect(token_type::end_object))
+ {
+ return;
+ }
+ break;
+ }
+
+ if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ case token_type::begin_array:
+ {
+ if (keep)
+ {
+ if (callback)
+ {
+ keep = callback(depth++, parse_event_t::array_start, result);
+ }
+
+ if (not callback or keep)
+ {
+ // explicitly set result to array to cope with []
+ result.m_type = value_t::array;
+ result.m_value = value_t::array;
+ }
+ }
+
+ // read next token
+ get_token();
+
+ // closing ] -> we are done
+ if (last_token == token_type::end_array)
+ {
+ if (callback and not callback(--depth, parse_event_t::array_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ // parse values
+ BasicJsonType value;
+ while (true)
+ {
+ // parse value
+ value.m_value.destroy(value.m_type);
+ value.m_type = value_t::discarded;
+ parse_internal(keep, value);
+
+ if (JSON_UNLIKELY(errored))
+ {
+ return;
+ }
+
+ if (keep and not value.is_discarded())
+ {
+ result.m_value.array->push_back(std::move(value));
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ if (not expect(token_type::end_array))
+ {
+ return;
+ }
+ break;
+ }
+
+ if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
+ {
+ result.m_value.destroy(result.m_type);
+ result.m_type = value_t::discarded;
+ }
+ break;
+ }
+
+ case token_type::literal_null:
+ {
+ result.m_type = value_t::null;
+ break;
+ }
+
+ case token_type::value_string:
+ {
+ result.m_type = value_t::string;
+ result.m_value = m_lexer.move_string();
+ break;
+ }
+
+ case token_type::literal_true:
+ {
+ result.m_type = value_t::boolean;
+ result.m_value = true;
+ break;
+ }
+
+ case token_type::literal_false:
+ {
+ result.m_type = value_t::boolean;
+ result.m_value = false;
+ break;
+ }
+
+ case token_type::value_unsigned:
+ {
+ result.m_type = value_t::number_unsigned;
+ result.m_value = m_lexer.get_number_unsigned();
+ break;
+ }
+
+ case token_type::value_integer:
+ {
+ result.m_type = value_t::number_integer;
+ result.m_value = m_lexer.get_number_integer();
+ break;
+ }
+
+ case token_type::value_float:
+ {
+ result.m_type = value_t::number_float;
+ result.m_value = m_lexer.get_number_float();
+
+ // throw in case of infinity or NAN
+ if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float)))
+ {
+ if (allow_exceptions)
+ {
+ JSON_THROW(out_of_range::create(406, "number overflow parsing '" +
+ m_lexer.get_token_string() + "'"));
+ }
+ expect(token_type::uninitialized);
+ }
+ break;
+ }
+
+ case token_type::parse_error:
+ {
+ // using "uninitialized" to avoid "expected" message
+ if (not expect(token_type::uninitialized))
+ {
+ return;
+ }
+ break; // LCOV_EXCL_LINE
+ }
+
+ default:
+ {
+ // the last token was unexpected; we expected a value
+ if (not expect(token_type::literal_or_value))
+ {
+ return;
+ }
+ break; // LCOV_EXCL_LINE
+ }
+ }
+
+ if (keep and callback and not callback(depth, parse_event_t::value, result))
+ {
+ result.m_type = value_t::discarded;
+ }
+ }
+
+ /*!
+ @brief the actual acceptor
+
+ @invariant 1. The last token is not yet processed. Therefore, the caller
+ of this function must make sure a token has been read.
+ 2. When this function returns, the last token is processed.
+ That is, the last read character was already considered.
+
+ This invariant makes sure that no token needs to be "unput".
+ */
+ bool accept_internal()
+ {
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ // read next token
+ get_token();
+
+ // closing } -> we are done
+ if (last_token == token_type::end_object)
+ {
+ return true;
+ }
+
+ // parse values
+ while (true)
+ {
+ // parse key
+ if (last_token != token_type::value_string)
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ get_token();
+ if (last_token != token_type::name_separator)
+ {
+ return false;
+ }
+
+ // parse value
+ get_token();
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing }
+ return (last_token == token_type::end_object);
+ }
+ }
+
+ case token_type::begin_array:
+ {
+ // read next token
+ get_token();
+
+ // closing ] -> we are done
+ if (last_token == token_type::end_array)
+ {
+ return true;
+ }
+
+ // parse values
+ while (true)
+ {
+ // parse value
+ if (not accept_internal())
+ {
+ return false;
+ }
+
+ // comma -> next value
+ get_token();
+ if (last_token == token_type::value_separator)
+ {
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ return (last_token == token_type::end_array);
+ }
+ }
+
+ case token_type::value_float:
+ {
+ // reject infinity or NAN
+ return std::isfinite(m_lexer.get_number_float());
+ }
+
+ case token_type::literal_false:
+ case token_type::literal_null:
+ case token_type::literal_true:
+ case token_type::value_integer:
+ case token_type::value_string:
+ case token_type::value_unsigned:
+ return true;
+
+ default: // the last token was unexpected
+ return false;
+ }
+ }
+
+ /// get next token from lexer
+ token_type get_token()
+ {
+ return (last_token = m_lexer.scan());
+ }
+
+ /*!
+ @throw parse_error.101 if expected token did not occur
+ */
+ bool expect(token_type t)
+ {
+ if (JSON_UNLIKELY(t != last_token))
+ {
+ errored = true;
+ expected = t;
+ if (allow_exceptions)
+ {
+ throw_exception();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ [[noreturn]] void throw_exception() const
+ {
+ std::string error_msg = "syntax error - ";
+ if (last_token == token_type::parse_error)
+ {
+ error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
+ m_lexer.get_token_string() + "'";
+ }
+ else
+ {
+ error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
+ }
+
+ if (expected != token_type::uninitialized)
+ {
+ error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
+ }
+
+ JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
+ }
+
+ private:
+ /// current level of recursion
+ int depth = 0;
+ /// callback function
+ const parser_callback_t callback = nullptr;
+ /// the type of the last read token
+ token_type last_token = token_type::uninitialized;
+ /// the lexer
+ lexer_t m_lexer;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// possible reason for the syntax error
+ token_type expected = token_type::uninitialized;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+};
+
+///////////////
+// iterators //
+///////////////
+
+/*!
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+
+ constexpr difference_type get_value() const noexcept
+ {
+ return m_it;
+ }
+
+ /// set iterator to a defined beginning
+ void set_begin() noexcept
+ {
+ m_it = begin_value;
+ }
+
+ /// set iterator to a defined past the end
+ void set_end() noexcept
+ {
+ m_it = end_value;
+ }
+
+ /// return whether the iterator can be dereferenced
+ constexpr bool is_begin() const noexcept
+ {
+ return m_it == begin_value;
+ }
+
+ /// return whether the iterator is at end
+ constexpr bool is_end() const noexcept
+ {
+ return m_it == end_value;
+ }
+
+ friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it == rhs.m_it;
+ }
+
+ friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it < rhs.m_it;
+ }
+
+ primitive_iterator_t operator+(difference_type i)
+ {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it - rhs.m_it;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it)
+ {
+ return os << it.m_it;
+ }
+
+ primitive_iterator_t& operator++()
+ {
+ ++m_it;
+ return *this;
+ }
+
+ primitive_iterator_t const operator++(int)
+ {
+ auto result = *this;
+ m_it++;
+ return result;
+ }
+
+ primitive_iterator_t& operator--()
+ {
+ --m_it;
+ return *this;
+ }
+
+ primitive_iterator_t const operator--(int)
+ {
+ auto result = *this;
+ m_it--;
+ return result;
+ }
+
+ primitive_iterator_t& operator+=(difference_type n)
+ {
+ m_it += n;
+ return *this;
+ }
+
+ primitive_iterator_t& operator-=(difference_type n)
+ {
+ m_it -= n;
+ return *this;
+ }
+
+ private:
+ static constexpr difference_type begin_value = 0;
+ static constexpr difference_type end_value = begin_value + 1;
+
+ /// iterator as signed integer type
+ difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+};
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+ /// iterator for JSON objects
+ typename BasicJsonType::object_t::iterator object_iterator {};
+ /// iterator for JSON arrays
+ typename BasicJsonType::array_t::iterator array_iterator {};
+ /// generic iterator for all other types
+ primitive_iterator_t primitive_iterator {};
+};
+
+template<typename IteratorType> class iteration_proxy;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+
+@note An iterator is called *initialized* when a pointer to a JSON value has
+ been set (e.g., by a constructor or a copy assignment). If the iterator is
+ default-constructed, it is *uninitialized* and most methods are undefined.
+ **The library uses assertions to detect calls on uninitialized iterators.**
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+ iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl
+{
+ /// allow basic_json to access private members
+ friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+ friend BasicJsonType;
+ friend iteration_proxy<iter_impl>;
+
+ using object_t = typename BasicJsonType::object_t;
+ using array_t = typename BasicJsonType::array_t;
+ // make sure BasicJsonType is basic_json or const basic_json
+ static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
+ "iter_impl only accepts (const) basic_json");
+
+ public:
+
+ /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
+ /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
+ /// A user-defined iterator should provide publicly accessible typedefs named
+ /// iterator_category, value_type, difference_type, pointer, and reference.
+ /// Note that value_type is required to be non-const, even for constant iterators.
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ /// the type of the values when the iterator is dereferenced
+ using value_type = typename BasicJsonType::value_type;
+ /// a type to represent differences between iterators
+ using difference_type = typename BasicJsonType::difference_type;
+ /// defines a pointer to the type iterated over (value_type)
+ using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_pointer,
+ typename BasicJsonType::pointer>::type;
+ /// defines a reference to the type iterated over (value_type)
+ using reference =
+ typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_reference,
+ typename BasicJsonType::reference>::type;
+
+ /// default constructor
+ iter_impl() = default;
+
+ /*!
+ @brief constructor for a given JSON instance
+ @param[in] object pointer to a JSON object for this iterator
+ @pre object != nullptr
+ @post The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ explicit iter_impl(pointer object) noexcept : m_object(object)
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = typename object_t::iterator();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = typename array_t::iterator();
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator = primitive_iterator_t();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @note The conventional copy constructor and copy assignment are implicitly
+ defined. Combined with the following converting constructor and
+ assignment, they support: (1) copy from iterator to iterator, (2)
+ copy from const iterator to const iterator, and (3) conversion from
+ iterator to const iterator. However conversion from const iterator
+ to iterator is not defined.
+ */
+
+ /*!
+ @brief converting constructor
+ @param[in] other non-const iterator to copy from
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+ : m_object(other.m_object), m_it(other.m_it) {}
+
+ /*!
+ @brief converting assignment
+ @param[in,out] other non-const iterator to copy from
+ @return const/non-const iterator
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ return *this;
+ }
+
+ private:
+ /*!
+ @brief set the iterator to the first value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_begin() noexcept
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_value.object->begin();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_value.array->begin();
+ break;
+ }
+
+ case value_t::null:
+ {
+ // set to end so begin()==end() is true: null is empty
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator.set_begin();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @brief set the iterator past the last value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_end() noexcept
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_value.object->end();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_value.array->end();
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+ }
+ }
+
+ public:
+ /*!
+ @brief return a reference to the value pointed to by the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator*() const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ assert(m_it.object_iterator != m_object->m_value.object->end());
+ return m_it.object_iterator->second;
+ }
+
+ case value_t::array:
+ {
+ assert(m_it.array_iterator != m_object->m_value.array->end());
+ return *m_it.array_iterator;
+ }
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+
+ default:
+ {
+ if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ }
+ }
+ }
+
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ pointer operator->() const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ assert(m_it.object_iterator != m_object->m_value.object->end());
+ return &(m_it.object_iterator->second);
+ }
+
+ case value_t::array:
+ {
+ assert(m_it.array_iterator != m_object->m_value.array->end());
+ return &*m_it.array_iterator;
+ }
+
+ default:
+ {
+ if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ }
+ }
+ }
+
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl const operator++(int)
+ {
+ auto result = *this;
+ ++(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-increment (++it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator++()
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, 1);
+ break;
+ }
+
+ default:
+ {
+ ++m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief post-decrement (it--)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl const operator--(int)
+ {
+ auto result = *this;
+ --(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-decrement (--it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator--()
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, -1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, -1);
+ break;
+ }
+
+ default:
+ {
+ --m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief comparison: equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator==(const iter_impl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
+ }
+
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ return (m_it.object_iterator == other.m_it.object_iterator);
+
+ case value_t::array:
+ return (m_it.array_iterator == other.m_it.array_iterator);
+
+ default:
+ return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: not equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator!=(const iter_impl& other) const
+ {
+ return not operator==(other);
+ }
+
+ /*!
+ @brief comparison: smaller
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<(const iter_impl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
+ }
+
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
+
+ case value_t::array:
+ return (m_it.array_iterator < other.m_it.array_iterator);
+
+ default:
+ return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: less than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator<=(const iter_impl& other) const
+ {
+ return not other.operator < (*this);
+ }
+
+ /*!
+ @brief comparison: greater than
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>(const iter_impl& other) const
+ {
+ return not operator<=(other);
+ }
+
+ /*!
+ @brief comparison: greater than or equal
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ bool operator>=(const iter_impl& other) const
+ {
+ return not operator<(other);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator+=(difference_type i)
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, i);
+ break;
+ }
+
+ default:
+ {
+ m_it.primitive_iterator += i;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator-=(difference_type i)
+ {
+ return operator+=(-i);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator+(difference_type i) const
+ {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief addition of distance and iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ friend iter_impl operator+(difference_type i, const iter_impl& it)
+ {
+ auto result = it;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator-(difference_type i) const
+ {
+ auto result = *this;
+ result -= i;
+ return result;
+ }
+
+ /*!
+ @brief return difference
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ difference_type operator-(const iter_impl& other) const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
+
+ case value_t::array:
+ return m_it.array_iterator - other.m_it.array_iterator;
+
+ default:
+ return m_it.primitive_iterator - other.m_it.primitive_iterator;
+ }
+ }
+
+ /*!
+ @brief access to successor
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator[](difference_type n) const
+ {
+ assert(m_object != nullptr);
+
+ switch (m_object->m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
+
+ case value_t::array:
+ return *std::next(m_it.array_iterator, n);
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+
+ default:
+ {
+ if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value"));
+ }
+ }
+ }
+
+ /*!
+ @brief return the key of an object iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ typename object_t::key_type key() const
+ {
+ assert(m_object != nullptr);
+
+ if (JSON_LIKELY(m_object->is_object()))
+ {
+ return m_it.object_iterator->first;
+ }
+
+ JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
+ }
+
+ /*!
+ @brief return the value of an iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference value() const
+ {
+ return operator*();
+ }
+
+ private:
+ /// associated JSON instance
+ pointer m_object = nullptr;
+ /// the actual iterator of the associated instance
+ internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it = {};
+};
+
+/// proxy class for the iterator_wrapper functions
+template<typename IteratorType> class iteration_proxy
+{
+ private:
+ /// helper class for iteration
+ class iteration_proxy_internal
+ {
+ private:
+ /// the iterator
+ IteratorType anchor;
+ /// an index for arrays (used to create key names)
+ std::size_t array_index = 0;
+
+ public:
+ explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {}
+
+ /// dereference operator (needed for range-based for)
+ iteration_proxy_internal& operator*()
+ {
+ return *this;
+ }
+
+ /// increment operator (needed for range-based for)
+ iteration_proxy_internal& operator++()
+ {
+ ++anchor;
+ ++array_index;
+
+ return *this;
+ }
+
+ /// inequality operator (needed for range-based for)
+ bool operator!=(const iteration_proxy_internal& o) const noexcept
+ {
+ return anchor != o.anchor;
+ }
+
+ /// return key of the iterator
+ std::string key() const
+ {
+ assert(anchor.m_object != nullptr);
+
+ switch (anchor.m_object->type())
+ {
+ // use integer array index as key
+ case value_t::array:
+ return std::to_string(array_index);
+
+ // use key from the object
+ case value_t::object:
+ return anchor.key();
+
+ // use an empty key for all primitive types
+ default:
+ return "";
+ }
+ }
+
+ /// return value of the iterator
+ typename IteratorType::reference value() const
+ {
+ return anchor.value();
+ }
+ };
+
+ /// the container to iterate
+ typename IteratorType::reference container;
+
+ public:
+ /// construct iteration proxy from a container
+ explicit iteration_proxy(typename IteratorType::reference cont)
+ : container(cont) {}
+
+ /// return iterator begin (needed for range-based for)
+ iteration_proxy_internal begin() noexcept
+ {
+ return iteration_proxy_internal(container.begin());
+ }
+
+ /// return iterator end (needed for range-based for)
+ iteration_proxy_internal end() noexcept
+ {
+ return iteration_proxy_internal(container.end());
+ }
+};
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+- [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
+ It is possible to write to the pointed-to element (only if @a Base is
+ @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+ /// shortcut to the reverse iterator adapter
+ using base_iterator = std::reverse_iterator<Base>;
+ /// the reference type for the pointed-to element
+ using reference = typename Base::reference;
+
+ /// create reverse iterator from iterator
+ json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+ : base_iterator(it) {}
+
+ /// create reverse iterator from base class
+ json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+ /// post-increment (it++)
+ json_reverse_iterator const operator++(int)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+ }
+
+ /// pre-increment (++it)
+ json_reverse_iterator& operator++()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+ }
+
+ /// post-decrement (it--)
+ json_reverse_iterator const operator--(int)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+ }
+
+ /// pre-decrement (--it)
+ json_reverse_iterator& operator--()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+ }
+
+ /// add to iterator
+ json_reverse_iterator& operator+=(difference_type i)
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+ }
+
+ /// add to iterator
+ json_reverse_iterator operator+(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+ }
+
+ /// subtract from iterator
+ json_reverse_iterator operator-(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+ }
+
+ /// return difference
+ difference_type operator-(const json_reverse_iterator& other) const
+ {
+ return base_iterator(*this) - base_iterator(other);
+ }
+
+ /// access to successor
+ reference operator[](difference_type n) const
+ {
+ return *(this->operator+(n));
+ }
+
+ /// return the key of an object iterator
+ auto key() const -> decltype(std::declval<Base>().key())
+ {
+ auto it = --this->base();
+ return it.key();
+ }
+
+ /// return the value of an iterator
+ reference value() const
+ {
+ auto it = --this->base();
+ return it.operator * ();
+ }
+};
+
+/////////////////////
+// output adapters //
+/////////////////////
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+ virtual void write_character(CharType c) = 0;
+ virtual void write_characters(const CharType* s, std::size_t length) = 0;
+ virtual ~output_adapter_protocol() = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_vector_adapter(std::vector<CharType>& vec) : v(vec) {}
+
+ void write_character(CharType c) override
+ {
+ v.push_back(c);
+ }
+
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ std::copy(s, s + length, std::back_inserter(v));
+ }
+
+ private:
+ std::vector<CharType>& v;
+};
+
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_stream_adapter(std::basic_ostream<CharType>& s) : stream(s) {}
+
+ void write_character(CharType c) override
+ {
+ stream.put(c);
+ }
+
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ stream.write(s, static_cast<std::streamsize>(length));
+ }
+
+ private:
+ std::basic_ostream<CharType>& stream;
+};
+
+/// output adapter for basic_string
+template<typename CharType>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_string_adapter(std::basic_string<CharType>& s) : str(s) {}
+
+ void write_character(CharType c) override
+ {
+ str.push_back(c);
+ }
+
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ str.append(s, length);
+ }
+
+ private:
+ std::basic_string<CharType>& str;
+};
+
+template<typename CharType>
+class output_adapter
+{
+ public:
+ output_adapter(std::vector<CharType>& vec)
+ : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
+
+ output_adapter(std::basic_ostream<CharType>& s)
+ : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+
+ output_adapter(std::basic_string<CharType>& s)
+ : oa(std::make_shared<output_string_adapter<CharType>>(s)) {}
+
+ operator output_adapter_t<CharType>()
+ {
+ return oa;
+ }
+
+ private:
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+//////////////////////////////
+// binary reader and writer //
+//////////////////////////////
+
+/*!
+@brief deserialization of CBOR and MessagePack values
+*/
+template<typename BasicJsonType>
+class binary_reader
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+
+ public:
+ /*!
+ @brief create a binary reader
+
+ @param[in] adapter input adapter to read from
+ */
+ explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter))
+ {
+ assert(ia);
+ }
+
+ /*!
+ @brief create a JSON value from CBOR input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from CBOR input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ BasicJsonType parse_cbor(const bool strict)
+ {
+ const auto res = parse_cbor_internal();
+ if (strict)
+ {
+ get();
+ check_eof(true);
+ }
+ return res;
+ }
+
+ /*!
+ @brief create a JSON value from MessagePack input
+
+ @param[in] strict whether to expect the input to be consumed completed
+ @return JSON value created from MessagePack input
+
+ @throw parse_error.110 if input ended unexpectedly or the end of file was
+ not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported byte was read
+ */
+ BasicJsonType parse_msgpack(const bool strict)
+ {
+ const auto res = parse_msgpack_internal();
+ if (strict)
+ {
+ get();
+ check_eof(true);
+ }
+ return res;
+ }
+
+ /*!
+ @brief determine system byte order
+
+ @return true if and only if system's byte order is little endian
+
+ @note from http://stackoverflow.com/a/1001328/266378
+ */
+ static constexpr bool little_endianess(int num = 1) noexcept
+ {
+ return (*reinterpret_cast<char*>(&num) == 1);
+ }
+
+ private:
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+ */
+ BasicJsonType parse_cbor_internal(const bool get_char = true)
+ {
+ switch (get_char ? get() : current)
+ {
+ // EOF
+ case std::char_traits<char>::eof():
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ // Integer 0x00..0x17 (0..23)
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ return static_cast<number_unsigned_t>(current);
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ return get_number<uint8_t>();
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ return get_number<uint16_t>();
+
+ case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+ return get_number<uint32_t>();
+
+ case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+ return get_number<uint64_t>();
+
+ // Negative integer -1-0x00..-1-0x17 (-1..-24)
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ return static_cast<int8_t>(0x20 - 1 - current);
+
+ case 0x38: // Negative integer (one-byte uint8_t follows)
+ {
+ // must be uint8_t !
+ return static_cast<number_integer_t>(-1) - get_number<uint8_t>();
+ }
+
+ case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ return static_cast<number_integer_t>(-1) - get_number<uint16_t>();
+ }
+
+ case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ return static_cast<number_integer_t>(-1) - get_number<uint32_t>();
+ }
+
+ case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ return static_cast<number_integer_t>(-1) -
+ static_cast<number_integer_t>(get_number<uint64_t>());
+ }
+
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ return get_cbor_string();
+ }
+
+ // array (0x00..0x17 data items follow)
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ {
+ return get_cbor_array(current & 0x1F);
+ }
+
+ case 0x98: // array (one-byte uint8_t for n follows)
+ {
+ return get_cbor_array(get_number<uint8_t>());
+ }
+
+ case 0x99: // array (two-byte uint16_t for n follow)
+ {
+ return get_cbor_array(get_number<uint16_t>());
+ }
+
+ case 0x9A: // array (four-byte uint32_t for n follow)
+ {
+ return get_cbor_array(get_number<uint32_t>());
+ }
+
+ case 0x9B: // array (eight-byte uint64_t for n follow)
+ {
+ return get_cbor_array(get_number<uint64_t>());
+ }
+
+ case 0x9F: // array (indefinite length)
+ {
+ BasicJsonType result = value_t::array;
+ while (get() != 0xFF)
+ {
+ result.push_back(parse_cbor_internal(false));
+ }
+ return result;
+ }
+
+ // map (0x00..0x17 pairs of data items follow)
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ {
+ return get_cbor_object(current & 0x1F);
+ }
+
+ case 0xB8: // map (one-byte uint8_t for n follows)
+ {
+ return get_cbor_object(get_number<uint8_t>());
+ }
+
+ case 0xB9: // map (two-byte uint16_t for n follow)
+ {
+ return get_cbor_object(get_number<uint16_t>());
+ }
+
+ case 0xBA: // map (four-byte uint32_t for n follow)
+ {
+ return get_cbor_object(get_number<uint32_t>());
+ }
+
+ case 0xBB: // map (eight-byte uint64_t for n follow)
+ {
+ return get_cbor_object(get_number<uint64_t>());
+ }
+
+ case 0xBF: // map (indefinite length)
+ {
+ BasicJsonType result = value_t::object;
+ while (get() != 0xFF)
+ {
+ auto key = get_cbor_string();
+ result[key] = parse_cbor_internal();
+ }
+ return result;
+ }
+
+ case 0xF4: // false
+ {
+ return false;
+ }
+
+ case 0xF5: // true
+ {
+ return true;
+ }
+
+ case 0xF6: // null
+ {
+ return value_t::null;
+ }
+
+ case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+ {
+ const int byte1 = get();
+ check_eof();
+ const int byte2 = get();
+ check_eof();
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const int half = (byte1 << 8) + byte2;
+ const int exp = (half >> 10) & 0x1F;
+ const int mant = half & 0x3FF;
+ double val;
+ if (exp == 0)
+ {
+ val = std::ldexp(mant, -24);
+ }
+ else if (exp != 31)
+ {
+ val = std::ldexp(mant + 1024, exp - 25);
+ }
+ else
+ {
+ val = (mant == 0) ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ }
+ return (half & 0x8000) != 0 ? -val : val;
+ }
+
+ case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+ {
+ return get_number<float>();
+ }
+
+ case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ return get_number<double>();
+ }
+
+ default: // anything else (0xFF is handled inside the other types)
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ BasicJsonType parse_msgpack_internal()
+ {
+ switch (get())
+ {
+ // EOF
+ case std::char_traits<char>::eof():
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+
+ // positive fixint
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ return static_cast<number_unsigned_t>(current);
+
+ // fixmap
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ {
+ return get_msgpack_object(current & 0x0F);
+ }
+
+ // fixarray
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ case 0x98:
+ case 0x99:
+ case 0x9A:
+ case 0x9B:
+ case 0x9C:
+ case 0x9D:
+ case 0x9E:
+ case 0x9F:
+ {
+ return get_msgpack_array(current & 0x0F);
+ }
+
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ return get_msgpack_string();
+
+ case 0xC0: // nil
+ return value_t::null;
+
+ case 0xC2: // false
+ return false;
+
+ case 0xC3: // true
+ return true;
+
+ case 0xCA: // float 32
+ return get_number<float>();
+
+ case 0xCB: // float 64
+ return get_number<double>();
+
+ case 0xCC: // uint 8
+ return get_number<uint8_t>();
+
+ case 0xCD: // uint 16
+ return get_number<uint16_t>();
+
+ case 0xCE: // uint 32
+ return get_number<uint32_t>();
+
+ case 0xCF: // uint 64
+ return get_number<uint64_t>();
+
+ case 0xD0: // int 8
+ return get_number<int8_t>();
+
+ case 0xD1: // int 16
+ return get_number<int16_t>();
+
+ case 0xD2: // int 32
+ return get_number<int32_t>();
+
+ case 0xD3: // int 64
+ return get_number<int64_t>();
+
+ case 0xD9: // str 8
+ case 0xDA: // str 16
+ case 0xDB: // str 32
+ return get_msgpack_string();
+
+ case 0xDC: // array 16
+ {
+ return get_msgpack_array(get_number<uint16_t>());
+ }
+
+ case 0xDD: // array 32
+ {
+ return get_msgpack_array(get_number<uint32_t>());
+ }
+
+ case 0xDE: // map 16
+ {
+ return get_msgpack_object(get_number<uint16_t>());
+ }
+
+ case 0xDF: // map 32
+ {
+ return get_msgpack_object(get_number<uint32_t>());
+ }
+
+ // positive fixint
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
+ case 0xEF:
+ case 0xF0:
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ case 0xF4:
+ case 0xF5:
+ case 0xF6:
+ case 0xF7:
+ case 0xF8:
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ case 0xFC:
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ return static_cast<int8_t>(current);
+
+ default: // anything else
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(112, chars_read,
+ "error reading MessagePack; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ /*!
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a -'ve valued
+ `std::char_traits<char>::eof()` in that case.
+
+ @return character read from the input
+ */
+ int get()
+ {
+ ++chars_read;
+ return (current = ia->get_character());
+ }
+
+ /*
+ @brief read a number from the input
+
+ @tparam NumberType the type of the number
+
+ @return number of type @a NumberType
+
+ @note This function needs to respect the system's endianess, because
+ bytes in CBOR and MessagePack are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
+
+ @throw parse_error.110 if input has less than `sizeof(NumberType)` bytes
+ */
+ template<typename NumberType> NumberType get_number()
+ {
+ // step 1: read input into array with system's byte order
+ std::array<uint8_t, sizeof(NumberType)> vec;
+ for (std::size_t i = 0; i < sizeof(NumberType); ++i)
+ {
+ get();
+ check_eof();
+
+ // reverse byte order prior to conversion if necessary
+ if (is_little_endian)
+ {
+ vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current);
+ }
+ else
+ {
+ vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE
+ }
+ }
+
+ // step 2: convert array into number of type T and return
+ NumberType result;
+ std::memcpy(&result, vec.data(), sizeof(NumberType));
+ return result;
+ }
+
+ /*!
+ @brief create a string by reading characters from the input
+
+ @param[in] len number of bytes to read
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref check_eof() detects the end of
+ the input before we run out of string memory.
+
+ @return string created by reading @a len bytes
+
+ @throw parse_error.110 if input has less than @a len bytes
+ */
+ template<typename NumberType>
+ std::string get_string(const NumberType len)
+ {
+ std::string result;
+ std::generate_n(std::back_inserter(result), len, [this]()
+ {
+ get();
+ check_eof();
+ return static_cast<char>(current);
+ });
+ return result;
+ }
+
+ /*!
+ @brief reads a CBOR string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+ Additionally, CBOR's strings with indefinite lengths are supported.
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_cbor_string()
+ {
+ check_eof();
+
+ switch (current)
+ {
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ return get_string(current & 0x1F);
+ }
+
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ {
+ return get_string(get_number<uint8_t>());
+ }
+
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ {
+ return get_string(get_number<uint16_t>());
+ }
+
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ {
+ return get_string(get_number<uint32_t>());
+ }
+
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ {
+ return get_string(get_number<uint64_t>());
+ }
+
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ std::string result;
+ while (get() != 0xFF)
+ {
+ check_eof();
+ result.push_back(static_cast<char>(current));
+ }
+ return result;
+ }
+
+ default:
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_cbor_array(const NumberType len)
+ {
+ BasicJsonType result = value_t::array;
+ std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
+ {
+ return parse_cbor_internal();
+ });
+ return result;
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_cbor_object(const NumberType len)
+ {
+ BasicJsonType result = value_t::object;
+ std::generate_n(std::inserter(*result.m_value.object,
+ result.m_value.object->end()),
+ len, [this]()
+ {
+ get();
+ auto key = get_cbor_string();
+ auto val = parse_cbor_internal();
+ return std::make_pair(std::move(key), std::move(val));
+ });
+ return result;
+ }
+
+ /*!
+ @brief reads a MessagePack string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+
+ @return string
+
+ @throw parse_error.110 if input ended
+ @throw parse_error.113 if an unexpected byte is read
+ */
+ std::string get_msgpack_string()
+ {
+ check_eof();
+
+ switch (current)
+ {
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ {
+ return get_string(current & 0x1F);
+ }
+
+ case 0xD9: // str 8
+ {
+ return get_string(get_number<uint8_t>());
+ }
+
+ case 0xDA: // str 16
+ {
+ return get_string(get_number<uint16_t>());
+ }
+
+ case 0xDB: // str 32
+ {
+ return get_string(get_number<uint32_t>());
+ }
+
+ default:
+ {
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << current;
+ JSON_THROW(parse_error::create(113, chars_read,
+ "expected a MessagePack string; last byte: 0x" + ss.str()));
+ }
+ }
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_msgpack_array(const NumberType len)
+ {
+ BasicJsonType result = value_t::array;
+ std::generate_n(std::back_inserter(*result.m_value.array), len, [this]()
+ {
+ return parse_msgpack_internal();
+ });
+ return result;
+ }
+
+ template<typename NumberType>
+ BasicJsonType get_msgpack_object(const NumberType len)
+ {
+ BasicJsonType result = value_t::object;
+ std::generate_n(std::inserter(*result.m_value.object,
+ result.m_value.object->end()),
+ len, [this]()
+ {
+ get();
+ auto key = get_msgpack_string();
+ auto val = parse_msgpack_internal();
+ return std::make_pair(std::move(key), std::move(val));
+ });
+ return result;
+ }
+
+ /*!
+ @brief check if input ended
+ @throw parse_error.110 if input ended
+ */
+ void check_eof(const bool expect_eof = false) const
+ {
+ if (expect_eof)
+ {
+ if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "expected end of input"));
+ }
+ }
+ else
+ {
+ if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))
+ {
+ JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input"));
+ }
+ }
+ }
+
+ private:
+ /// input adapter
+ input_adapter_t ia = nullptr;
+
+ /// the current character
+ int current = std::char_traits<char>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// whether we can assume little endianess
+ const bool is_little_endian = little_endianess();
+};
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+ public:
+ /*!
+ @brief create a binary writer
+
+ @param[in] adapter output adapter to write to
+ */
+ explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
+ {
+ assert(oa);
+ }
+
+ /*!
+ @brief[in] j JSON value to serialize
+ */
+ void write_cbor(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ oa->write_character(static_cast<CharType>(0xF6));
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ oa->write_character(j.m_value.boolean
+ ? static_cast<CharType>(0xF5)
+ : static_cast<CharType>(0xF4));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // CBOR does not differentiate between positive signed
+ // integers and unsigned integers. Therefore, we used the
+ // code from the value_t::number_unsigned case here.
+ if (j.m_value.number_integer <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x18));
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x19));
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x1A));
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else
+ {
+ oa->write_character(static_cast<CharType>(0x1B));
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ // The conversions below encode the sign in the first
+ // byte, and the value is converted to a positive number.
+ const auto positive_number = -1 - j.m_value.number_integer;
+ if (j.m_value.number_integer >= -24)
+ {
+ write_number(static_cast<uint8_t>(0x20 + positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x38));
+ write_number(static_cast<uint8_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x39));
+ write_number(static_cast<uint16_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x3A));
+ write_number(static_cast<uint32_t>(positive_number));
+ }
+ else
+ {
+ oa->write_character(static_cast<CharType>(0x3B));
+ write_number(static_cast<uint64_t>(positive_number));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x18));
+ write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x19));
+ write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ oa->write_character(static_cast<CharType>(0x1A));
+ write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
+ }
+ else
+ {
+ oa->write_character(static_cast<CharType>(0x1B));
+ write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
+ }
+ break;
+ }
+
+ case value_t::number_float: // Double-Precision Float
+ {
+ oa->write_character(static_cast<CharType>(0xFB));
+ write_number(j.m_value.number_float);
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_value.string->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0x60 + N));
+ }
+ else if (N <= 0xFF)
+ {
+ oa->write_character(static_cast<CharType>(0x78));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x79));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x7A));
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xFFFFFFFFFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x7B));
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0x80 + N));
+ }
+ else if (N <= 0xFF)
+ {
+ oa->write_character(static_cast<CharType>(0x98));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x99));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x9A));
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xFFFFFFFFFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0x9B));
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_cbor(el);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<uint8_t>(0xA0 + N));
+ }
+ else if (N <= 0xFF)
+ {
+ oa->write_character(static_cast<CharType>(0xB8));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0xB9));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0xBA));
+ write_number(static_cast<uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= 0xFFFFFFFFFFFFFFFF)
+ {
+ oa->write_character(static_cast<CharType>(0xBB));
+ write_number(static_cast<uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_cbor(el.first);
+ write_cbor(el.second);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ /*!
+ @brief[in] j JSON value to serialize
+ */
+ void write_msgpack(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null: // nil
+ {
+ oa->write_character(static_cast<CharType>(0xC0));
+ break;
+ }
+
+ case value_t::boolean: // true and false
+ {
+ oa->write_character(j.m_value.boolean
+ ? static_cast<CharType>(0xC3)
+ : static_cast<CharType>(0xC2));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_value.number_integer >= 0)
+ {
+ // MessagePack does not differentiate between positive
+ // signed integers and unsigned integers. Therefore, we used
+ // the code from the value_t::number_unsigned case here.
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(static_cast<CharType>(0xCC));
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(static_cast<CharType>(0xCD));
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(static_cast<CharType>(0xCE));
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(static_cast<CharType>(0xCF));
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ }
+ else
+ {
+ if (j.m_value.number_integer >= -32)
+ {
+ // negative fixnum
+ write_number(static_cast<int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
+ {
+ // int 8
+ oa->write_character(static_cast<CharType>(0xD0));
+ write_number(static_cast<int8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
+ {
+ // int 16
+ oa->write_character(static_cast<CharType>(0xD1));
+ write_number(static_cast<int16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
+ {
+ // int 32
+ oa->write_character(static_cast<CharType>(0xD2));
+ write_number(static_cast<int32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
+ j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
+ {
+ // int 64
+ oa->write_character(static_cast<CharType>(0xD3));
+ write_number(static_cast<int64_t>(j.m_value.number_integer));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(static_cast<CharType>(0xCC));
+ write_number(static_cast<uint8_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(static_cast<CharType>(0xCD));
+ write_number(static_cast<uint16_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(static_cast<CharType>(0xCE));
+ write_number(static_cast<uint32_t>(j.m_value.number_integer));
+ }
+ else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(static_cast<CharType>(0xCF));
+ write_number(static_cast<uint64_t>(j.m_value.number_integer));
+ }
+ break;
+ }
+
+ case value_t::number_float: // float 64
+ {
+ oa->write_character(static_cast<CharType>(0xCB));
+ write_number(j.m_value.number_float);
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_value.string->size();
+ if (N <= 31)
+ {
+ // fixstr
+ write_number(static_cast<uint8_t>(0xA0 | N));
+ }
+ else if (N <= 255)
+ {
+ // str 8
+ oa->write_character(static_cast<CharType>(0xD9));
+ write_number(static_cast<uint8_t>(N));
+ }
+ else if (N <= 65535)
+ {
+ // str 16
+ oa->write_character(static_cast<CharType>(0xDA));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 4294967295)
+ {
+ // str 32
+ oa->write_character(static_cast<CharType>(0xDB));
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
+ j.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_value.array->size();
+ if (N <= 15)
+ {
+ // fixarray
+ write_number(static_cast<uint8_t>(0x90 | N));
+ }
+ else if (N <= 0xFFFF)
+ {
+ // array 16
+ oa->write_character(static_cast<CharType>(0xDC));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 0xFFFFFFFF)
+ {
+ // array 32
+ oa->write_character(static_cast<CharType>(0xDD));
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.array)
+ {
+ write_msgpack(el);
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_value.object->size();
+ if (N <= 15)
+ {
+ // fixmap
+ write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
+ }
+ else if (N <= 65535)
+ {
+ // map 16
+ oa->write_character(static_cast<CharType>(0xDE));
+ write_number(static_cast<uint16_t>(N));
+ }
+ else if (N <= 4294967295)
+ {
+ // map 32
+ oa->write_character(static_cast<CharType>(0xDF));
+ write_number(static_cast<uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_value.object)
+ {
+ write_msgpack(el.first);
+ write_msgpack(el.second);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ private:
+ /*
+ @brief write a number to output input
+
+ @param[in] n number of type @a NumberType
+ @tparam NumberType the type of the number
+
+ @note This function needs to respect the system's endianess, because bytes
+ in CBOR and MessagePack are stored in network order (big endian) and
+ therefore need reordering on little endian systems.
+ */
+ template<typename NumberType> void write_number(NumberType n)
{
- return '.';
+ // step 1: write number to array of length NumberType
+ std::array<CharType, sizeof(NumberType)> vec;
+ std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+ // step 2: write array to output (with possible reordering)
+ if (is_little_endian)
+ {
+ // reverse byte order prior to conversion if necessary
+ std::reverse(vec.begin(), vec.end());
+ }
+
+ oa->write_characters(vec.data(), sizeof(NumberType));
}
+
+ private:
+ /// whether we can assume little endianess
+ const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
+
+ /// the output
+ output_adapter_t<CharType> oa = nullptr;
};
+///////////////////
+// serialization //
+///////////////////
+
+template<typename BasicJsonType>
+class serializer
+{
+ using string_t = typename BasicJsonType::string_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ public:
+ /*!
+ @param[in] s output stream to serialize to
+ @param[in] ichar indentation character to use
+ */
+ serializer(output_adapter_t<char> s, const char ichar)
+ : o(std::move(s)), loc(std::localeconv()),
+ thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
+ decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
+ indent_char(ichar), indent_string(512, indent_char) {}
+
+ // delete because of pointer members
+ serializer(const serializer&) = delete;
+ serializer& operator=(const serializer&) = delete;
+
+ /*!
+ @brief internal implementation of the serialization function
+
+ This function is called by the public member function dump and organizes
+ the serialization internally. The indentation level is propagated as
+ additional parameter. In case of arrays and objects, the function is
+ called recursively.
+
+ - strings and object keys are escaped using `escape_string()`
+ - integer numbers are converted implicitly via `operator<<`
+ - floating-point numbers are converted to a string using `"%g"` format
+
+ @param[in] val value to serialize
+ @param[in] pretty_print whether the output shall be pretty-printed
+ @param[in] indent_step the indent level
+ @param[in] current_indent the current indent level (only used internally)
+ */
+ void dump(const BasicJsonType& val, const bool pretty_print,
+ const bool ensure_ascii,
+ const unsigned int indent_step,
+ const unsigned int current_indent = 0)
+ {
+ switch (val.m_type)
+ {
+ case value_t::object:
+ {
+ if (val.m_value.object->empty())
+ {
+ o->write_characters("{}", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("{\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ auto i = val.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ assert(i != val.m_value.object->cend());
+ assert(std::next(i) == val.m_value.object->cend());
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_character('{');
+
+ // first n-1 elements
+ auto i = val.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ assert(i != val.m_value.object->cend());
+ assert(std::next(i) == val.m_value.object->cend());
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character('}');
+ }
+
+ return;
+ }
+
+ case value_t::array:
+ {
+ if (val.m_value.array->empty())
+ {
+ o->write_characters("[]", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("[\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(*i, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ assert(not val.m_value.array->empty());
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character(']');
+ }
+ else
+ {
+ o->write_character('[');
+
+ // first n-1 elements
+ for (auto i = val.m_value.array->cbegin();
+ i != val.m_value.array->cend() - 1; ++i)
+ {
+ dump(*i, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ assert(not val.m_value.array->empty());
+ dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character(']');
+ }
+
+ return;
+ }
+
+ case value_t::string:
+ {
+ o->write_character('\"');
+ dump_escaped(*val.m_value.string, ensure_ascii);
+ o->write_character('\"');
+ return;
+ }
+
+ case value_t::boolean:
+ {
+ if (val.m_value.boolean)
+ {
+ o->write_characters("true", 4);
+ }
+ else
+ {
+ o->write_characters("false", 5);
+ }
+ return;
+ }
+
+ case value_t::number_integer:
+ {
+ dump_integer(val.m_value.number_integer);
+ return;
+ }
+
+ case value_t::number_unsigned:
+ {
+ dump_integer(val.m_value.number_unsigned);
+ return;
+ }
+
+ case value_t::number_float:
+ {
+ dump_float(val.m_value.number_float);
+ return;
+ }
+
+ case value_t::discarded:
+ {
+ o->write_characters("<discarded>", 11);
+ return;
+ }
+
+ case value_t::null:
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+ }
+ }
+
+ private:
+ /*!
+ @brief returns the number of expected bytes following in UTF-8 string
+
+ @param[in] u the first byte of a UTF-8 string
+ @return the number of expected bytes following
+ */
+ static constexpr std::size_t bytes_following(const uint8_t u)
+ {
+ return ((u <= 127) ? 0
+ : ((192 <= u and u <= 223) ? 1
+ : ((224 <= u and u <= 239) ? 2
+ : ((240 <= u and u <= 247) ? 3 : std::string::npos))));
+ }
+
+ /*!
+ @brief calculates the extra space to escape a JSON string
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+ @return the number of characters required to escape string @a s
+
+ @complexity Linear in the length of string @a s.
+ */
+ static std::size_t extra_space(const string_t& s,
+ const bool ensure_ascii) noexcept
+ {
+ std::size_t res = 0;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ switch (s[i])
+ {
+ // control characters that can be escaped with a backslash
+ case '"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ {
+ // from c (1 byte) to \x (2 bytes)
+ res += 1;
+ break;
+ }
+
+ // control characters that need \uxxxx escaping
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x0B:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ {
+ // from c (1 byte) to \uxxxx (6 bytes)
+ res += 5;
+ break;
+ }
+
+ default:
+ {
+ if (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F))
+ {
+ const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
+ // invalid characters will be detected by throw_if_invalid_utf8
+ assert (bytes != std::string::npos);
+
+ if (bytes == 3)
+ {
+ // codepoints that need 4 bytes (i.e., 3 additional
+ // bytes) in UTF-8 need a surrogate pair when \u
+ // escaping is used: from 4 bytes to \uxxxx\uxxxx
+ // (12 bytes)
+ res += (12 - bytes - 1);
+ }
+ else
+ {
+ // from x bytes to \uxxxx (6 bytes)
+ res += (6 - bytes - 1);
+ }
+
+ // skip the additional bytes
+ i += bytes;
+ }
+ break;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ static void escape_codepoint(int codepoint, string_t& result, std::size_t& pos)
+ {
+ // expecting a proper codepoint
+ assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
+
+ // the last written character was the backslash before the 'u'
+ assert(result[pos] == '\\');
+
+ // write the 'u'
+ result[++pos] = 'u';
+
+ // convert a number 0..15 to its hex representation (0..f)
+ static const std::array<char, 16> hexify =
+ {
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ }
+ };
+
+ if (codepoint < 0x10000)
+ {
+ // codepoints U+0000..U+FFFF can be represented as \uxxxx.
+ result[++pos] = hexify[(codepoint >> 12) & 0x0F];
+ result[++pos] = hexify[(codepoint >> 8) & 0x0F];
+ result[++pos] = hexify[(codepoint >> 4) & 0x0F];
+ result[++pos] = hexify[codepoint & 0x0F];
+ }
+ else
+ {
+ // codepoints U+10000..U+10FFFF need a surrogate pair to be
+ // represented as \uxxxx\uxxxx.
+ // http://www.unicode.org/faq/utf_bom.html#utf16-4
+ codepoint -= 0x10000;
+ const int high_surrogate = 0xD800 | ((codepoint >> 10) & 0x3FF);
+ const int low_surrogate = 0xDC00 | (codepoint & 0x3FF);
+ result[++pos] = hexify[(high_surrogate >> 12) & 0x0F];
+ result[++pos] = hexify[(high_surrogate >> 8) & 0x0F];
+ result[++pos] = hexify[(high_surrogate >> 4) & 0x0F];
+ result[++pos] = hexify[high_surrogate & 0x0F];
+ ++pos; // backslash is already in output
+ result[++pos] = 'u';
+ result[++pos] = hexify[(low_surrogate >> 12) & 0x0F];
+ result[++pos] = hexify[(low_surrogate >> 8) & 0x0F];
+ result[++pos] = hexify[(low_surrogate >> 4) & 0x0F];
+ result[++pos] = hexify[low_surrogate & 0x0F];
+ }
+
+ ++pos;
+ }
+
+ /*!
+ @brief dump escaped string
+
+ Escape a string by replacing certain special characters by a sequence of an
+ escape character (backslash) and another character and other control
+ characters by a sequence of "\u" followed by a four-digit hex
+ representation. The escaped string is written to output stream @a o.
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+
+ @complexity Linear in the length of string @a s.
+ */
+ void dump_escaped(const string_t& s, const bool ensure_ascii) const
+ {
+ throw_if_invalid_utf8(s);
+
+ const auto space = extra_space(s, ensure_ascii);
+ if (space == 0)
+ {
+ o->write_characters(s.c_str(), s.size());
+ return;
+ }
+
+ // create a result string of necessary size
+ string_t result(s.size() + space, '\\');
+ std::size_t pos = 0;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ switch (s[i])
+ {
+ case '"': // quotation mark (0x22)
+ {
+ result[pos + 1] = '"';
+ pos += 2;
+ break;
+ }
+
+ case '\\': // reverse solidus (0x5C)
+ {
+ // nothing to change
+ pos += 2;
+ break;
+ }
+
+ case '\b': // backspace (0x08)
+ {
+ result[pos + 1] = 'b';
+ pos += 2;
+ break;
+ }
+
+ case '\f': // formfeed (0x0C)
+ {
+ result[pos + 1] = 'f';
+ pos += 2;
+ break;
+ }
+
+ case '\n': // newline (0x0A)
+ {
+ result[pos + 1] = 'n';
+ pos += 2;
+ break;
+ }
+
+ case '\r': // carriage return (0x0D)
+ {
+ result[pos + 1] = 'r';
+ pos += 2;
+ break;
+ }
+
+ case '\t': // horizontal tab (0x09)
+ {
+ result[pos + 1] = 't';
+ pos += 2;
+ break;
+ }
+
+ default:
+ {
+ // escape control characters (0x00..0x1F) or, if
+ // ensure_ascii parameter is used, non-ASCII characters
+ if ((0x00 <= s[i] and s[i] <= 0x1F) or
+ (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F)))
+ {
+ const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
+ // invalid characters will be detected by throw_if_invalid_utf8
+ assert (bytes != std::string::npos);
+
+ // check that the additional bytes are present
+ assert(i + bytes < s.size());
+
+ // to use \uxxxx escaping, we first need to calculate
+ // the codepoint from the UTF-8 bytes
+ int codepoint = 0;
+
+ // bytes is unsigned type:
+ assert(bytes <= 3);
+ switch (bytes)
+ {
+ case 0:
+ {
+ codepoint = s[i] & 0xFF;
+ break;
+ }
+
+ case 1:
+ {
+ codepoint = ((s[i] & 0x3F) << 6)
+ + (s[i + 1] & 0x7F);
+ break;
+ }
+
+ case 2:
+ {
+ codepoint = ((s[i] & 0x1F) << 12)
+ + ((s[i + 1] & 0x7F) << 6)
+ + (s[i + 2] & 0x7F);
+ break;
+ }
+
+ case 3:
+ {
+ codepoint = ((s[i] & 0xF) << 18)
+ + ((s[i + 1] & 0x7F) << 12)
+ + ((s[i + 2] & 0x7F) << 6)
+ + (s[i + 3] & 0x7F);
+ break;
+ }
+
+ default:
+ break; // LCOV_EXCL_LINE
+ }
+
+ escape_codepoint(codepoint, result, pos);
+ i += bytes;
+ }
+ else
+ {
+ // all other characters are added as-is
+ result[pos++] = s[i];
+ }
+ break;
+ }
+ }
+ }
+
+ assert(pos == result.size());
+ o->write_characters(result.c_str(), result.size());
+ }
+
+ /*!
+ @brief dump an integer
+
+ Dump a given integer to output stream @a o. Works internally with
+ @a number_buffer.
+
+ @param[in] x integer number (signed or unsigned) to dump
+ @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+ */
+ template<typename NumberType, detail::enable_if_t<
+ std::is_same<NumberType, number_unsigned_t>::value or
+ std::is_same<NumberType, number_integer_t>::value,
+ int> = 0>
+ void dump_integer(NumberType x)
+ {
+ // special case for "0"
+ if (x == 0)
+ {
+ o->write_character('0');
+ return;
+ }
+
+ const bool is_negative = (x <= 0) and (x != 0); // see issue #755
+ std::size_t i = 0;
+
+ while (x != 0)
+ {
+ // spare 1 byte for '\0'
+ assert(i < number_buffer.size() - 1);
+
+ const auto digit = std::labs(static_cast<long>(x % 10));
+ number_buffer[i++] = static_cast<char>('0' + digit);
+ x /= 10;
+ }
+
+ if (is_negative)
+ {
+ // make sure there is capacity for the '-'
+ assert(i < number_buffer.size() - 2);
+ number_buffer[i++] = '-';
+ }
+
+ std::reverse(number_buffer.begin(), number_buffer.begin() + i);
+ o->write_characters(number_buffer.data(), i);
+ }
+
+ /*!
+ @brief dump a floating-point number
+
+ Dump a given floating-point number to output stream @a o. Works internally
+ with @a number_buffer.
+
+ @param[in] x floating-point number to dump
+ */
+ void dump_float(number_float_t x)
+ {
+ // NaN / inf
+ if (not std::isfinite(x) or std::isnan(x))
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+
+ // get number of digits for a text -> float -> text round-trip
+ static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
+
+ // the actual conversion
+ std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+ // negative value indicates an error
+ assert(len > 0);
+ // check if buffer was large enough
+ assert(static_cast<std::size_t>(len) < number_buffer.size());
+
+ // erase thousands separator
+ if (thousands_sep != '\0')
+ {
+ const auto end = std::remove(number_buffer.begin(),
+ number_buffer.begin() + len, thousands_sep);
+ std::fill(end, number_buffer.end(), '\0');
+ assert((end - number_buffer.begin()) <= len);
+ len = (end - number_buffer.begin());
+ }
+
+ // convert decimal point to '.'
+ if (decimal_point != '\0' and decimal_point != '.')
+ {
+ const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+ if (dec_pos != number_buffer.end())
+ {
+ *dec_pos = '.';
+ }
+ }
+
+ o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+ // determine if need to append ".0"
+ const bool value_is_int_like =
+ std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+ [](char c)
+ {
+ return (c == '.' or c == 'e');
+ });
+
+ if (value_is_int_like)
+ {
+ o->write_characters(".0", 2);
+ }
+ }
+
+ /*!
+ @brief check whether a string is UTF-8 encoded
+
+ The function checks each byte of a string whether it is UTF-8 encoded. The
+ result of the check is stored in the @a state parameter. The function must
+ be called initially with state 0 (accept). State 1 means the string must
+ be rejected, because the current byte is not allowed. If the string is
+ completely processed, but the state is non-zero, the string ended
+ prematurely; that is, the last byte indicated more bytes should have
+ followed.
+
+ @param[in,out] state the state of the decoding
+ @param[in] byte next byte to decode
+
+ @note The function has been edited: a std::array is used and the code
+ point is not calculated.
+
+ @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ */
+ static void decode(uint8_t& state, const uint8_t byte)
+ {
+ static const std::array<uint8_t, 400> utf8d =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+ 8, 8, 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, // C0..DF
+ 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+ 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+ 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+ 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+ 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+ }
+ };
+
+ const uint8_t type = utf8d[byte];
+ state = utf8d[256u + state * 16u + type];
+ }
+
+ /*!
+ @brief throw an exception if a string is not UTF-8 encoded
+
+ @param[in] str UTF-8 string to check
+ @throw type_error.316 if passed string is not UTF-8 encoded
+
+ @since version 3.0.0
+ */
+ static void throw_if_invalid_utf8(const std::string& str)
+ {
+ // start with state 0 (= accept)
+ uint8_t state = 0;
+
+ for (size_t i = 0; i < str.size(); ++i)
+ {
+ const auto byte = static_cast<uint8_t>(str[i]);
+ decode(state, byte);
+ if (state == 1)
+ {
+ // state 1 means reject
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(byte);
+ JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str()));
+ }
+ }
+
+ if (state != 0)
+ {
+ // we finish reading, but do not accept: string was incomplete
+ std::stringstream ss;
+ ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(static_cast<uint8_t>(str.back()));
+ JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str()));
+ }
+ }
+
+ private:
+ /// the output of the serializer
+ output_adapter_t<char> o = nullptr;
+
+ /// a (hopefully) large enough character buffer
+ std::array<char, 64> number_buffer{{}};
+
+ /// the locale
+ const std::lconv* loc = nullptr;
+ /// the locale's thousand separator character
+ const char thousands_sep = '\0';
+ /// the locale's decimal point character
+ const char decimal_point = '\0';
+
+ /// the indentation character
+ const char indent_char;
+
+ /// the indentation string
+ string_t indent_string;
+};
+
+template<typename BasicJsonType>
+class json_ref
+{
+ public:
+ using value_type = BasicJsonType;
+
+ json_ref(value_type&& value)
+ : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
+ {}
+
+ json_ref(const value_type& value)
+ : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
+ {}
+
+ json_ref(std::initializer_list<json_ref> init)
+ : owned_value(init), value_ref(&owned_value), is_rvalue(true)
+ {}
+
+ template<class... Args>
+ json_ref(Args&& ... args)
+ : owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
+ {}
+
+ // class should be movable only
+ json_ref(json_ref&&) = default;
+ json_ref(const json_ref&) = delete;
+ json_ref& operator=(const json_ref&) = delete;
+
+ value_type moved_or_copied() const
+ {
+ if (is_rvalue)
+ {
+ return std::move(*value_ref);
+ }
+ return *value_ref;
+ }
+
+ value_type const& operator*() const
+ {
+ return *static_cast<value_type const*>(value_ref);
+ }
+
+ value_type const* operator->() const
+ {
+ return static_cast<value_type const*>(value_ref);
+ }
+
+ private:
+ mutable value_type owned_value = nullptr;
+ value_type* value_ref = nullptr;
+ const bool is_rvalue;
+};
+
+} // namespace detail
+
+/// namespace to hold default `to_json` / `from_json` functions
+namespace
+{
+constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
+constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
}
+
+/*!
+@brief default JSONSerializer template argument
+
+This serializer ignores the template arguments and uses ADL
+([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
+for serialization.
+*/
+template<typename, typename>
+struct adl_serializer
+{
+ /*!
+ @brief convert a JSON value to any value type
+
+ This function is usually called by the `get()` function of the
+ @ref basic_json class (either explicit or via conversion operators).
+
+ @param[in] j JSON value to read from
+ @param[in,out] val value to write to
+ */
+ template<typename BasicJsonType, typename ValueType>
+ static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+ {
+ ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+ }
+
+ /*!
+ @brief convert any value type to a JSON value
+
+ This function is usually called by the constructors of the @ref basic_json
+ class.
+
+ @param[in,out] j JSON value to write to
+ @param[in] val value to read from
+ */
+ template<typename BasicJsonType, typename ValueType>
+ static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
+ noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
+ {
+ ::nlohmann::to_json(j, std::forward<ValueType>(val));
+ }
+};
+
+/*!
+@brief JSON Pointer
+
+A JSON pointer defines a string syntax for identifying a specific value
+within a JSON document. It can be used with functions `at` and
+`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+
+@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
+
+@since version 2.0.0
+*/
+class json_pointer
+{
+ /// allow basic_json to access private members
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ friend class basic_json;
+
+ public:
+ /*!
+ @brief create JSON pointer
+
+ Create a JSON pointer according to the syntax described in
+ [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
+
+ @param[in] s string representing the JSON pointer; if omitted, the empty
+ string is assumed which references the whole JSON value
+
+ @throw parse_error.107 if the given JSON pointer @a s is nonempty and
+ does not begin with a slash (`/`); see example below
+
+ @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s
+ is not followed by `0` (representing `~`) or `1` (representing `/`);
+ see example below
+
+ @liveexample{The example shows the construction several valid JSON
+ pointers as well as the exceptional behavior.,json_pointer}
+
+ @since version 2.0.0
+ */
+ explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {}
+
+ /*!
+ @brief return a string representation of the JSON pointer
+
+ @invariant For each JSON pointer `ptr`, it holds:
+ @code {.cpp}
+ ptr == json_pointer(ptr.to_string());
+ @endcode
+
+ @return a string representation of the JSON pointer
+
+ @liveexample{The example shows the result of `to_string`.,
+ json_pointer__to_string}
+
+ @since version 2.0.0
+ */
+ std::string to_string() const noexcept
+ {
+ return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+ std::string{},
+ [](const std::string & a, const std::string & b)
+ {
+ return a + "/" + escape(b);
+ });
+ }
+
+ /// @copydoc to_string()
+ operator std::string() const
+ {
+ return to_string();
+ }
+
+ /*!
+ @param[in] s reference token to be converted into an array index
+
+ @return integer representation of @a s
+
+ @throw out_of_range.404 if string @a s could not be converted to an integer
+ */
+ static int array_index(const std::string& s)
+ {
+ size_t processed_chars = 0;
+ const int res = std::stoi(s, &processed_chars);
+
+ // check if the string was completely read
+ if (JSON_UNLIKELY(processed_chars != s.size()))
+ {
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
+ }
+
+ return res;
+ }
+
+ private:
+ /*!
+ @brief remove and return last reference pointer
+ @throw out_of_range.405 if JSON pointer has no parent
+ */
+ std::string pop_back()
+ {
+ if (JSON_UNLIKELY(is_root()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
+ }
+
+ auto last = reference_tokens.back();
+ reference_tokens.pop_back();
+ return last;
+ }
+
+ /// return whether pointer points to the root document
+ bool is_root() const
+ {
+ return reference_tokens.empty();
+ }
+
+ json_pointer top() const
+ {
+ if (JSON_UNLIKELY(is_root()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
+ }
+
+ json_pointer result = *this;
+ result.reference_tokens = {reference_tokens[0]};
+ return result;
+ }
+
+ /*!
+ @brief create and return a reference to the pointed to value
+
+ @complexity Linear in the number of reference tokens.
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.313 if value cannot be unflattened
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const;
+
+ /*!
+ @brief return a reference to the pointed to value
+
+ @note This version does not throw if a value is not present, but tries to
+ create nested values instead. For instance, calling this function
+ with pointer `"/this/that"` on a null value is equivalent to calling
+ `operator[]("this").operator[]("that")` on that value, effectively
+ changing the null value to an object.
+
+ @param[in] ptr a JSON value
+
+ @return reference to the JSON value pointed to by the JSON pointer
+
+ @complexity Linear in the length of the JSON pointer.
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @brief return a const reference to the pointed to value
+
+ @param[in] ptr a JSON value
+
+ @return const reference to the JSON value pointed to by the JSON
+ pointer
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const;
+
+ /*!
+ @brief split the string input to reference tokens
+
+ @note This function is only called by the json_pointer constructor.
+ All exceptions below are documented there.
+
+ @throw parse_error.107 if the pointer is not empty or begins with '/'
+ @throw parse_error.108 if character '~' is not followed by '0' or '1'
+ */
+ static std::vector<std::string> split(const std::string& reference_string)
+ {
+ std::vector<std::string> result;
+
+ // special case: empty reference string -> no reference tokens
+ if (reference_string.empty())
+ {
+ return result;
+ }
+
+ // check if nonempty reference string begins with slash
+ if (JSON_UNLIKELY(reference_string[0] != '/'))
+ {
+ JSON_THROW(detail::parse_error::create(107, 1,
+ "JSON pointer must be empty or begin with '/' - was: '" +
+ reference_string + "'"));
+ }
+
+ // extract the reference tokens:
+ // - slash: position of the last read slash (or end of string)
+ // - start: position after the previous slash
+ for (
+ // search for the first slash after the first character
+ std::size_t slash = reference_string.find_first_of('/', 1),
+ // set the beginning of the first reference token
+ start = 1;
+ // we can stop if start == string::npos+1 = 0
+ start != 0;
+ // set the beginning of the next reference token
+ // (will eventually be 0 if slash == std::string::npos)
+ start = slash + 1,
+ // find next slash
+ slash = reference_string.find_first_of('/', start))
+ {
+ // use the text between the beginning of the reference token
+ // (start) and the last slash (slash).
+ auto reference_token = reference_string.substr(start, slash - start);
+
+ // check reference tokens are properly escaped
+ for (std::size_t pos = reference_token.find_first_of('~');
+ pos != std::string::npos;
+ pos = reference_token.find_first_of('~', pos + 1))
+ {
+ assert(reference_token[pos] == '~');
+
+ // ~ must be followed by 0 or 1
+ if (JSON_UNLIKELY(pos == reference_token.size() - 1 or
+ (reference_token[pos + 1] != '0' and
+ reference_token[pos + 1] != '1')))
+ {
+ JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
+ }
+ }
+
+ // finally, store the reference token
+ unescape(reference_token);
+ result.push_back(reference_token);
+ }
+
+ return result;
+ }
+
+ /*!
+ @brief replace all occurrences of a substring by another string
+
+ @param[in,out] s the string to manipulate; changed so that all
+ occurrences of @a f are replaced with @a t
+ @param[in] f the substring to replace with @a t
+ @param[in] t the string to replace @a f
+
+ @pre The search string @a f must not be empty. **This precondition is
+ enforced with an assertion.**
+
+ @since version 2.0.0
+ */
+ static void replace_substring(std::string& s, const std::string& f,
+ const std::string& t)
+ {
+ assert(not f.empty());
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != std::string::npos; // make sure f was found
+ s.replace(pos, f.size(), t), // replace with t, and
+ pos = s.find(f, pos + t.size())) // find next occurrence of f
+ {}
+ }
+
+ /// escape "~"" to "~0" and "/" to "~1"
+ static std::string escape(std::string s)
+ {
+ replace_substring(s, "~", "~0");
+ replace_substring(s, "/", "~1");
+ return s;
+ }
+
+ /// unescape "~1" to tilde and "~0" to slash (order is important!)
+ static void unescape(std::string& s)
+ {
+ replace_substring(s, "~1", "/");
+ replace_substring(s, "~0", "~");
+ }
+
+ /*!
+ @param[in] reference_string the reference string to the current value
+ @param[in] value the value to consider
+ @param[in,out] result the result object to insert values to
+
+ @note Empty objects or arrays are flattened to `null`.
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ static void flatten(const std::string& reference_string,
+ const NLOHMANN_BASIC_JSON_TPL& value,
+ NLOHMANN_BASIC_JSON_TPL& result);
+
+ /*!
+ @param[in] value flattened JSON
+
+ @return unflattened JSON
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.314 if value is not an object
+ @throw type_error.315 if object values are not primitive
+ @throw type_error.313 if value cannot be unflattened
+ */
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ static NLOHMANN_BASIC_JSON_TPL
+ unflatten(const NLOHMANN_BASIC_JSON_TPL& value);
+
+ friend bool operator==(json_pointer const& lhs,
+ json_pointer const& rhs) noexcept;
+
+ friend bool operator!=(json_pointer const& lhs,
+ json_pointer const& rhs) noexcept;
+
+ /// the reference tokens
+ std::vector<std::string> reference_tokens;
+};
+
/*!
@brief a class to store JSON values
@@ -122,11 +7300,14 @@ default; will be used in @ref number_integer_t)
default; will be used in @ref number_float_t)
@tparam AllocatorType type of the allocator to use (`std::allocator` by
default)
+@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`
+and `from_json()` (@ref adl_serializer by default)
@requirement The class satisfies the following concept requirements:
- Basic
- [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible):
- JSON values can be default constructed. The result will be a JSON null value.
+ JSON values can be default constructed. The result will be a JSON null
+ value.
- [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible):
A JSON value can be constructed from an rvalue argument.
- [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible):
@@ -141,8 +7322,8 @@ default)
- [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType):
JSON values have
[standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout):
- All non-static data members are private and standard layout types, the class
- has no virtual functions or (virtual) base classes.
+ All non-static data members are private and standard layout types, the
+ class has no virtual functions or (virtual) base classes.
- Library-wide
- [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable):
JSON values can be compared with `==`, see @ref
@@ -163,6 +7344,13 @@ default)
JSON values can be used like STL containers and provide reverse iterator
access.
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
@internal
@note ObjectType trick from http://stackoverflow.com/a/9860911
@endinternal
@@ -174,39 +7362,85 @@ Format](http://rfc7159.net/rfc7159)
@nosubgrouping
*/
-template <
- template<typename U, typename V, typename... Args> class ObjectType = std::map,
- template<typename U, typename... Args> class ArrayType = std::vector,
- class StringType = std::string,
- class BooleanType = bool,
- class NumberIntegerType = std::int64_t,
- class NumberUnsignedType = std::uint64_t,
- class NumberFloatType = double,
- template<typename U> class AllocatorType = std::allocator
- >
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
class basic_json
{
private:
+ template<detail::value_t> friend struct detail::external_constructor;
+ friend ::nlohmann::json_pointer;
+ friend ::nlohmann::detail::parser<basic_json>;
+ friend ::nlohmann::detail::serializer<basic_json>;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::iter_impl;
+ template<typename BasicJsonType, typename CharType>
+ friend class ::nlohmann::detail::binary_writer;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::binary_reader;
+
/// workaround type for MSVC
- using basic_json_t = basic_json<ObjectType,
- ArrayType,
- StringType,
- BooleanType,
- NumberIntegerType,
- NumberUnsignedType,
- NumberFloatType,
- AllocatorType>;
+ using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+
+ // convenience aliases for types residing in namespace detail;
+ using lexer = ::nlohmann::detail::lexer<basic_json>;
+ using parser = ::nlohmann::detail::parser<basic_json>;
+
+ using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
+ template<typename BasicJsonType>
+ using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
+ template<typename BasicJsonType>
+ using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
+ template<typename Iterator>
+ using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
+ template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
+
+ template<typename CharType>
+ using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
+
+ using binary_reader = ::nlohmann::detail::binary_reader<basic_json>;
+ template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+
+ using serializer = ::nlohmann::detail::serializer<basic_json>;
public:
- // forward declarations
- template<typename Base> class json_reverse_iterator;
- class json_pointer;
+ using value_t = detail::value_t;
+ /// @copydoc nlohmann::json_pointer
+ using json_pointer = ::nlohmann::json_pointer;
+ template<typename T, typename SFINAE>
+ using json_serializer = JSONSerializer<T, SFINAE>;
+ /// helper type for initializer lists of basic_json values
+ using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+ ////////////////
+ // exceptions //
+ ////////////////
+
+ /// @name exceptions
+ /// Classes to implement user-defined exceptions.
+ /// @{
+
+ /// @copydoc detail::exception
+ using exception = detail::exception;
+ /// @copydoc detail::parse_error
+ using parse_error = detail::parse_error;
+ /// @copydoc detail::invalid_iterator
+ using invalid_iterator = detail::invalid_iterator;
+ /// @copydoc detail::type_error
+ using type_error = detail::type_error;
+ /// @copydoc detail::out_of_range
+ using out_of_range = detail::out_of_range;
+ /// @copydoc detail::other_error
+ using other_error = detail::other_error;
+
+ /// @}
+
/////////////////////
// container types //
/////////////////////
/// @name container types
+ /// The canonic container types to use @ref basic_json like any other STL
+ /// container.
/// @{
/// the type of elements in a basic_json container
@@ -231,9 +7465,9 @@ class basic_json
using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
/// an iterator for a basic_json container
- class iterator;
+ using iterator = iter_impl<basic_json>;
/// a const iterator for a basic_json container
- class const_iterator;
+ using const_iterator = iter_impl<const basic_json>;
/// a reverse iterator for a basic_json container
using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
/// a const reverse iterator for a basic_json container
@@ -250,14 +7484,102 @@ class basic_json
return allocator_type();
}
+ /*!
+ @brief returns version information on the library
+
+ This function returns a JSON object with information about the library,
+ including the version number and information on the platform and compiler.
+
+ @return JSON object holding version information
+ key | description
+ ----------- | ---------------
+ `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).
+ `copyright` | The copyright line for the library as string.
+ `name` | The name of the library as string.
+ `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.
+ `url` | The URL of the project as string.
+ `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).
+
+ @liveexample{The following code shows an example output of the `meta()`
+ function.,meta}
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
+ @complexity Constant.
+
+ @since 2.1.0
+ */
+ static basic_json meta()
+ {
+ basic_json result;
+
+ result["copyright"] = "(C) 2013-2017 Niels Lohmann";
+ result["name"] = "JSON for Modern C++";
+ result["url"] = "https://github.com/nlohmann/json";
+ result["version"] =
+ {
+ {"string", "3.0.1"}, {"major", 3}, {"minor", 0}, {"patch", 1}
+ };
+
+#ifdef _WIN32
+ result["platform"] = "win32";
+#elif defined __linux__
+ result["platform"] = "linux";
+#elif defined __APPLE__
+ result["platform"] = "apple";
+#elif defined __unix__
+ result["platform"] = "unix";
+#else
+ result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+ result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+ result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}};
+#elif defined(__HP_cc) || defined(__HP_aCC)
+ result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+ result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+ result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+ result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+ result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+ result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#ifdef __cplusplus
+ result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+ result["compiler"]["c++"] = "unknown";
+#endif
+ return result;
+ }
+
///////////////////////////
// JSON value data types //
///////////////////////////
/// @name JSON value data types
+ /// The data types to store a JSON value. These types are derived from
+ /// the template arguments passed to class @ref basic_json.
/// @{
+#if defined(JSON_HAS_CPP_14)
+ // Use transparent comparator if possible, combined with perfect forwarding
+ // on find() and count() calls prevents unnecessary string construction.
+ using object_comparator_t = std::less<>;
+#else
+ using object_comparator_t = std::less<StringType>;
+#endif
+
/*!
@brief a type for an object
@@ -318,7 +7640,7 @@ class basic_json
[RFC 7159](http://rfc7159.net/rfc7159) specifies:
> An implementation may set limits on the maximum depth of nesting.
- In this class, the object's limit of nesting is not constraint explicitly.
+ In this class, the object's limit of nesting is not explicitly constrained.
However, a maximum depth of nesting may be introduced by the compiler or
runtime environment. A theoretical limit can be queried by calling the
@ref max_size function of a JSON object.
@@ -343,7 +7665,7 @@ class basic_json
*/
using object_t = ObjectType<StringType,
basic_json,
- std::less<StringType>,
+ object_comparator_t,
AllocatorType<std::pair<const StringType,
basic_json>>>;
@@ -358,7 +7680,7 @@ class basic_json
@tparam ArrayType container type to store arrays (e.g., `std::vector` or
`std::list`)
- @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
+ @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
#### Default type
@@ -377,7 +7699,7 @@ class basic_json
[RFC 7159](http://rfc7159.net/rfc7159) specifies:
> An implementation may set limits on the maximum depth of nesting.
- In this class, the array's limit of nesting is not constraint explicitly.
+ In this class, the array's limit of nesting is not explicitly constrained.
However, a maximum depth of nesting may be introduced by the compiler or
runtime environment. A theoretical limit can be queried by calling the
@ref max_size function of a JSON array.
@@ -415,6 +7737,12 @@ class basic_json
std::string
@endcode
+ #### Encoding
+
+ Strings are stored in UTF-8 encoding. Therefore, functions like
+ `std::string::size()` or `std::string::length()` return the number of
+ bytes in the string rather than the number of characters or glyphs.
+
#### String comparison
[RFC 7159](http://rfc7159.net/rfc7159) states:
@@ -595,15 +7923,14 @@ class basic_json
> that implementations will agree exactly on their numeric values.
As this range is a subrange (when considered in conjunction with the
- number_integer_t type) of the exactly supported range [0, UINT64_MAX], this
- class's integer type is interoperable.
+ number_integer_t type) of the exactly supported range [0, UINT64_MAX],
+ this class's integer type is interoperable.
#### Storage
Integer number values are stored directly inside a @ref basic_json type.
@sa @ref number_float_t -- type for number values (floating-point)
-
@sa @ref number_integer_t -- type for number values (integer)
@since version 2.0.0
@@ -680,115 +8007,22 @@ class basic_json
/// @}
-
- ///////////////////////////
- // JSON type enumeration //
- ///////////////////////////
-
- /*!
- @brief the JSON type enumeration
-
- This enumeration collects the different JSON types. It is internally used
- to distinguish the stored values, and the functions @ref is_null(), @ref
- is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref
- is_number(), and @ref is_discarded() rely on it.
-
- @since version 1.0.0
- */
- enum class value_t : uint8_t
- {
- null, ///< null value
- object, ///< object (unordered set of name/value pairs)
- array, ///< array (ordered collection of values)
- string, ///< string value
- boolean, ///< boolean value
- number_integer, ///< number value (integer)
- number_unsigned, ///< number value (unsigned integer)
- number_float, ///< number value (floating-point)
- discarded ///< discarded by the the parser callback function
- };
-
-
private:
- /*!
- @brief a type to hold JSON type information
-
- This bitfield type holds information about JSON types. It is internally
- used to hold the basic JSON type enumeration, as well as additional
- information in the case of values that have been parsed from a string
- including whether of not it was created directly or parsed, and in the
- case of floating point numbers the number of significant figures in the
- original representaiton and if it was in exponential form, if a '+' was
- included in the exponent and the capitilization of the exponent marker.
- The sole purpose of this information is to permit accurate round trips.
-
- @since version 2.0.0
- */
- union type_data_t
- {
- struct
- {
- /// the type of the value (@ref value_t)
- uint16_t type : 4;
- /// whether the number was parsed from a string
- uint16_t parsed : 1;
- /// whether parsed number contained an exponent ('e'/'E')
- uint16_t has_exp : 1;
- /// whether parsed number contained a plus in the exponent
- uint16_t exp_plus : 1;
- /// whether parsed number's exponent was capitalized ('E')
- uint16_t exp_cap : 1;
- /// the number of figures for a parsed number
- uint16_t precision : 8;
- } bits;
- uint16_t data;
-
- /// return the type as value_t
- operator value_t() const
- {
- return static_cast<value_t>(bits.type);
- }
-
- /// test type for equality (ignore other fields)
- bool operator==(const value_t& rhs) const
- {
- return static_cast<value_t>(bits.type) == rhs;
- }
-
- /// assignment
- type_data_t& operator=(value_t rhs)
- {
- bits.type = static_cast<uint16_t>(rhs) & 15; // avoid overflow
- return *this;
- }
-
- /// construct from value_t
- type_data_t(value_t t) noexcept
- {
- *reinterpret_cast<uint16_t*>(this) = 0;
- bits.type = static_cast<uint16_t>(t) & 15; // avoid overflow
- }
-
- /// default constructor
- type_data_t() noexcept
- {
- data = 0;
- bits.type = reinterpret_cast<uint16_t>(value_t::null);
- }
- };
-
/// helper for exception-safe object creation
template<typename T, typename... Args>
static T* create(Args&& ... args)
{
AllocatorType<T> alloc;
+ using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
+
auto deleter = [&](T * object)
{
- alloc.deallocate(object, 1);
+ AllocatorTraits::deallocate(alloc, object, 1);
};
- std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
- alloc.construct(object.get(), std::forward<Args>(args)...);
+ std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);
+ AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);
+ assert(object != nullptr);
return object.release();
}
@@ -799,7 +8033,24 @@ class basic_json
/*!
@brief a JSON value
- The actual storage for a JSON value of the @ref basic_json class.
+ The actual storage for a JSON value of the @ref basic_json class. This
+ union combines the different storage types for the JSON value types
+ defined in @ref value_t.
+
+ JSON type | value_t type | used type
+ --------- | --------------- | ------------------------
+ object | object | pointer to @ref object_t
+ array | array | pointer to @ref array_t
+ string | string | pointer to @ref string_t
+ boolean | boolean | @ref boolean_t
+ number | number_integer | @ref number_integer_t
+ number | number_unsigned | @ref number_unsigned_t
+ number | number_float | @ref number_float_t
+ null | null | *no value is stored*
+
+ @note Variable-length types (objects, arrays, and strings) are stored as
+ pointers. The size of the union should not exceed 64 bits if the default
+ value types are used.
@since version 1.0.0
*/
@@ -877,8 +8128,19 @@ class basic_json
break;
}
+ case value_t::null:
+ {
+ object = nullptr; // silence warning, see #821
+ break;
+ }
+
default:
{
+ object = nullptr; // silence warning, see #821
+ if (JSON_UNLIKELY(t == value_t::null))
+ {
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.0.1")); // LCOV_EXCL_LINE
+ }
break;
}
}
@@ -890,19 +8152,87 @@ class basic_json
string = create<string_t>(value);
}
+ /// constructor for rvalue strings
+ json_value(string_t&& value)
+ {
+ string = create<string_t>(std::move(value));
+ }
+
/// constructor for objects
json_value(const object_t& value)
{
object = create<object_t>(value);
}
+ /// constructor for rvalue objects
+ json_value(object_t&& value)
+ {
+ object = create<object_t>(std::move(value));
+ }
+
/// constructor for arrays
json_value(const array_t& value)
{
array = create<array_t>(value);
}
+
+ /// constructor for rvalue arrays
+ json_value(array_t&& value)
+ {
+ array = create<array_t>(std::move(value));
+ }
+
+ void destroy(value_t t)
+ {
+ switch (t)
+ {
+ case value_t::object:
+ {
+ AllocatorType<object_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ AllocatorType<array_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+ break;
+ }
+
+ case value_t::string:
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
};
+ /*!
+ @brief checks the class invariants
+
+ This function asserts the class invariants. It needs to be called at the
+ end of every constructor to make sure that created objects respect the
+ invariant. Furthermore, it has to be called each time the type of a JSON
+ value is changed, because the invariant expresses a relationship between
+ @a m_type and @a m_value.
+ */
+ void assert_invariant() const
+ {
+ assert(m_type != value_t::object or m_value.object != nullptr);
+ assert(m_type != value_t::array or m_value.array != nullptr);
+ assert(m_type != value_t::string or m_value.string != nullptr);
+ }
public:
//////////////////////////
@@ -910,39 +8240,31 @@ class basic_json
//////////////////////////
/*!
- @brief JSON callback events
+ @brief parser event types
- This enumeration lists the parser events that can trigger calling a
- callback function of type @ref parser_callback_t during parsing.
+ The parser callback distinguishes the following events:
+ - `object_start`: the parser read `{` and started to process a JSON object
+ - `key`: the parser read a key of a value in an object
+ - `object_end`: the parser read `}` and finished processing a JSON object
+ - `array_start`: the parser read `[` and started to process a JSON array
+ - `array_end`: the parser read `]` and finished processing a JSON array
+ - `value`: the parser finished reading a JSON value
- @since version 1.0.0
+ @image html callback_events.png "Example when certain parse events are triggered"
+
+ @sa @ref parser_callback_t for more information and examples
*/
- enum class parse_event_t : uint8_t
- {
- /// the parser read `{` and started to process a JSON object
- object_start,
- /// the parser read `}` and finished processing a JSON object
- object_end,
- /// the parser read `[` and started to process a JSON array
- array_start,
- /// the parser read `]` and finished processing a JSON array
- array_end,
- /// the parser read a key of a value in an object
- key,
- /// the parser finished reading a JSON value
- value
- };
+ using parse_event_t = typename parser::parse_event_t;
/*!
@brief per-element parser callback type
With a parser callback function, the result of parsing a JSON text can be
- influenced. When passed to @ref parse(std::istream&, parser_callback_t) or
- @ref parse(const string_t&, parser_callback_t), it is called on certain
- events (passed as @ref parse_event_t via parameter @a event) with a set
- recursion depth @a depth and context JSON value @a parsed. The return
- value of the callback function is a boolean indicating whether the element
- that emitted the callback shall be kept or not.
+ influenced. When passed to @ref parse, it is called on certain events
+ (passed as @ref parse_event_t via parameter @a event) with a set recursion
+ depth @a depth and context JSON value @a parsed. The return value of the
+ callback function is a boolean indicating whether the element that emitted
+ the callback shall be kept or not.
We distinguish six scenarios (determined by the event type) in which the
callback function can be called. The following table describes the values
@@ -957,6 +8279,8 @@ class basic_json
parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array
parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value
+ @image html callback_events.png "Example when certain parse events are triggered"
+
Discarding a value (i.e., returning `false`) has different effects
depending on the context in which function was called:
@@ -977,12 +8301,11 @@ class basic_json
should be kept (`true`) or not (`false`). In the latter case, it is either
skipped completely or replaced by an empty discarded object.
- @sa @ref parse(std::istream&, parser_callback_t) or
- @ref parse(const string_t&, parser_callback_t) for examples
+ @sa @ref parse for examples
@since version 1.0.0
*/
- using parser_callback_t = std::function<bool(int depth, parse_event_t event, basic_json& parsed)>;
+ using parser_callback_t = typename parser::parser_callback_t;
//////////////////
@@ -990,6 +8313,8 @@ class basic_json
//////////////////
/// @name constructors and destructors
+ /// Constructors of class @ref basic_json, copy/move constructor, copy
+ /// assignment, static functions creating objects, and the destructor.
/// @{
/*!
@@ -1007,66 +8332,31 @@ class basic_json
object | `{}`
array | `[]`
- @param[in] value_type the type of the value to create
+ @param[in] v the type of the value to create
@complexity Constant.
- @throw std::bad_alloc if allocation for object, array, or string value
- fails
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
@liveexample{The following code shows the constructor for different @ref
value_t values,basic_json__value_t}
- @sa @ref basic_json(std::nullptr_t) -- create a `null` value
- @sa @ref basic_json(boolean_t value) -- create a boolean value
- @sa @ref basic_json(const string_t&) -- create a string value
- @sa @ref basic_json(const object_t&) -- create a object value
- @sa @ref basic_json(const array_t&) -- create a array value
- @sa @ref basic_json(const number_float_t) -- create a number
- (floating-point) value
- @sa @ref basic_json(const number_integer_t) -- create a number (integer)
- value
- @sa @ref basic_json(const number_unsigned_t) -- create a number (unsigned)
- value
+ @sa @ref clear() -- restores the postcondition of this constructor
@since version 1.0.0
*/
- basic_json(const value_t value_type)
- : m_type(value_type), m_value(value_type)
- {}
-
- /*!
- @brief create a null object (implicitly)
-
- Create a `null` JSON value. This is the implicit version of the `null`
- value constructor as it takes no parameters.
-
- @complexity Constant.
-
- @exceptionsafety No-throw guarantee: this constructor never throws
- exceptions.
-
- @requirement This function helps `basic_json` satisfying the
- [Container](http://en.cppreference.com/w/cpp/concept/Container)
- requirements:
- - The complexity is constant.
- - As postcondition, it holds: `basic_json().empty() == true`.
-
- @liveexample{The following code shows the constructor for a `null` JSON
- value.,basic_json}
-
- @sa @ref basic_json(std::nullptr_t) -- create a `null` value
-
- @since version 1.0.0
- */
- basic_json() = default;
+ basic_json(const value_t v)
+ : m_type(v), m_value(v)
+ {
+ assert_invariant();
+ }
/*!
- @brief create a null object (explicitly)
+ @brief create a null object
- Create a `null` JSON value. This is the explicitly version of the `null`
- value constructor as it takes a null pointer as parameter. It allows to
- create `null` values by explicitly assigning a `nullptr` to a JSON value.
+ Create a `null` JSON value. It either takes a null pointer as parameter
+ (explicitly creating `null`) or no parameter (implicitly creating `null`).
The passed null pointer itself is not read -- it is only used to choose
the right constructor.
@@ -1075,480 +8365,88 @@ class basic_json
@exceptionsafety No-throw guarantee: this constructor never throws
exceptions.
- @liveexample{The following code shows the constructor with null pointer
- parameter.,basic_json__nullptr_t}
-
- @sa @ref basic_json() -- default constructor (implicitly creating a `null`
- value)
+ @liveexample{The following code shows the constructor with and without a
+ null pointer parameter.,basic_json__nullptr_t}
@since version 1.0.0
*/
- basic_json(std::nullptr_t) noexcept
+ basic_json(std::nullptr_t = nullptr) noexcept
: basic_json(value_t::null)
- {}
-
- /*!
- @brief create an object (explicit)
-
- Create an object JSON value with a given content.
-
- @param[in] val a value for the object
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for object value fails
-
- @liveexample{The following code shows the constructor with an @ref
- object_t parameter.,basic_json__object_t}
-
- @sa @ref basic_json(const CompatibleObjectType&) -- create an object value
- from a compatible STL container
-
- @since version 1.0.0
- */
- basic_json(const object_t& val)
- : m_type(value_t::object), m_value(val)
- {}
-
- /*!
- @brief create an object (implicit)
-
- Create an object JSON value with a given content. This constructor allows
- any type @a CompatibleObjectType that can be used to construct values of
- type @ref object_t.
-
- @tparam CompatibleObjectType An object type whose `key_type` and
- `value_type` is compatible to @ref object_t. Examples include `std::map`,
- `std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with
- a `key_type` of `std::string`, and a `value_type` from which a @ref
- basic_json value can be constructed.
-
- @param[in] val a value for the object
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for object value fails
-
- @liveexample{The following code shows the constructor with several
- compatible object type parameters.,basic_json__CompatibleObjectType}
-
- @sa @ref basic_json(const object_t&) -- create an object value
-
- @since version 1.0.0
- */
- template <class CompatibleObjectType, typename
- std::enable_if<
- std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
- std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
- = 0>
- basic_json(const CompatibleObjectType& val)
- : m_type(value_t::object)
{
- using std::begin;
- using std::end;
- m_value.object = create<object_t>(begin(val), end(val));
+ assert_invariant();
}
/*!
- @brief create an array (explicit)
-
- Create an array JSON value with a given content.
-
- @param[in] val a value for the array
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for array value fails
+ @brief create a JSON value
- @liveexample{The following code shows the constructor with an @ref array_t
- parameter.,basic_json__array_t}
+ This is a "catch all" constructor for all compatible JSON types; that is,
+ types for which a `to_json()` method exists. The constructor forwards the
+ parameter @a val to that method (to `json_serializer<U>::to_json` method
+ with `U = uncvref_t<CompatibleType>`, to be exact).
- @sa @ref basic_json(const CompatibleArrayType&) -- create an array value
- from a compatible STL containers
+ Template type @a CompatibleType includes, but is not limited to, the
+ following types:
+ - **arrays**: @ref array_t and all kinds of compatible containers such as
+ `std::vector`, `std::deque`, `std::list`, `std::forward_list`,
+ `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,
+ `std::multiset`, and `std::unordered_multiset` with a `value_type` from
+ which a @ref basic_json value can be constructed.
+ - **objects**: @ref object_t and all kinds of compatible associative
+ containers such as `std::map`, `std::unordered_map`, `std::multimap`,
+ and `std::unordered_multimap` with a `key_type` compatible to
+ @ref string_t and a `value_type` from which a @ref basic_json value can
+ be constructed.
+ - **strings**: @ref string_t, string literals, and all compatible string
+ containers can be used.
+ - **numbers**: @ref number_integer_t, @ref number_unsigned_t,
+ @ref number_float_t, and all convertible number types such as `int`,
+ `size_t`, `int64_t`, `float` or `double` can be used.
+ - **boolean**: @ref boolean_t / `bool` can be used.
- @since version 1.0.0
- */
- basic_json(const array_t& val)
- : m_type(value_t::array), m_value(val)
- {}
+ See the examples below.
- /*!
- @brief create an array (implicit)
-
- Create an array JSON value with a given content. This constructor allows
- any type @a CompatibleArrayType that can be used to construct values of
- type @ref array_t.
+ @tparam CompatibleType a type such that:
+ - @a CompatibleType is not derived from `std::istream`,
+ - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
+ constructors),
+ - @a CompatibleType is not a @ref basic_json nested type (e.g.,
+ @ref json_pointer, @ref iterator, etc ...)
+ - @ref @ref json_serializer<U> has a
+ `to_json(basic_json_t&, CompatibleType&&)` method
- @tparam CompatibleArrayType An object type whose `value_type` is
- compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
- `std::list`, `std::forward_list`, `std::array`, `std::set`,
- `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
- `value_type` from which a @ref basic_json value can be constructed.
+ @tparam U = `uncvref_t<CompatibleType>`
- @param[in] val a value for the array
+ @param[in] val the value to be forwarded to the respective constructor
- @complexity Linear in the size of the passed @a val.
+ @complexity Usually linear in the size of the passed @a val, also
+ depending on the implementation of the called `to_json()`
+ method.
- @throw std::bad_alloc if allocation for array value fails
+ @exceptionsafety Depends on the called constructor. For types directly
+ supported by the library (i.e., all types for which no `to_json()` function
+ was provided), strong guarantee holds: if an exception is thrown, there are
+ no changes to any JSON value.
@liveexample{The following code shows the constructor with several
- compatible array type parameters.,basic_json__CompatibleArrayType}
-
- @sa @ref basic_json(const array_t&) -- create an array value
+ compatible types.,basic_json__CompatibleType}
- @since version 1.0.0
+ @since version 2.1.0
*/
- template <class CompatibleArrayType, typename
- std::enable_if<
- not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
- not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
- not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
- not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
- std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
- = 0>
- basic_json(const CompatibleArrayType& val)
- : m_type(value_t::array)
+ template<typename CompatibleType, typename U = detail::uncvref_t<CompatibleType>,
+ detail::enable_if_t<not std::is_base_of<std::istream, U>::value and
+ not std::is_same<U, basic_json_t>::value and
+ not detail::is_basic_json_nested_type<
+ basic_json_t, U>::value and
+ detail::has_to_json<basic_json, U>::value,
+ int> = 0>
+ basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer<U>::to_json(
+ std::declval<basic_json_t&>(), std::forward<CompatibleType>(val))))
{
- using std::begin;
- using std::end;
- m_value.array = create<array_t>(begin(val), end(val));
+ JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+ assert_invariant();
}
/*!
- @brief create a string (explicit)
-
- Create an string JSON value with a given content.
-
- @param[in] val a value for the string
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for string value fails
-
- @liveexample{The following code shows the constructor with an @ref
- string_t parameter.,basic_json__string_t}
-
- @sa @ref basic_json(const typename string_t::value_type*) -- create a
- string value from a character pointer
- @sa @ref basic_json(const CompatibleStringType&) -- create a string value
- from a compatible string container
-
- @since version 1.0.0
- */
- basic_json(const string_t& val)
- : m_type(value_t::string), m_value(val)
- {}
-
- /*!
- @brief create a string (explicit)
-
- Create a string JSON value with a given content.
-
- @param[in] val a literal value for the string
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for string value fails
-
- @liveexample{The following code shows the constructor with string literal
- parameter.,basic_json__string_t_value_type}
-
- @sa @ref basic_json(const string_t&) -- create a string value
- @sa @ref basic_json(const CompatibleStringType&) -- create a string value
- from a compatible string container
-
- @since version 1.0.0
- */
- basic_json(const typename string_t::value_type* val)
- : basic_json(string_t(val))
- {}
-
- /*!
- @brief create a string (implicit)
-
- Create a string JSON value with a given content.
-
- @param[in] val a value for the string
-
- @tparam CompatibleStringType an string type which is compatible to @ref
- string_t, for instance `std::string`.
-
- @complexity Linear in the size of the passed @a val.
-
- @throw std::bad_alloc if allocation for string value fails
-
- @liveexample{The following code shows the construction of a string value
- from a compatible type.,basic_json__CompatibleStringType}
-
- @sa @ref basic_json(const string_t&) -- create a string value
- @sa @ref basic_json(const typename string_t::value_type*) -- create a
- string value from a character pointer
-
- @since version 1.0.0
- */
- template <class CompatibleStringType, typename
- std::enable_if<
- std::is_constructible<string_t, CompatibleStringType>::value, int>::type
- = 0>
- basic_json(const CompatibleStringType& val)
- : basic_json(string_t(val))
- {}
-
- /*!
- @brief create a boolean (explicit)
-
- Creates a JSON boolean type from a given value.
-
- @param[in] val a boolean value to store
-
- @complexity Constant.
-
- @liveexample{The example below demonstrates boolean
- values.,basic_json__boolean_t}
-
- @since version 1.0.0
- */
- basic_json(boolean_t val) noexcept
- : m_type(value_t::boolean), m_value(val)
- {}
-
- /*!
- @brief create an integer number (explicit)
-
- Create an integer number JSON value with a given content.
-
- @tparam T A helper type to remove this function via SFINAE in case @ref
- number_integer_t is the same as `int`. In this case, this constructor
- would have the same signature as @ref basic_json(const int value). Note
- the helper type @a T is not visible in this constructor's interface.
-
- @param[in] val an integer to create a JSON number from
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of an integer
- number value.,basic_json__number_integer_t}
-
- @sa @ref basic_json(const int) -- create a number value (integer)
- @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
- value (integer) from a compatible number type
-
- @since version 1.0.0
- */
- template<typename T,
- typename std::enable_if<
- not (std::is_same<T, int>::value)
- and std::is_same<T, number_integer_t>::value
- , int>::type
- = 0>
- basic_json(const number_integer_t val) noexcept
- : m_type(value_t::number_integer), m_value(val)
- {}
-
- /*!
- @brief create an integer number from an enum type (explicit)
-
- Create an integer number JSON value with a given content.
-
- @param[in] val an integer to create a JSON number from
-
- @note This constructor allows to pass enums directly to a constructor. As
- C++ has no way of specifying the type of an anonymous enum explicitly, we
- can only rely on the fact that such values implicitly convert to int. As
- int may already be the same type of number_integer_t, we may need to
- switch off the constructor @ref basic_json(const number_integer_t).
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of an integer
- number value from an anonymous enum.,basic_json__const_int}
-
- @sa @ref basic_json(const number_integer_t) -- create a number value
- (integer)
- @sa @ref basic_json(const CompatibleNumberIntegerType) -- create a number
- value (integer) from a compatible number type
-
- @since version 1.0.0
- */
- basic_json(const int val) noexcept
- : m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
- {}
-
- /*!
- @brief create an integer number (implicit)
-
- Create an integer number JSON value with a given content. This constructor
- allows any type @a CompatibleNumberIntegerType that can be used to
- construct values of type @ref number_integer_t.
-
- @tparam CompatibleNumberIntegerType An integer type which is compatible to
- @ref number_integer_t. Examples include the types `int`, `int32_t`,
- `long`, and `short`.
-
- @param[in] val an integer to create a JSON number from
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of several integer
- number values from compatible
- types.,basic_json__CompatibleIntegerNumberType}
-
- @sa @ref basic_json(const number_integer_t) -- create a number value
- (integer)
- @sa @ref basic_json(const int) -- create a number value (integer)
-
- @since version 1.0.0
- */
- template<typename CompatibleNumberIntegerType, typename
- std::enable_if<
- std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
- std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
- std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
- CompatibleNumberIntegerType>::type
- = 0>
- basic_json(const CompatibleNumberIntegerType val) noexcept
- : m_type(value_t::number_integer),
- m_value(static_cast<number_integer_t>(val))
- {}
-
- /*!
- @brief create an unsigned integer number (explicit)
-
- Create an unsigned integer number JSON value with a given content.
-
- @tparam T helper type to compare number_unsigned_t and unsigned int
- (not visible in) the interface.
-
- @param[in] val an integer to create a JSON number from
-
- @complexity Constant.
-
- @sa @ref basic_json(const CompatibleNumberUnsignedType) -- create a number
- value (unsigned integer) from a compatible number type
-
- @since version 2.0.0
- */
- template<typename T,
- typename std::enable_if<
- not (std::is_same<T, int>::value)
- and std::is_same<T, number_unsigned_t>::value
- , int>::type
- = 0>
- basic_json(const number_unsigned_t val) noexcept
- : m_type(value_t::number_unsigned), m_value(val)
- {}
-
- /*!
- @brief create an unsigned number (implicit)
-
- Create an unsigned number JSON value with a given content. This
- constructor allows any type @a CompatibleNumberUnsignedType that can be
- used to construct values of type @ref number_unsigned_t.
-
- @tparam CompatibleNumberUnsignedType An integer type which is compatible
- to @ref number_unsigned_t. Examples may include the types `unsigned int`,
- `uint32_t`, or `unsigned short`.
-
- @param[in] val an unsigned integer to create a JSON number from
-
- @complexity Constant.
-
- @sa @ref basic_json(const number_unsigned_t) -- create a number value
- (unsigned)
-
- @since version 2.0.0
- */
- template <typename CompatibleNumberUnsignedType, typename
- std::enable_if <
- std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
- std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
- not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
- CompatibleNumberUnsignedType>::type
- = 0>
- basic_json(const CompatibleNumberUnsignedType val) noexcept
- : m_type(value_t::number_unsigned),
- m_value(static_cast<number_unsigned_t>(val))
- {}
-
- /*!
- @brief create a floating-point number (explicit)
-
- Create a floating-point number JSON value with a given content.
-
- @param[in] val a floating-point value to create a JSON number from
-
- @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
- disallows NaN values:
- > Numeric values that cannot be represented in the grammar below (such as
- > Infinity and NaN) are not permitted.
- In case the parameter @a val is not a number, a JSON null value is
- created instead.
-
- @complexity Constant.
-
- @liveexample{The following example creates several floating-point
- values.,basic_json__number_float_t}
-
- @sa @ref basic_json(const CompatibleNumberFloatType) -- create a number
- value (floating-point) from a compatible number type
-
- @since version 1.0.0
- */
- basic_json(const number_float_t val) noexcept
- : m_type(value_t::number_float), m_value(val)
- {
- // replace infinity and NAN by null
- if (not std::isfinite(val))
- {
- m_type = value_t::null;
- m_value = json_value();
- }
- }
-
- /*!
- @brief create an floating-point number (implicit)
-
- Create an floating-point number JSON value with a given content. This
- constructor allows any type @a CompatibleNumberFloatType that can be used
- to construct values of type @ref number_float_t.
-
- @tparam CompatibleNumberFloatType A floating-point type which is
- compatible to @ref number_float_t. Examples may include the types `float`
- or `double`.
-
- @param[in] val a floating-point to create a JSON number from
-
- @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
- disallows NaN values:
- > Numeric values that cannot be represented in the grammar below (such as
- > Infinity and NaN) are not permitted.
- In case the parameter @a val is not a number, a JSON null value is
- created instead.
-
- @complexity Constant.
-
- @liveexample{The example below shows the construction of several
- floating-point number values from compatible
- types.,basic_json__CompatibleNumberFloatType}
-
- @sa @ref basic_json(const number_float_t) -- create a number value
- (floating-point)
-
- @since version 1.0.0
- */
- template<typename CompatibleNumberFloatType, typename = typename
- std::enable_if<
- std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
- std::is_floating_point<CompatibleNumberFloatType>::value>::type
- >
- basic_json(const CompatibleNumberFloatType val) noexcept
- : basic_json(number_float_t(val))
- {}
-
- /*!
@brief create a container (array or object) from an initializer list
Creates a JSON value of type array or object from the passed initializer
@@ -1558,29 +8456,29 @@ class basic_json
1. If the list is empty, an empty JSON object value `{}` is created.
2. If the list consists of pairs whose first element is a string, a JSON
- object value is created where the first elements of the pairs are treated
- as keys and the second elements are as values.
+ object value is created where the first elements of the pairs are
+ treated as keys and the second elements are as values.
3. In all other cases, an array is created.
The rules aim to create the best fit between a C++ initializer list and
JSON values. The rationale is as follows:
1. The empty initializer list is written as `{}` which is exactly an empty
- JSON object.
- 2. C++ has now way of describing mapped types other than to list a list of
- pairs. As JSON requires that keys must be of type string, rule 2 is the
- weakest constraint one can pose on initializer lists to interpret them as
- an object.
+ JSON object.
+ 2. C++ has no way of describing mapped types other than to list a list of
+ pairs. As JSON requires that keys must be of type string, rule 2 is the
+ weakest constraint one can pose on initializer lists to interpret them
+ as an object.
3. In all other cases, the initializer list could not be interpreted as
- JSON object type, so interpreting it as JSON array type is safe.
+ JSON object type, so interpreting it as JSON array type is safe.
With the rules described above, the following JSON values cannot be
expressed by an initializer list:
- - the empty array (`[]`): use @ref array(std::initializer_list<basic_json>)
+ - the empty array (`[]`): use @ref array(initializer_list_t)
with an empty initializer list in this case
- arrays whose elements satisfy rule 2: use @ref
- array(std::initializer_list<basic_json>) with the same initializer list
+ array(initializer_list_t) with the same initializer list
in this case
@note When used without parentheses around an empty initializer list, @ref
@@ -1592,51 +8490,47 @@ class basic_json
@param[in] type_deduction internal parameter; when set to `true`, the type
of the JSON value is deducted from the initializer list @a init; when set
to `false`, the type provided via @a manual_type is forced. This mode is
- used by the functions @ref array(std::initializer_list<basic_json>) and
- @ref object(std::initializer_list<basic_json>).
+ used by the functions @ref array(initializer_list_t) and
+ @ref object(initializer_list_t).
@param[in] manual_type internal parameter; when @a type_deduction is set
to `false`, the created JSON value will use the provided type (only @ref
value_t::array and @ref value_t::object are valid); when @a type_deduction
is set to `true`, this parameter has no effect
- @throw std::domain_error if @a type_deduction is `false`, @a manual_type
- is `value_t::object`, but @a init contains an element which is not a pair
- whose first element is a string; example: `"cannot create object from
- initializer list"`
+ @throw type_error.301 if @a type_deduction is `false`, @a manual_type is
+ `value_t::object`, but @a init contains an element which is not a pair
+ whose first element is a string. In this case, the constructor could not
+ create an object. If @a type_deduction would have be `true`, an array
+ would have been created. See @ref object(initializer_list_t)
+ for an example.
@complexity Linear in the size of the initializer list @a init.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The example below shows how JSON values are created from
initializer lists.,basic_json__list_init_t}
- @sa @ref array(std::initializer_list<basic_json>) -- create a JSON array
+ @sa @ref array(initializer_list_t) -- create a JSON array
value from an initializer list
- @sa @ref object(std::initializer_list<basic_json>) -- create a JSON object
+ @sa @ref object(initializer_list_t) -- create a JSON object
value from an initializer list
@since version 1.0.0
*/
- basic_json(std::initializer_list<basic_json> init,
+ basic_json(initializer_list_t init,
bool type_deduction = true,
value_t manual_type = value_t::array)
{
- // the initializer list could describe an object
- bool is_an_object = true;
-
// check if each element is an array with two elements whose first
// element is a string
- for (const auto& element : init)
+ bool is_an_object = std::all_of(init.begin(), init.end(),
+ [](const detail::json_ref<basic_json>& element_ref)
{
- if (not element.is_array() or element.size() != 2
- or not element[0].is_string())
- {
- // we found an element that makes it impossible to use the
- // initializer list as object
- is_an_object = false;
- break;
- }
- }
+ return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());
+ });
// adjust type if type deduction is not wanted
if (not type_deduction)
@@ -1648,9 +8542,9 @@ class basic_json
}
// if object is wanted but impossible, throw an exception
- if (manual_type == value_t::object and not is_an_object)
+ if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))
{
- throw std::domain_error("cannot create object from initializer list");
+ JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
}
}
@@ -1660,19 +8554,22 @@ class basic_json
m_type = value_t::object;
m_value = value_t::object;
- assert(m_value.object != nullptr);
-
- for (auto& element : init)
+ std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)
{
- m_value.object->emplace(*(element[0].m_value.string), element[1]);
- }
+ auto element = element_ref.moved_or_copied();
+ m_value.object->emplace(
+ std::move(*((*element.m_value.array)[0].m_value.string)),
+ std::move((*element.m_value.array)[1]));
+ });
}
else
{
// the initializer list describes an array -> create array
m_type = value_t::array;
- m_value.array = create<array_t>(init);
+ m_value.array = create<array_t>(init.begin(), init.end());
}
+
+ assert_invariant();
}
/*!
@@ -1684,7 +8581,7 @@ class basic_json
@note This function is only needed to express two edge cases that cannot
be realized with the initializer list constructor (@ref
- basic_json(std::initializer_list<basic_json>, bool, value_t)). These cases
+ basic_json(initializer_list_t, bool, value_t)). These cases
are:
1. creating an array whose elements are all pairs whose first element is a
string -- in this case, the initializer list constructor would create an
@@ -1699,18 +8596,20 @@ class basic_json
@complexity Linear in the size of @a init.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The following code shows an example for the `array`
function.,array}
- @sa @ref basic_json(std::initializer_list<basic_json>, bool, value_t) --
+ @sa @ref basic_json(initializer_list_t, bool, value_t) --
create a JSON value from an initializer list
- @sa @ref object(std::initializer_list<basic_json>) -- create a JSON object
+ @sa @ref object(initializer_list_t) -- create a JSON object
value from an initializer list
@since version 1.0.0
*/
- static basic_json array(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ static basic_json array(initializer_list_t init = {})
{
return basic_json(init, false, value_t::array);
}
@@ -1723,34 +8622,37 @@ class basic_json
the initializer list is empty, the empty object `{}` is created.
@note This function is only added for symmetry reasons. In contrast to the
- related function @ref array(std::initializer_list<basic_json>), there are
+ related function @ref array(initializer_list_t), there are
no cases which can only be expressed by this function. That is, any
initializer list @a init can also be passed to the initializer list
- constructor @ref basic_json(std::initializer_list<basic_json>, bool,
- value_t).
+ constructor @ref basic_json(initializer_list_t, bool, value_t).
@param[in] init initializer list to create an object from (optional)
@return JSON object value
- @throw std::domain_error if @a init is not a pair whose first elements are
- strings; thrown by
- @ref basic_json(std::initializer_list<basic_json>, bool, value_t)
+ @throw type_error.301 if @a init is not a list of pairs whose first
+ elements are strings. In this case, no object can be created. When such a
+ value is passed to @ref basic_json(initializer_list_t, bool, value_t),
+ an array would have been created from the passed initializer list @a init.
+ See example below.
@complexity Linear in the size of @a init.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The following code shows an example for the `object`
function.,object}
- @sa @ref basic_json(std::initializer_list<basic_json>, bool, value_t) --
+ @sa @ref basic_json(initializer_list_t, bool, value_t) --
create a JSON value from an initializer list
- @sa @ref array(std::initializer_list<basic_json>) -- create a JSON array
+ @sa @ref array(initializer_list_t) -- create a JSON array
value from an initializer list
@since version 1.0.0
*/
- static basic_json object(std::initializer_list<basic_json> init =
- std::initializer_list<basic_json>())
+ static basic_json object(initializer_list_t init = {})
{
return basic_json(init, false, value_t::object);
}
@@ -1759,14 +8661,18 @@ class basic_json
@brief construct an array with count copies of given value
Constructs a JSON array value by creating @a cnt copies of a passed value.
- In case @a cnt is `0`, an empty array is created. As postcondition,
- `std::distance(begin(),end()) == cnt` holds.
+ In case @a cnt is `0`, an empty array is created.
@param[in] cnt the number of JSON copies of @a val to create
@param[in] val the JSON value to copy
+ @post `std::distance(begin(),end()) == cnt` holds.
+
@complexity Linear in @a cnt.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The following code shows examples for the @ref
basic_json(size_type\, const basic_json&)
constructor.,basic_json__size_type_basic_json}
@@ -1777,6 +8683,7 @@ class basic_json
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
+ assert_invariant();
}
/*!
@@ -1784,12 +8691,13 @@ class basic_json
Constructs the JSON value with the contents of the range `[first, last)`.
The semantics depends on the different types a JSON value can have:
- - In case of primitive types (number, boolean, or string), @a first must
- be `begin()` and @a last must be `end()`. In this case, the value is
- copied. Otherwise, std::out_of_range is thrown.
+ - In case of a null type, invalid_iterator.206 is thrown.
+ - In case of other primitive types (number, boolean, or string), @a first
+ must be `begin()` and @a last must be `end()`. In this case, the value is
+ copied. Otherwise, invalid_iterator.204 is thrown.
- In case of structured types (array, object), the constructor behaves as
- similar versions for `std::vector`.
- - In case of a null type, std::domain_error is thrown.
+ similar versions for `std::vector` or `std::map`; that is, a JSON array
+ or object is constructed from the values in the range.
@tparam InputIT an input iterator type (@ref iterator or @ref
const_iterator)
@@ -1797,36 +8705,59 @@ class basic_json
@param[in] first begin of the range to copy from (included)
@param[in] last end of the range to copy from (excluded)
- @throw std::domain_error if iterators are not compatible; that is, do not
- belong to the same JSON value; example: `"iterators are not compatible"`
- @throw std::out_of_range if iterators are for a primitive type (number,
- boolean, or string) where an out of range error can be detected easily;
- example: `"iterators out of range"`
- @throw std::bad_alloc if allocation for object, array, or string fails
- @throw std::domain_error if called with a null value; example: `"cannot
- use construct with iterators from null"`
+ @pre Iterators @a first and @a last must be initialized. **This
+ precondition is enforced with an assertion (see warning).** If
+ assertions are switched off, a violation of this precondition yields
+ undefined behavior.
+
+ @pre Range `[first, last)` is valid. Usually, this precondition cannot be
+ checked efficiently. Only certain edge cases are detected; see the
+ description of the exceptions below. A violation of this precondition
+ yields undefined behavior.
+
+ @warning A precondition is enforced with a runtime assertion that will
+ result in calling `std::abort` if this precondition is not met.
+ Assertions can be disabled by defining `NDEBUG` at compile time.
+ See http://en.cppreference.com/w/cpp/error/assert for more
+ information.
+
+ @throw invalid_iterator.201 if iterators @a first and @a last are not
+ compatible (i.e., do not belong to the same JSON value). In this case,
+ the range `[first, last)` is undefined.
+ @throw invalid_iterator.204 if iterators @a first and @a last belong to a
+ primitive type (number, boolean, or string), but @a first does not point
+ to the first element any more. In this case, the range `[first, last)` is
+ undefined. See example code below.
+ @throw invalid_iterator.206 if iterators @a first and @a last belong to a
+ null value. In this case, the range `[first, last)` is undefined.
@complexity Linear in distance between @a first and @a last.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@liveexample{The example below shows several ways to create JSON values by
specifying a subrange with iterators.,basic_json__InputIt_InputIt}
@since version 1.0.0
*/
- template <class InputIT, typename
- std::enable_if<
- std::is_same<InputIT, typename basic_json_t::iterator>::value or
- std::is_same<InputIT, typename basic_json_t::const_iterator>::value
- , int>::type
- = 0>
- basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
+ template<class InputIT, typename std::enable_if<
+ std::is_same<InputIT, typename basic_json_t::iterator>::value or
+ std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
+ basic_json(InputIT first, InputIT last)
{
+ assert(first.m_object != nullptr);
+ assert(last.m_object != nullptr);
+
// make sure iterator fits the current value
- if (first.m_object != last.m_object)
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
{
- throw std::domain_error("iterators are not compatible");
+ JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
}
+ // copy type from first iterator
+ m_type = first.m_object->m_type;
+
// check if iterator range is complete for primitive values
switch (m_type)
{
@@ -1836,105 +8767,82 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
+ if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin()
+ or not last.m_it.primitive_iterator.is_end()))
{
- throw std::out_of_range("iterators out of range");
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
}
break;
}
default:
- {
break;
- }
}
switch (m_type)
{
case value_t::number_integer:
{
- assert(first.m_object != nullptr);
m_value.number_integer = first.m_object->m_value.number_integer;
break;
}
case value_t::number_unsigned:
{
- assert(first.m_object != nullptr);
m_value.number_unsigned = first.m_object->m_value.number_unsigned;
break;
}
case value_t::number_float:
{
- assert(first.m_object != nullptr);
m_value.number_float = first.m_object->m_value.number_float;
break;
}
case value_t::boolean:
{
- assert(first.m_object != nullptr);
m_value.boolean = first.m_object->m_value.boolean;
break;
}
case value_t::string:
{
- assert(first.m_object != nullptr);
m_value = *first.m_object->m_value.string;
break;
}
case value_t::object:
{
- m_value.object = create<object_t>(first.m_it.object_iterator, last.m_it.object_iterator);
+ m_value.object = create<object_t>(first.m_it.object_iterator,
+ last.m_it.object_iterator);
break;
}
case value_t::array:
{
- m_value.array = create<array_t>(first.m_it.array_iterator, last.m_it.array_iterator);
+ m_value.array = create<array_t>(first.m_it.array_iterator,
+ last.m_it.array_iterator);
break;
}
default:
- {
- assert(first.m_object != nullptr);
- throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
- }
+ JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
+ std::string(first.m_object->type_name())));
}
- }
-
- /*!
- @brief construct a JSON value given an input stream
-
- @param[in,out] i stream to read a serialized JSON value from
- @param[in] cb a parser callback function of type @ref parser_callback_t
- which is used to control the deserialization by filtering unwanted values
- (optional)
-
- @complexity Linear in the length of the input. The parser is a predictive
- LL(1) parser. The complexity can be higher if the parser callback function
- @a cb has a super-linear complexity.
- @note A UTF-8 byte order mark is silently ignored.
-
- @liveexample{The example below demonstrates constructing a JSON value from
- a `std::stringstream` with and without callback
- function.,basic_json__istream}
-
- @since version 2.0.0
- */
- explicit basic_json(std::istream& i, parser_callback_t cb = nullptr)
- {
- *this = parser(i, cb).parse();
+ assert_invariant();
}
+
///////////////////////////////////////
// other constructors and destructor //
///////////////////////////////////////
+ /// @private
+ basic_json(const detail::json_ref<basic_json>& ref)
+ : basic_json(ref.moved_or_copied())
+ {}
+
/*!
@brief copy constructor
@@ -1942,16 +8850,19 @@ class basic_json
@param[in] other the JSON value to copy
+ @post `*this == other`
+
@complexity Linear in the size of @a other.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes to any JSON value.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
- The complexity is linear.
- As postcondition, it holds: `other == basic_json(other)`.
- @throw std::bad_alloc if allocation for object, array, or string fails.
-
@liveexample{The following code shows an example for the copy
constructor.,basic_json__basic_json}
@@ -1960,25 +8871,25 @@ class basic_json
basic_json(const basic_json& other)
: m_type(other.m_type)
{
+ // check of passed value is valid
+ other.assert_invariant();
+
switch (m_type)
{
case value_t::object:
{
- assert(other.m_value.object != nullptr);
m_value = *other.m_value.object;
break;
}
case value_t::array:
{
- assert(other.m_value.array != nullptr);
m_value = *other.m_value.array;
break;
}
case value_t::string:
{
- assert(other.m_value.string != nullptr);
m_value = *other.m_value.string;
break;
}
@@ -2008,10 +8919,10 @@ class basic_json
}
default:
- {
break;
- }
}
+
+ assert_invariant();
}
/*!
@@ -2023,10 +8934,18 @@ class basic_json
@param[in,out] other value to move to this object
- @post @a other is a JSON null value
+ @post `*this` has the same value as @a other before the call.
+ @post @a other is a JSON null value.
@complexity Constant.
+ @exceptionsafety No-throw guarantee: this constructor never throws
+ exceptions.
+
+ @requirement This function helps `basic_json` satisfying the
+ [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible)
+ requirements.
+
@liveexample{The code below shows the move constructor explicitly called
via std::move.,basic_json__moveconstructor}
@@ -2036,9 +8955,14 @@ class basic_json
: m_type(std::move(other.m_type)),
m_value(std::move(other.m_value))
{
+ // check that passed value is valid
+ other.assert_invariant();
+
// invalidate payload
other.m_type = value_t::null;
other.m_value = {};
+
+ assert_invariant();
}
/*!
@@ -2046,7 +8970,7 @@ class basic_json
Copy assignment operator. Copies a JSON value via the "copy and swap"
strategy: It is expressed in terms of the copy constructor, destructor,
- and the swap() member function.
+ and the `swap()` member function.
@param[in] other value to copy from
@@ -2071,9 +8995,14 @@ class basic_json
std::is_nothrow_move_assignable<json_value>::value
)
{
+ // check that passed value is valid
+ other.assert_invariant();
+
using std::swap;
swap(m_type, other.m_type);
swap(m_value, other.m_value);
+
+ assert_invariant();
return *this;
}
@@ -2094,38 +9023,8 @@ class basic_json
*/
~basic_json()
{
- switch (m_type)
- {
- case value_t::object:
- {
- AllocatorType<object_t> alloc;
- alloc.destroy(m_value.object);
- alloc.deallocate(m_value.object, 1);
- break;
- }
-
- case value_t::array:
- {
- AllocatorType<array_t> alloc;
- alloc.destroy(m_value.array);
- alloc.deallocate(m_value.array, 1);
- break;
- }
-
- case value_t::string:
- {
- AllocatorType<string_t> alloc;
- alloc.destroy(m_value.string);
- alloc.deallocate(m_value.string, 1);
- break;
- }
-
- default:
- {
- // all other types need no specific destructor
- break;
- }
- }
+ assert_invariant();
+ m_value.destroy(m_type);
}
/// @}
@@ -2136,45 +9035,61 @@ class basic_json
///////////////////////
/// @name object inspection
+ /// Functions to inspect the type of a JSON value.
/// @{
/*!
@brief serialization
Serialization function for JSON values. The function tries to mimic
- Python's @p json.dumps() function, and currently supports its @p indent
- parameter.
+ Python's `json.dumps()` function, and currently supports its @a indent
+ and @a ensure_ascii parameters.
- @param[in] indent if indent is nonnegative, then array elements and object
+ @param[in] indent If indent is nonnegative, then array elements and object
members will be pretty-printed with that indent level. An indent level of
- 0 will only insert newlines. -1 (the default) selects the most compact
- representation
+ `0` will only insert newlines. `-1` (the default) selects the most compact
+ representation.
+ @param[in] indent_char The character to use for indentation if @a indent is
+ greater than `0`. The default is ` ` (space).
+ @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+ in the output are escaped with `\uXXXX` sequences, and the result consists
+ of ASCII characters only.
@return string containing the serialization of the JSON value
+ @throw type_error.316 if a string stored inside the JSON value is not
+ UTF-8 encoded
+
@complexity Linear.
- @liveexample{The following example shows the effect of different @a indent
- parameters to the result of the serialization.,dump}
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @liveexample{The following example shows the effect of different @a indent\,
+ @a indent_char\, and @a ensure_ascii parameters to the result of the
+ serialization.,dump}
@see https://docs.python.org/2/library/json.html#json.dump
- @since version 1.0.0
+ @since version 1.0.0; indentation character @a indent_char, option
+ @a ensure_ascii and exceptions added in version 3.0.0
*/
- string_t dump(const int indent = -1) const
+ string_t dump(const int indent = -1, const char indent_char = ' ',
+ const bool ensure_ascii = false) const
{
- std::stringstream ss;
+ string_t result;
+ serializer s(detail::output_adapter<char>(result), indent_char);
if (indent >= 0)
{
- dump(ss, true, static_cast<unsigned int>(indent));
+ s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
}
else
{
- dump(ss, false, 0);
+ s.dump(*this, false, ensure_ascii, 0);
}
- return ss.str();
+ return result;
}
/*!
@@ -2184,6 +9099,17 @@ class basic_json
enumeration.
@return the type of the JSON value
+ Value type | return value
+ ------------------------- | -------------------------
+ null | value_t::null
+ boolean | value_t::boolean
+ string | value_t::string
+ number (integer) | value_t::number_integer
+ number (unsigned integer) | value_t::number_unsigned
+ number (floating-point) | value_t::number_float
+ object | value_t::object
+ array | value_t::array
+ discarded | value_t::discarded
@complexity Constant.
@@ -2193,6 +9119,9 @@ class basic_json
@liveexample{The following code exemplifies `type()` for all JSON
types.,type}
+ @sa @ref operator value_t() -- return the type of the JSON value (implicit)
+ @sa @ref type_name() -- return the type as string
+
@since version 1.0.0
*/
constexpr value_t type() const noexcept
@@ -2203,8 +9132,8 @@ class basic_json
/*!
@brief return whether type is primitive
- This function returns true iff the JSON type is primitive (string, number,
- boolean, or null).
+ This function returns true if and only if the JSON type is primitive
+ (string, number, boolean, or null).
@return `true` if type is primitive (string, number, boolean, or null),
`false` otherwise.
@@ -2233,8 +9162,8 @@ class basic_json
/*!
@brief return whether type is structured
- This function returns true iff the JSON type is structured (array or
- object).
+ This function returns true if and only if the JSON type is structured
+ (array or object).
@return `true` if type is structured (array or object), `false` otherwise.
@@ -2260,7 +9189,7 @@ class basic_json
/*!
@brief return whether value is null
- This function returns true iff the JSON value is null.
+ This function returns true if and only if the JSON value is null.
@return `true` if type is null, `false` otherwise.
@@ -2276,13 +9205,13 @@ class basic_json
*/
constexpr bool is_null() const noexcept
{
- return m_type == value_t::null;
+ return (m_type == value_t::null);
}
/*!
@brief return whether value is a boolean
- This function returns true iff the JSON value is a boolean.
+ This function returns true if and only if the JSON value is a boolean.
@return `true` if type is boolean, `false` otherwise.
@@ -2298,14 +9227,14 @@ class basic_json
*/
constexpr bool is_boolean() const noexcept
{
- return m_type == value_t::boolean;
+ return (m_type == value_t::boolean);
}
/*!
@brief return whether value is a number
- This function returns true iff the JSON value is a number. This includes
- both integer and floating-point values.
+ This function returns true if and only if the JSON value is a number. This
+ includes both integer (signed and unsigned) and floating-point values.
@return `true` if type is number (regardless whether integer, unsigned
integer or floating-type), `false` otherwise.
@@ -2334,8 +9263,8 @@ class basic_json
/*!
@brief return whether value is an integer number
- This function returns true iff the JSON value is an integer or unsigned
- integer number. This excludes floating-point values.
+ This function returns true if and only if the JSON value is a signed or
+ unsigned integer number. This excludes floating-point values.
@return `true` if type is an integer or unsigned integer number, `false`
otherwise.
@@ -2357,14 +9286,14 @@ class basic_json
*/
constexpr bool is_number_integer() const noexcept
{
- return m_type == value_t::number_integer or m_type == value_t::number_unsigned;
+ return (m_type == value_t::number_integer or m_type == value_t::number_unsigned);
}
/*!
@brief return whether value is an unsigned integer number
- This function returns true iff the JSON value is an unsigned integer
- number. This excludes floating-point and (signed) integer values.
+ This function returns true if and only if the JSON value is an unsigned
+ integer number. This excludes floating-point and signed integer values.
@return `true` if type is an unsigned integer number, `false` otherwise.
@@ -2385,14 +9314,14 @@ class basic_json
*/
constexpr bool is_number_unsigned() const noexcept
{
- return m_type == value_t::number_unsigned;
+ return (m_type == value_t::number_unsigned);
}
/*!
@brief return whether value is a floating-point number
- This function returns true iff the JSON value is a floating-point number.
- This excludes integer and unsigned integer values.
+ This function returns true if and only if the JSON value is a
+ floating-point number. This excludes signed and unsigned integer values.
@return `true` if type is a floating-point number, `false` otherwise.
@@ -2413,13 +9342,13 @@ class basic_json
*/
constexpr bool is_number_float() const noexcept
{
- return m_type == value_t::number_float;
+ return (m_type == value_t::number_float);
}
/*!
@brief return whether value is an object
- This function returns true iff the JSON value is an object.
+ This function returns true if and only if the JSON value is an object.
@return `true` if type is object, `false` otherwise.
@@ -2435,13 +9364,13 @@ class basic_json
*/
constexpr bool is_object() const noexcept
{
- return m_type == value_t::object;
+ return (m_type == value_t::object);
}
/*!
@brief return whether value is an array
- This function returns true iff the JSON value is an array.
+ This function returns true if and only if the JSON value is an array.
@return `true` if type is array, `false` otherwise.
@@ -2457,13 +9386,13 @@ class basic_json
*/
constexpr bool is_array() const noexcept
{
- return m_type == value_t::array;
+ return (m_type == value_t::array);
}
/*!
@brief return whether value is a string
- This function returns true iff the JSON value is a string.
+ This function returns true if and only if the JSON value is a string.
@return `true` if type is string, `false` otherwise.
@@ -2479,14 +9408,14 @@ class basic_json
*/
constexpr bool is_string() const noexcept
{
- return m_type == value_t::string;
+ return (m_type == value_t::string);
}
/*!
@brief return whether value is discarded
- This function returns true iff the JSON value was discarded during parsing
- with a callback function (see @ref parser_callback_t).
+ This function returns true if and only if the JSON value was discarded
+ during parsing with a callback function (see @ref parser_callback_t).
@note This function will always be `false` for JSON values after parsing.
That is, discarded values can only occur during parsing, but will be
@@ -2506,7 +9435,7 @@ class basic_json
*/
constexpr bool is_discarded() const noexcept
{
- return m_type == value_t::discarded;
+ return (m_type == value_t::discarded);
}
/*!
@@ -2525,6 +9454,9 @@ class basic_json
@liveexample{The following code exemplifies the @ref value_t operator for
all JSON types.,operator__value_t}
+ @sa @ref type() -- return the type of the JSON value (explicit)
+ @sa @ref type_name() -- return the type as string
+
@since version 1.0.0
*/
constexpr operator value_t() const noexcept
@@ -2539,263 +9471,97 @@ class basic_json
// value access //
//////////////////
- /// get an object (explicit)
- template <class T, typename
- std::enable_if<
- std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
- std::is_convertible<basic_json_t, typename T::mapped_type>::value
- , int>::type = 0>
- T get_impl(T*) const
- {
- if (is_object())
- {
- assert(m_value.object != nullptr);
- return T(m_value.object->begin(), m_value.object->end());
- }
- else
- {
- throw std::domain_error("type must be object, but is " + type_name());
- }
- }
-
- /// get an object (explicit)
- object_t get_impl(object_t*) const
- {
- if (is_object())
- {
- assert(m_value.object != nullptr);
- return *(m_value.object);
- }
- else
- {
- throw std::domain_error("type must be object, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- template <class T, typename
- std::enable_if<
- std::is_convertible<basic_json_t, typename T::value_type>::value and
- not std::is_same<basic_json_t, typename T::value_type>::value and
- not std::is_arithmetic<T>::value and
- not std::is_convertible<std::string, T>::value and
- not has_mapped_type<T>::value
- , int>::type = 0>
- T get_impl(T*) const
- {
- if (is_array())
- {
- T to_vector;
- assert(m_value.array != nullptr);
- std::transform(m_value.array->begin(), m_value.array->end(),
- std::inserter(to_vector, to_vector.end()), [](basic_json i)
- {
- return i.get<typename T::value_type>();
- });
- return to_vector;
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- template <class T, typename
- std::enable_if<
- std::is_convertible<basic_json_t, T>::value and
- not std::is_same<basic_json_t, T>::value
- , int>::type = 0>
- std::vector<T> get_impl(std::vector<T>*) const
- {
- if (is_array())
- {
- std::vector<T> to_vector;
- assert(m_value.array != nullptr);
- to_vector.reserve(m_value.array->size());
- std::transform(m_value.array->begin(), m_value.array->end(),
- std::inserter(to_vector, to_vector.end()), [](basic_json i)
- {
- return i.get<T>();
- });
- return to_vector;
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- template <class T, typename
- std::enable_if<
- std::is_same<basic_json, typename T::value_type>::value and
- not has_mapped_type<T>::value
- , int>::type = 0>
- T get_impl(T*) const
- {
- if (is_array())
- {
- assert(m_value.array != nullptr);
- return T(m_value.array->begin(), m_value.array->end());
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get an array (explicit)
- array_t get_impl(array_t*) const
- {
- if (is_array())
- {
- assert(m_value.array != nullptr);
- return *(m_value.array);
- }
- else
- {
- throw std::domain_error("type must be array, but is " + type_name());
- }
- }
-
- /// get a string (explicit)
- template <typename T, typename
- std::enable_if<
- std::is_convertible<string_t, T>::value
- , int>::type = 0>
- T get_impl(T*) const
- {
- if (is_string())
- {
- assert(m_value.string != nullptr);
- return *m_value.string;
- }
- else
- {
- throw std::domain_error("type must be string, but is " + type_name());
- }
- }
-
- /// get a number (explicit)
- template<typename T, typename
- std::enable_if<
- std::is_arithmetic<T>::value
- , int>::type = 0>
- T get_impl(T*) const
+ /// get a boolean (explicit)
+ boolean_t get_impl(boolean_t* /*unused*/) const
{
- switch (m_type)
+ if (JSON_LIKELY(is_boolean()))
{
- case value_t::number_integer:
- {
- return static_cast<T>(m_value.number_integer);
- }
-
- case value_t::number_unsigned:
- {
- return static_cast<T>(m_value.number_unsigned);
- }
-
- case value_t::number_float:
- {
- return static_cast<T>(m_value.number_float);
- }
-
- default:
- {
- throw std::domain_error("type must be number, but is " + type_name());
- }
+ return m_value.boolean;
}
- }
- /// get a boolean (explicit)
- constexpr boolean_t get_impl(boolean_t*) const
- {
- return is_boolean()
- ? m_value.boolean
- : throw std::domain_error("type must be boolean, but is " + type_name());
+ JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name())));
}
/// get a pointer to the value (object)
- object_t* get_impl_ptr(object_t*) noexcept
+ object_t* get_impl_ptr(object_t* /*unused*/) noexcept
{
return is_object() ? m_value.object : nullptr;
}
/// get a pointer to the value (object)
- constexpr const object_t* get_impl_ptr(const object_t*) const noexcept
+ constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
{
return is_object() ? m_value.object : nullptr;
}
/// get a pointer to the value (array)
- array_t* get_impl_ptr(array_t*) noexcept
+ array_t* get_impl_ptr(array_t* /*unused*/) noexcept
{
return is_array() ? m_value.array : nullptr;
}
/// get a pointer to the value (array)
- constexpr const array_t* get_impl_ptr(const array_t*) const noexcept
+ constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
{
return is_array() ? m_value.array : nullptr;
}
/// get a pointer to the value (string)
- string_t* get_impl_ptr(string_t*) noexcept
+ string_t* get_impl_ptr(string_t* /*unused*/) noexcept
{
return is_string() ? m_value.string : nullptr;
}
/// get a pointer to the value (string)
- constexpr const string_t* get_impl_ptr(const string_t*) const noexcept
+ constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
{
return is_string() ? m_value.string : nullptr;
}
/// get a pointer to the value (boolean)
- boolean_t* get_impl_ptr(boolean_t*) noexcept
+ boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
{
return is_boolean() ? &m_value.boolean : nullptr;
}
/// get a pointer to the value (boolean)
- constexpr const boolean_t* get_impl_ptr(const boolean_t*) const noexcept
+ constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
{
return is_boolean() ? &m_value.boolean : nullptr;
}
/// get a pointer to the value (integer number)
- number_integer_t* get_impl_ptr(number_integer_t*) noexcept
+ number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
{
return is_number_integer() ? &m_value.number_integer : nullptr;
}
/// get a pointer to the value (integer number)
- constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept
+ constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
{
return is_number_integer() ? &m_value.number_integer : nullptr;
}
/// get a pointer to the value (unsigned number)
- number_unsigned_t* get_impl_ptr(number_unsigned_t*) noexcept
+ number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
{
return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
}
/// get a pointer to the value (unsigned number)
- constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t*) const noexcept
+ constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
{
return is_number_unsigned() ? &m_value.number_unsigned : nullptr;
}
/// get a pointer to the value (floating-point number)
- number_float_t* get_impl_ptr(number_float_t*) noexcept
+ number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
{
return is_number_float() ? &m_value.number_float : nullptr;
}
/// get a pointer to the value (floating-point number)
- constexpr const number_float_t* get_impl_ptr(const number_float_t*) const noexcept
+ constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
{
return is_number_float() ? &m_value.number_float : nullptr;
}
@@ -2803,52 +9569,84 @@ class basic_json
/*!
@brief helper function to implement get_ref()
- This funcion helps to implement get_ref() without code duplication for
+ This function helps to implement get_ref() without code duplication for
const and non-const overloads
@tparam ThisType will be deduced as `basic_json` or `const basic_json`
- @throw std::domain_error if ReferenceType does not match underlying value
+ @throw type_error.303 if ReferenceType does not match underlying value
type of the current JSON
*/
template<typename ReferenceType, typename ThisType>
static ReferenceType get_ref_impl(ThisType& obj)
{
// delegate the call to get_ptr<>()
- using PointerType = typename std::add_pointer<ReferenceType>::type;
- auto ptr = obj.template get_ptr<PointerType>();
+ auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
- if (ptr != nullptr)
+ if (JSON_LIKELY(ptr != nullptr))
{
return *ptr;
}
- else
- {
- throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
- obj.type_name());
- }
+
+ JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name())));
}
public:
-
/// @name value access
+ /// Direct access to the stored value of a JSON value.
/// @{
/*!
+ @brief get special-case overload
+
+ This overloads avoids a lot of template boilerplate, it can be seen as the
+ identity method
+
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this
+
+ @complexity Constant.
+
+ @since version 2.1.0
+ */
+ template<typename BasicJsonType, detail::enable_if_t<
+ std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,
+ int> = 0>
+ basic_json get() const
+ {
+ return *this;
+ }
+
+ /*!
@brief get a value (explicit)
- Explicit type conversion between the JSON value and a compatible value.
+ Explicit type conversion between the JSON value and a compatible value
+ which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+ and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
- @tparam ValueType non-pointer type compatible to the JSON value, for
- instance `int` for JSON integer numbers, `bool` for JSON booleans, or
- `std::vector` types for JSON arrays
+ The function is equivalent to executing
+ @code {.cpp}
+ ValueType ret;
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ @endcode
- @return copy of the JSON value, converted to type @a ValueType
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json,
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `void from_json(const basic_json&, ValueType&)`, and
+ - @ref json_serializer<ValueType> does not have a `from_json()` method of
+ the form `ValueType from_json(const basic_json&)`
- @throw std::domain_error in case passed type @a ValueType is incompatible
- to JSON; example: `"type must be object, but is null"`
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
- @complexity Linear in the size of the JSON value.
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
@liveexample{The example below shows several conversions from JSON values
to other types. There a few things to note: (1) Floating-point numbers can
@@ -2857,23 +9655,71 @@ class basic_json
associative containers such as `std::unordered_map<std::string\,
json>`.,get__ValueType_const}
- @internal
- The idea of using a casted null pointer to choose the correct
- implementation is from <http://stackoverflow.com/a/8315197/266378>.
- @endinternal
+ @since version 2.1.0
+ */
+ template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
+ detail::enable_if_t <
+ not std::is_same<basic_json_t, ValueType>::value and
+ detail::has_from_json<basic_json_t, ValueType>::value and
+ not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int> = 0>
+ ValueType get() const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+ {
+ // we cannot static_assert on ValueTypeCV being non-const, because
+ // there is support for get<const basic_json_t>(), which is why we
+ // still need the uncvref
+ static_assert(not std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ static_assert(std::is_default_constructible<ValueType>::value,
+ "types must be DefaultConstructible when used with get()");
- @sa @ref operator ValueType() const for implicit conversion
- @sa @ref get() for pointer-member access
+ ValueType ret;
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ }
- @since version 1.0.0
+ /*!
+ @brief get a value (explicit); special case
+
+ Explicit type conversion between the JSON value and a compatible value
+ which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible)
+ and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ return JSONSerializer<ValueTypeCV>::from_json(*this);
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json and
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `ValueType from_json(const basic_json&)`
+
+ @note If @ref json_serializer<ValueType> has both overloads of
+ `from_json()`, this one is chosen.
+
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @since version 2.1.0
*/
- template<typename ValueType, typename
- std::enable_if<
- not std::is_pointer<ValueType>::value
- , int>::type = 0>
- ValueType get() const
+ template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
+ detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and
+ detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int> = 0>
+ ValueType get() const noexcept(noexcept(
+ JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>())))
{
- return get_impl(static_cast<ValueType*>(nullptr));
+ static_assert(not std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ return JSONSerializer<ValueTypeCV>::from_json(*this);
}
/*!
@@ -2882,7 +9728,8 @@ class basic_json
Explicit pointer access to the internally stored JSON value. No copies are
made.
- @warning The pointer becomes invalid if the underlying JSON object changes.
+ @warning The pointer becomes invalid if the underlying JSON object
+ changes.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@@ -2902,10 +9749,8 @@ class basic_json
@since version 1.0.0
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get() noexcept
{
// delegate the call to get_ptr
@@ -2916,10 +9761,8 @@ class basic_json
@brief get a pointer value (explicit)
@copydoc get()
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
constexpr const PointerType get() const noexcept
{
// delegate the call to get_ptr
@@ -2937,7 +9780,8 @@ class basic_json
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
- @ref number_unsigned_t, or @ref number_float_t.
+ @ref number_unsigned_t, or @ref number_float_t. Enforced by a static
+ assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@@ -2951,12 +9795,25 @@ class basic_json
@since version 1.0.0
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept
{
+ // get the type of the PointerType (remove pointer and const)
+ using pointee_t = typename std::remove_const<typename
+ std::remove_pointer<typename
+ std::remove_const<PointerType>::type>::type>::type;
+ // make sure the type matches the allowed types
+ static_assert(
+ std::is_same<object_t, pointee_t>::value
+ or std::is_same<array_t, pointee_t>::value
+ or std::is_same<string_t, pointee_t>::value
+ or std::is_same<boolean_t, pointee_t>::value
+ or std::is_same<number_integer_t, pointee_t>::value
+ or std::is_same<number_unsigned_t, pointee_t>::value
+ or std::is_same<number_float_t, pointee_t>::value
+ , "incompatible pointer type");
+
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
@@ -2965,21 +9822,34 @@ class basic_json
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
- template<typename PointerType, typename
- std::enable_if<
- std::is_pointer<PointerType>::value
- and std::is_const<typename std::remove_pointer<PointerType>::type>::value
- , int>::type = 0>
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value and
+ std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept
{
+ // get the type of the PointerType (remove pointer and const)
+ using pointee_t = typename std::remove_const<typename
+ std::remove_pointer<typename
+ std::remove_const<PointerType>::type>::type>::type;
+ // make sure the type matches the allowed types
+ static_assert(
+ std::is_same<object_t, pointee_t>::value
+ or std::is_same<array_t, pointee_t>::value
+ or std::is_same<string_t, pointee_t>::value
+ or std::is_same<boolean_t, pointee_t>::value
+ or std::is_same<number_integer_t, pointee_t>::value
+ or std::is_same<number_unsigned_t, pointee_t>::value
+ or std::is_same<number_float_t, pointee_t>::value
+ , "incompatible pointer type");
+
// delegate the call to get_impl_ptr<>() const
- return get_impl_ptr(static_cast<const PointerType>(nullptr));
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
}
/*!
@brief get a reference value (implicit)
- Implict reference access to the internally stored JSON value. No copies
+ Implicit reference access to the internally stored JSON value. No copies
are made.
@warning Writing data to the referee of the result yields an undefined
@@ -2987,14 +9857,14 @@ class basic_json
@tparam ReferenceType reference type; must be a reference to @ref array_t,
@ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or
- @ref number_float_t.
+ @ref number_float_t. Enforced by static assertion.
@return reference to the internally stored JSON value if the requested
reference type @a ReferenceType fits to the JSON value; throws
- std::domain_error otherwise
+ type_error.303 otherwise
- @throw std::domain_error in case passed type @a ReferenceType is
- incompatible with the stored JSON value
+ @throw type_error.303 in case passed type @a ReferenceType is incompatible
+ with the stored JSON value; see example below
@complexity Constant.
@@ -3002,10 +9872,8 @@ class basic_json
@since version 1.1.0
*/
- template<typename ReferenceType, typename
- std::enable_if<
- std::is_reference<ReferenceType>::value
- , int>::type = 0>
+ template<typename ReferenceType, typename std::enable_if<
+ std::is_reference<ReferenceType>::value, int>::type = 0>
ReferenceType get_ref()
{
// delegate call to get_ref_impl
@@ -3016,11 +9884,9 @@ class basic_json
@brief get a reference value (implicit)
@copydoc get_ref()
*/
- template<typename ReferenceType, typename
- std::enable_if<
- std::is_reference<ReferenceType>::value
- and std::is_const<typename std::remove_reference<ReferenceType>::type>::value
- , int>::type = 0>
+ template<typename ReferenceType, typename std::enable_if<
+ std::is_reference<ReferenceType>::value and
+ std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
ReferenceType get_ref() const
{
// delegate call to get_ref_impl
@@ -3041,8 +9907,9 @@ class basic_json
@return copy of the JSON value, converted to type @a ValueType
- @throw std::domain_error in case passed type @a ValueType is incompatible
- to JSON, thrown by @ref get() const
+ @throw type_error.302 in case passed type @a ValueType is incompatible
+ to the JSON value type (e.g., the JSON value is of type boolean, but a
+ string is requested); see example below
@complexity Linear in the size of the JSON value.
@@ -3055,13 +9922,16 @@ class basic_json
@since version 1.0.0
*/
- template < typename ValueType, typename
- std::enable_if <
- not std::is_pointer<ValueType>::value
- and not std::is_same<ValueType, typename string_t::value_type>::value
-#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015
+ template < typename ValueType, typename std::enable_if <
+ not std::is_pointer<ValueType>::value and
+ not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
+ not std::is_same<ValueType, typename string_t::value_type>::value
+#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif
+#if defined(JSON_HAS_CPP_17)
+ and not std::is_same<ValueType, typename std::string_view>::value
+#endif
, int >::type = 0 >
operator ValueType() const
{
@@ -3077,6 +9947,7 @@ class basic_json
////////////////////
/// @name element access
+ /// Access to the JSON value.
/// @{
/*!
@@ -3089,37 +9960,40 @@ class basic_json
@return reference to the element at index @a idx
- @throw std::domain_error if the JSON value is not an array; example:
- `"cannot use at() with string"`
- @throw std::out_of_range if the index @a idx is out of range of the array;
- that is, `idx >= size()`; example: `"array index 7 is out of range"`
+ @throw type_error.304 if the JSON value is not an array; in this case,
+ calling `at` with an index makes no sense. See example below.
+ @throw out_of_range.401 if the index @a idx is out of range of the array;
+ that is, `idx >= size()`. See example below.
- @complexity Constant.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how array elements can be read and
- written using `at()`.,at__size_type}
+ @complexity Constant.
@since version 1.0.0
+
+ @liveexample{The example below shows how array elements can be read and
+ written using `at()`. It also demonstrates the different exceptions that
+ can be thrown.,at__size_type}
*/
reference at(size_type idx)
{
// at only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- try
+ JSON_TRY
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3133,37 +10007,40 @@ class basic_json
@return const reference to the element at index @a idx
- @throw std::domain_error if the JSON value is not an array; example:
- `"cannot use at() with string"`
- @throw std::out_of_range if the index @a idx is out of range of the array;
- that is, `idx >= size()`; example: `"array index 7 is out of range"`
+ @throw type_error.304 if the JSON value is not an array; in this case,
+ calling `at` with an index makes no sense. See example below.
+ @throw out_of_range.401 if the index @a idx is out of range of the array;
+ that is, `idx >= size()`. See example below.
- @complexity Constant.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how array elements can be read using
- `at()`.,at__size_type_const}
+ @complexity Constant.
@since version 1.0.0
+
+ @liveexample{The example below shows how array elements can be read using
+ `at()`. It also demonstrates the different exceptions that can be thrown.,
+ at__size_type_const}
*/
const_reference at(size_type idx) const
{
// at only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- try
+ JSON_TRY
{
- assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3177,41 +10054,44 @@ class basic_json
@return reference to the element at key @a key
- @throw std::domain_error if the JSON value is not an object; example:
- `"cannot use at() with boolean"`
- @throw std::out_of_range if the key @a key is is not stored in the object;
- that is, `find(key) == end()`; example: `"key "the fast" not found"`
+ @throw type_error.304 if the JSON value is not an object; in this case,
+ calling `at` with a key makes no sense. See example below.
+ @throw out_of_range.403 if the key @a key is is not stored in the object;
+ that is, `find(key) == end()`. See example below.
- @complexity Logarithmic in the size of the container.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how object elements can be read and
- written using `at()`.,at__object_t_key_type}
+ @complexity Logarithmic in the size of the container.
@sa @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
@sa @ref value() for access by value with a default value
@since version 1.0.0
+
+ @liveexample{The example below shows how object elements can be read and
+ written using `at()`. It also demonstrates the different exceptions that
+ can be thrown.,at__object_t_key_type}
*/
reference at(const typename object_t::key_type& key)
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- try
+ JSON_TRY
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("key '" + key + "' not found");
+ JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3225,41 +10105,44 @@ class basic_json
@return const reference to the element at key @a key
- @throw std::domain_error if the JSON value is not an object; example:
- `"cannot use at() with boolean"`
- @throw std::out_of_range if the key @a key is is not stored in the object;
- that is, `find(key) == end()`; example: `"key "the fast" not found"`
+ @throw type_error.304 if the JSON value is not an object; in this case,
+ calling `at` with a key makes no sense. See example below.
+ @throw out_of_range.403 if the key @a key is is not stored in the object;
+ that is, `find(key) == end()`. See example below.
- @complexity Logarithmic in the size of the container.
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
- @liveexample{The example below shows how object elements can be read using
- `at()`.,at__object_t_key_type_const}
+ @complexity Logarithmic in the size of the container.
@sa @ref operator[](const typename object_t::key_type&) for unchecked
access by reference
@sa @ref value() for access by value with a default value
@since version 1.0.0
+
+ @liveexample{The example below shows how object elements can be read using
+ `at()`. It also demonstrates the different exceptions that can be thrown.,
+ at__object_t_key_type_const}
*/
const_reference at(const typename object_t::key_type& key) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- try
+ JSON_TRY
{
- assert(m_value.object != nullptr);
return m_value.object->at(key);
}
- catch (std::out_of_range&)
+ JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
- throw std::out_of_range("key '" + key + "' not found");
+ JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
}
}
else
{
- throw std::domain_error("cannot use at() with " + type_name());
+ JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
}
}
@@ -3276,8 +10159,8 @@ class basic_json
@return reference to the element at index @a idx
- @throw std::domain_error if JSON is not an array or null; example:
- `"cannot use operator[] with string"`
+ @throw type_error.305 if the JSON value is not an array or null; in that
+ cases, using the [] operator with an index makes no sense.
@complexity Constant if @a idx is in the range of the array. Otherwise
linear in `idx - size()`.
@@ -3295,24 +10178,24 @@ class basic_json
{
m_type = value_t::array;
m_value.array = create<array_t>();
+ assert_invariant();
}
// operator[] only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- // fill up array with null values until given idx is reached
- assert(m_value.array != nullptr);
- for (size_t i = m_value.array->size(); i <= idx; ++i)
+ // fill up array with null values if given idx is outside range
+ if (idx >= m_value.array->size())
{
- m_value.array->push_back(basic_json());
+ m_value.array->insert(m_value.array->end(),
+ idx - m_value.array->size() + 1,
+ basic_json());
}
return m_value.array->operator[](idx);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3324,8 +10207,8 @@ class basic_json
@return const reference to the element at index @a idx
- @throw std::domain_error if JSON is not an array; example: `"cannot use
- operator[] with null"`
+ @throw type_error.305 if the JSON value is not an array; in that case,
+ using the [] operator with an index makes no sense.
@complexity Constant.
@@ -3337,15 +10220,12 @@ class basic_json
const_reference operator[](size_type idx) const
{
// const operator[] only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- assert(m_value.array != nullptr);
return m_value.array->operator[](idx);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3361,8 +10241,8 @@ class basic_json
@return reference to the element at key @a key
- @throw std::domain_error if JSON is not an object or null; example:
- `"cannot use operator[] with string"`
+ @throw type_error.305 if the JSON value is not an object or null; in that
+ cases, using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3382,18 +10262,16 @@ class basic_json
{
m_type = value_t::object;
m_value.object = create<object_t>();
+ assert_invariant();
}
// operator[] only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3409,8 +10287,11 @@ class basic_json
@return const reference to the element at key @a key
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- operator[] with null"`
+ @pre The element with key @a key must exist. **This precondition is
+ enforced with an assertion.**
+
+ @throw type_error.305 if the JSON value is not an object; in that case,
+ using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3426,84 +10307,13 @@ class basic_json
const_reference operator[](const typename object_t::key_type& key) const
{
// const operator[] only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- assert(m_value.object != nullptr);
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
- }
-
- /*!
- @brief access specified object element
-
- Returns a reference to the element at with specified key @a key.
-
- @note If @a key is not found in the object, then it is silently added to
- the object and filled with a `null` value to make `key` a valid reference.
- In case the value was `null` before, it is converted to an object.
-
- @param[in] key key of the element to access
-
- @return reference to the element at key @a key
-
- @throw std::domain_error if JSON is not an object or null; example:
- `"cannot use operator[] with string"`
-
- @complexity Logarithmic in the size of the container.
-
- @liveexample{The example below shows how object elements can be read and
- written using the `[]` operator.,operatorarray__key_type}
-
- @sa @ref at(const typename object_t::key_type&) for access by reference
- with range checking
- @sa @ref value() for access by value with a default value
-
- @since version 1.0.0
- */
- template<typename T, std::size_t n>
- reference operator[](T * (&key)[n])
- {
- return operator[](static_cast<const T>(key));
- }
-
- /*!
- @brief read-only access specified object element
-
- Returns a const reference to the element at with specified key @a key. No
- bounds checking is performed.
-
- @warning If the element with key @a key does not exist, the behavior is
- undefined.
-
- @note This function is required for compatibility reasons with Clang.
-
- @param[in] key key of the element to access
-
- @return const reference to the element at key @a key
-
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- operator[] with null"`
- @complexity Logarithmic in the size of the container.
-
- @liveexample{The example below shows how object elements can be read using
- the `[]` operator.,operatorarray__key_type_const}
-
- @sa @ref at(const typename object_t::key_type&) for access by reference
- with range checking
- @sa @ref value() for access by value with a default value
-
- @since version 1.0.0
- */
- template<typename T, std::size_t n>
- const_reference operator[](T * (&key)[n]) const
- {
- return operator[](static_cast<const T>(key));
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3519,8 +10329,8 @@ class basic_json
@return reference to the element at key @a key
- @throw std::domain_error if JSON is not an object or null; example:
- `"cannot use operator[] with string"`
+ @throw type_error.305 if the JSON value is not an object or null; in that
+ cases, using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3541,18 +10351,16 @@ class basic_json
{
m_type = value_t::object;
m_value = value_t::object;
+ assert_invariant();
}
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@@ -3568,8 +10376,11 @@ class basic_json
@return const reference to the element at key @a key
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- operator[] with null"`
+ @pre The element with key @a key must exist. **This precondition is
+ enforced with an assertion.**
+
+ @throw type_error.305 if the JSON value is not an object; in that case,
+ using the [] operator with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3586,29 +10397,26 @@ class basic_json
const_reference operator[](T* key) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- assert(m_value.object != nullptr);
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
- else
- {
- throw std::domain_error("cannot use operator[] with " + type_name());
- }
+
+ JSON_THROW(type_error::create(305, "cannot use operator[] with " + std::string(type_name())));
}
/*!
@brief access specified object element with default value
- Returns either a copy of an object's element at the specified key @a key or
- a given default value if no element with key @a key exists.
+ Returns either a copy of an object's element at the specified key @a key
+ or a given default value if no element with key @a key exists.
The function is basically equivalent to executing
@code {.cpp}
try {
return at(key);
- } catch(std::out_of_range) {
+ } catch(out_of_range) {
return default_value;
}
@endcode
@@ -3631,8 +10439,8 @@ class basic_json
@return copy of the element at key @a key or @a default_value if @a key
is not found
- @throw std::domain_error if JSON is not an object; example: `"cannot use
- value() with null"`
+ @throw type_error.306 if the JSON value is not an object; in that case,
+ using `value()` with a key makes no sense.
@complexity Logarithmic in the size of the container.
@@ -3646,14 +10454,12 @@ class basic_json
@since version 1.0.0
*/
- template <class ValueType, typename
- std::enable_if<
- std::is_convertible<basic_json_t, ValueType>::value
- , int>::type = 0>
- ValueType value(const typename object_t::key_type& key, ValueType default_value) const
+ template<class ValueType, typename std::enable_if<
+ std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
+ ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{
// at only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
// if key is found, return value and given default value otherwise
const auto it = find(key);
@@ -3661,24 +10467,91 @@ class basic_json
{
return *it;
}
- else
+
+ return default_value;
+ }
+
+ JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
+ }
+
+ /*!
+ @brief overload for a default value of type const char*
+ @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const
+ */
+ string_t value(const typename object_t::key_type& key, const char* default_value) const
+ {
+ return value(key, string_t(default_value));
+ }
+
+ /*!
+ @brief access specified object element via JSON Pointer with default value
+
+ Returns either a copy of an object's element at the specified key @a key
+ or a given default value if no element with key @a key exists.
+
+ The function is basically equivalent to executing
+ @code {.cpp}
+ try {
+ return at(ptr);
+ } catch(out_of_range) {
+ return default_value;
+ }
+ @endcode
+
+ @note Unlike @ref at(const json_pointer&), this function does not throw
+ if the given key @a key was not found.
+
+ @param[in] ptr a JSON pointer to the element to access
+ @param[in] default_value the value to return if @a ptr found no value
+
+ @tparam ValueType type compatible to JSON values, for instance `int` for
+ JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
+ JSON arrays. Note the type of the expected value at @a key and the default
+ value @a default_value must be compatible.
+
+ @return copy of the element at key @a key or @a default_value if @a key
+ is not found
+
+ @throw type_error.306 if the JSON value is not an objec; in that case,
+ using `value()` with a key makes no sense.
+
+ @complexity Logarithmic in the size of the container.
+
+ @liveexample{The example below shows how object elements can be queried
+ with a default value.,basic_json__value_ptr}
+
+ @sa @ref operator[](const json_pointer&) for unchecked access by reference
+
+ @since version 2.0.2
+ */
+ template<class ValueType, typename std::enable_if<
+ std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
+ ValueType value(const json_pointer& ptr, const ValueType& default_value) const
+ {
+ // at only works for objects
+ if (JSON_LIKELY(is_object()))
+ {
+ // if pointer resolves a value, return it or use default value
+ JSON_TRY
+ {
+ return ptr.get_checked(this);
+ }
+ JSON_CATCH (out_of_range&)
{
return default_value;
}
}
- else
- {
- throw std::domain_error("cannot use value() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
}
/*!
@brief overload for a default value of type const char*
- @copydoc basic_json::value()
+ @copydoc basic_json::value(const json_pointer&, ValueType) const
*/
- string_t value(const typename object_t::key_type& key, const char* default_value) const
+ string_t value(const json_pointer& ptr, const char* default_value) const
{
- return value(key, string_t(default_value));
+ return value(ptr, string_t(default_value));
}
/*!
@@ -3688,16 +10561,17 @@ class basic_json
container `c`, the expression `c.front()` is equivalent to `*c.begin()`.
@return In case of a structured type (array or object), a reference to the
- first element is returned. In cast of number, string, or boolean values, a
+ first element is returned. In case of number, string, or boolean values, a
reference to the value is returned.
@complexity Constant.
@pre The JSON value must not be `null` (would throw `std::out_of_range`)
- or an empty array or object (undefined behavior, guarded by assertions).
+ or an empty array or object (undefined behavior, **guarded by
+ assertions**).
@post The JSON value remains unchanged.
- @throw std::out_of_range when called on `null` value
+ @throw invalid_iterator.214 when called on `null` value
@liveexample{The following code shows an example for `front()`.,front}
@@ -3730,16 +10604,18 @@ class basic_json
@endcode
@return In case of a structured type (array or object), a reference to the
- last element is returned. In cast of number, string, or boolean values, a
+ last element is returned. In case of number, string, or boolean values, a
reference to the value is returned.
@complexity Constant.
@pre The JSON value must not be `null` (would throw `std::out_of_range`)
- or an empty array or object (undefined behavior, guarded by assertions).
+ or an empty array or object (undefined behavior, **guarded by
+ assertions**).
@post The JSON value remains unchanged.
- @throw std::out_of_range when called on `null` value.
+ @throw invalid_iterator.214 when called on a `null` value. See example
+ below.
@liveexample{The following code shows an example for `back()`.,back}
@@ -3778,29 +10654,30 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a
pos refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
- @throw std::domain_error if called on a `null` value; example: `"cannot
- use erase() with null"`
- @throw std::domain_error if called on an iterator which does not belong to
- the current JSON value; example: `"iterator does not fit current value"`
- @throw std::out_of_range if called on a primitive type with invalid
+ @throw type_error.307 if called on a `null` value; example: `"cannot use
+ erase() with null"`
+ @throw invalid_iterator.202 if called on an iterator which does not belong
+ to the current JSON value; example: `"iterator does not fit current
+ value"`
+ @throw invalid_iterator.205 if called on a primitive type with invalid
iterator (i.e., any iterator which is not `begin()`); example: `"iterator
out of range"`
@complexity The complexity depends on the type:
- objects: amortized constant
- - arrays: linear in distance between pos and the end of the container
+ - arrays: linear in distance between @a pos and the end of the container
- strings: linear in the length of the string
- other types: constant
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType}
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -3809,21 +10686,19 @@ class basic_json
@since version 1.0.0
*/
- template <class InteratorType, typename
- std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
- , int>::type
- = 0>
- InteratorType erase(InteratorType pos)
+ template<class IteratorType, typename std::enable_if<
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
+ = 0>
+ IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
- if (this != pos.m_object)
+ if (JSON_UNLIKELY(this != pos.m_object))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -3833,39 +10708,38 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (not pos.m_it.primitive_iterator.is_begin())
+ if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))
{
- throw std::out_of_range("iterator out of range");
+ JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
}
if (is_string())
{
- delete m_value.string;
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
m_value.string = nullptr;
}
m_type = value_t::null;
+ assert_invariant();
break;
}
case value_t::object:
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
break;
}
case value_t::array:
{
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
break;
}
default:
- {
- throw std::domain_error("cannot use erase() with " + type_name());
- }
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
return result;
@@ -3886,16 +10760,16 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a
second refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
- @throw std::domain_error if called on a `null` value; example: `"cannot
- use erase() with null"`
- @throw std::domain_error if called on iterators which does not belong to
- the current JSON value; example: `"iterators do not fit current value"`
- @throw std::out_of_range if called on a primitive type with invalid
+ @throw type_error.307 if called on a `null` value; example: `"cannot use
+ erase() with null"`
+ @throw invalid_iterator.203 if called on iterators which does not belong
+ to the current JSON value; example: `"iterators do not fit current value"`
+ @throw invalid_iterator.204 if called on a primitive type with invalid
iterators (i.e., if `first != begin()` and `last != end()`); example:
`"iterators out of range"`
@@ -3909,7 +10783,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType}
- @sa @ref erase(InteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@sa @ref erase(const size_type) -- removes the element from an array at
@@ -3917,21 +10791,19 @@ class basic_json
@since version 1.0.0
*/
- template <class InteratorType, typename
- std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
- , int>::type
- = 0>
- InteratorType erase(InteratorType first, InteratorType last)
+ template<class IteratorType, typename std::enable_if<
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
+ = 0>
+ IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
- if (this != first.m_object or this != last.m_object)
+ if (JSON_UNLIKELY(this != first.m_object or this != last.m_object))
{
- throw std::domain_error("iterators do not fit current value");
+ JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -3941,24 +10813,27 @@ class basic_json
case value_t::number_unsigned:
case value_t::string:
{
- if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
+ if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin()
+ or not last.m_it.primitive_iterator.is_end()))
{
- throw std::out_of_range("iterators out of range");
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
}
if (is_string())
{
- delete m_value.string;
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);
m_value.string = nullptr;
}
m_type = value_t::null;
+ assert_invariant();
break;
}
case value_t::object:
{
- assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
last.m_it.object_iterator);
break;
@@ -3966,16 +10841,13 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
last.m_it.array_iterator);
break;
}
default:
- {
- throw std::domain_error("cannot use erase() with " + type_name());
- }
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
return result;
@@ -3995,15 +10867,15 @@ class basic_json
@post References and iterators to the erased elements are invalidated.
Other references and iterators are not affected.
- @throw std::domain_error when called on a type other than JSON object;
+ @throw type_error.307 when called on a type other than JSON object;
example: `"cannot use erase() with null"`
@complexity `log(size()) + count(key)`
@liveexample{The example shows the effect of `erase()`.,erase__key_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const size_type) -- removes the element from an array at
the given index
@@ -4013,15 +10885,12 @@ class basic_json
size_type erase(const typename object_t::key_type& key)
{
// this erase only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- assert(m_value.object != nullptr);
return m_value.object->erase(key);
}
- else
- {
- throw std::domain_error("cannot use erase() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
/*!
@@ -4031,17 +10900,17 @@ class basic_json
@param[in] idx index of the element to remove
- @throw std::domain_error when called on a type other than JSON array;
+ @throw type_error.307 when called on a type other than JSON object;
example: `"cannot use erase() with null"`
- @throw std::out_of_range when `idx >= size()`; example: `"array index 17
+ @throw out_of_range.401 when `idx >= size()`; example: `"array index 17
is out of range"`
@complexity Linear in distance between @a idx and the end of the container.
@liveexample{The example shows the effect of `erase()`.,erase__size_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -4051,19 +10920,18 @@ class basic_json
void erase(const size_type idx)
{
// this erase only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- if (idx >= size())
+ if (JSON_UNLIKELY(idx >= size()))
{
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
- assert(m_value.array != nullptr);
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
{
- throw std::domain_error("cannot use erase() with " + type_name());
+ JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
}
}
@@ -4084,10 +10952,14 @@ class basic_json
element is not found or the JSON value is not an object, end() is
returned.
- @param[in] key key value of the element to search for
+ @note This method always returns @ref end() when executed on a JSON type
+ that is not an object.
+
+ @param[in] key key value of the element to search for.
@return Iterator to an element with key equivalent to @a key. If no such
- element is found, past-the-end (see end()) iterator is returned.
+ element is found or the JSON value is not an object, past-the-end (see
+ @ref end()) iterator is returned.
@complexity Logarithmic in the size of the JSON object.
@@ -4095,14 +10967,14 @@ class basic_json
@since version 1.0.0
*/
- iterator find(typename object_t::key_type key)
+ template<typename KeyT>
+ iterator find(KeyT&& key)
{
auto result = end();
if (is_object())
{
- assert(m_value.object != nullptr);
- result.m_it.object_iterator = m_value.object->find(key);
+ result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));
}
return result;
@@ -4110,16 +10982,16 @@ class basic_json
/*!
@brief find an element in a JSON object
- @copydoc find(typename object_t::key_type)
+ @copydoc find(KeyT&&)
*/
- const_iterator find(typename object_t::key_type key) const
+ template<typename KeyT>
+ const_iterator find(KeyT&& key) const
{
auto result = cend();
if (is_object())
{
- assert(m_value.object != nullptr);
- result.m_it.object_iterator = m_value.object->find(key);
+ result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));
}
return result;
@@ -4132,6 +11004,9 @@ class basic_json
default `std::map` type, the return value will always be `0` (@a key was
not found) or `1` (@a key was found).
+ @note This method always returns `0` when executed on a JSON type that is
+ not an object.
+
@param[in] key key value of the element to count
@return Number of elements with key @a key. If the JSON value is not an
@@ -4143,11 +11018,11 @@ class basic_json
@since version 1.0.0
*/
- size_type count(typename object_t::key_type key) const
+ template<typename KeyT>
+ size_type count(KeyT&& key) const
{
// return 0 for all nonobject types
- assert(not is_object() or m_value.object != nullptr);
- return is_object() ? m_value.object->count(key) : 0;
+ return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;
}
/// @}
@@ -4433,10 +11308,6 @@ class basic_json
return const_reverse_iterator(cbegin());
}
- private:
- // forward declaration
- template<typename IteratorType> class iteration_proxy;
-
public:
/*!
@brief wrapper to access iterator member functions in range-based for
@@ -4446,20 +11317,62 @@ class basic_json
reference to the JSON values is returned, so there is no access to the
underlying iterator.
+ For loop without iterator_wrapper:
+
+ @code{cpp}
+ for (auto it = j_object.begin(); it != j_object.end(); ++it)
+ {
+ std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+ }
+ @endcode
+
+ Range-based for loop without iterator proxy:
+
+ @code{cpp}
+ for (auto it : j_object)
+ {
+ // "it" is of type json::reference and has no key() member
+ std::cout << "value: " << it << '\n';
+ }
+ @endcode
+
+ Range-based for loop with iterator proxy:
+
+ @code{cpp}
+ for (auto it : json::iterator_wrapper(j_object))
+ {
+ std::cout << "key: " << it.key() << ", value:" << it.value() << '\n';
+ }
+ @endcode
+
+ @note When iterating over an array, `key()` will return the index of the
+ element as string (see example).
+
+ @param[in] ref reference to a JSON value
+ @return iteration proxy object wrapping @a ref with an interface to use in
+ range-based for loops
+
+ @liveexample{The following code shows how the wrapper is used,iterator_wrapper}
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
+
@note The name of this function is not yet final and may change in the
future.
*/
- static iteration_proxy<iterator> iterator_wrapper(reference cont)
+ static iteration_proxy<iterator> iterator_wrapper(reference ref)
{
- return iteration_proxy<iterator>(cont);
+ return iteration_proxy<iterator>(ref);
}
/*!
@copydoc iterator_wrapper(reference)
*/
- static iteration_proxy<const_iterator> iterator_wrapper(const_reference cont)
+ static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref)
{
- return iteration_proxy<const_iterator>(cont);
+ return iteration_proxy<const_iterator>(ref);
}
/// @}
@@ -4473,9 +11386,9 @@ class basic_json
/// @{
/*!
- @brief checks whether the container is empty
+ @brief checks whether the container is empty.
- Checks if a JSON value has no elements.
+ Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).
@return The return value depends on the different types and is
defined as follows:
@@ -4488,19 +11401,27 @@ class basic_json
object | result of function `object_t::empty()`
array | result of function `array_t::empty()`
+ @liveexample{The following code uses `empty()` to check if a JSON
+ object contains any elements.,empty}
+
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their `empty()` functions have constant
complexity.
+ @iterators No changes.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
+ @note This function does not return whether a string stored as JSON value
+ is empty - it returns whether the JSON container itself is empty which is
+ false in the case of a string.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
- The complexity is constant.
- Has the semantics of `begin() == end()`.
- @liveexample{The following code uses `empty()` to check if a JSON
- object contains any elements.,empty}
-
@sa @ref size() -- returns the number of elements
@since version 1.0.0
@@ -4517,13 +11438,13 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
+ // delegate call to array_t::empty()
return m_value.array->empty();
}
case value_t::object:
{
- assert(m_value.object != nullptr);
+ // delegate call to object_t::empty()
return m_value.object->empty();
}
@@ -4551,19 +11472,27 @@ class basic_json
object | result of function object_t::size()
array | result of function array_t::size()
+ @liveexample{The following code calls `size()` on the different value
+ types.,size}
+
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their size() functions have constant
complexity.
+ @iterators No changes.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
+ @note This function does not return the length of a string stored as JSON
+ value - it returns the number of elements in the JSON value which is 1 in
+ the case of a string.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
- The complexity is constant.
- Has the semantics of `std::distance(begin(), end())`.
- @liveexample{The following code calls `size()` on the different value
- types.,size}
-
@sa @ref empty() -- checks whether the container is empty
@sa @ref max_size() -- returns the maximal number of elements
@@ -4581,13 +11510,13 @@ class basic_json
case value_t::array:
{
- assert(m_value.array != nullptr);
+ // delegate call to array_t::size()
return m_value.array->size();
}
case value_t::object:
{
- assert(m_value.object != nullptr);
+ // delegate call to object_t::size()
return m_value.object->size();
}
@@ -4617,10 +11546,17 @@ class basic_json
object | result of function `object_t::max_size()`
array | result of function `array_t::max_size()`
+ @liveexample{The following code calls `max_size()` on the different value
+ types. Note the output is implementation specific.,max_size}
+
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their `max_size()` functions have constant
complexity.
+ @iterators No changes.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@requirement This function helps `basic_json` satisfying the
[Container](http://en.cppreference.com/w/cpp/concept/Container)
requirements:
@@ -4628,9 +11564,6 @@ class basic_json
- Has the semantics of returning `b.size()` where `b` is the largest
possible JSON value.
- @liveexample{The following code calls `max_size()` on the different value
- types. Note the output is implementation specific.,max_size}
-
@sa @ref size() -- returns the number of elements
@since version 1.0.0
@@ -4641,13 +11574,13 @@ class basic_json
{
case value_t::array:
{
- assert(m_value.array != nullptr);
+ // delegate call to array_t::max_size()
return m_value.array->max_size();
}
case value_t::object:
{
- assert(m_value.object != nullptr);
+ // delegate call to object_t::max_size()
return m_value.object->max_size();
}
@@ -4673,7 +11606,8 @@ class basic_json
@brief clears the contents
Clears the content of a JSON value and resets it to the default value as
- if @ref basic_json(value_t) would have been called:
+ if @ref basic_json(value_t) would have been called with the current value
+ type from @ref type():
Value type | initial value
----------- | -------------
@@ -4684,14 +11618,24 @@ class basic_json
object | `{}`
array | `[]`
- @note Floating-point numbers are set to `0.0` which will be serialized to
- `0`. The vale type remains @ref number_float_t.
-
- @complexity Linear in the size of the JSON value.
+ @post Has the same effect as calling
+ @code {.cpp}
+ *this = basic_json(type());
+ @endcode
@liveexample{The example below shows the effect of `clear()` to different
JSON types.,clear}
+ @complexity Linear in the size of the JSON value.
+
+ @iterators All iterators, pointers and references related to this container
+ are invalidated.
+
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
+ @sa @ref basic_json(value_t) -- constructor that creates an object with the
+ same value than calling `clear()`
+
@since version 1.0.0
*/
void clear() noexcept
@@ -4724,29 +11668,24 @@ class basic_json
case value_t::string:
{
- assert(m_value.string != nullptr);
m_value.string->clear();
break;
}
case value_t::array:
{
- assert(m_value.array != nullptr);
m_value.array->clear();
break;
}
case value_t::object:
{
- assert(m_value.object != nullptr);
m_value.object->clear();
break;
}
default:
- {
break;
- }
}
}
@@ -4759,7 +11698,7 @@ class basic_json
@param[in] val the value to add to the JSON array
- @throw std::domain_error when called on a type other than JSON array or
+ @throw type_error.308 when called on a type other than JSON array or
null; example: `"cannot use push_back() with number"`
@complexity Amortized constant.
@@ -4773,9 +11712,9 @@ class basic_json
void push_back(basic_json&& val)
{
// push_back only works for null objects or arrays
- if (not(is_null() or is_array()))
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- throw std::domain_error("cannot use push_back() with " + type_name());
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
}
// transform null object into an array
@@ -4783,10 +11722,10 @@ class basic_json
{
m_type = value_t::array;
m_value = value_t::array;
+ assert_invariant();
}
// add element to array (move semantics)
- assert(m_value.array != nullptr);
m_value.array->push_back(std::move(val));
// invalidate object
val.m_type = value_t::null;
@@ -4809,9 +11748,9 @@ class basic_json
void push_back(const basic_json& val)
{
// push_back only works for null objects or arrays
- if (not(is_null() or is_array()))
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
{
- throw std::domain_error("cannot use push_back() with " + type_name());
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
}
// transform null object into an array
@@ -4819,10 +11758,10 @@ class basic_json
{
m_type = value_t::array;
m_value = value_t::array;
+ assert_invariant();
}
// add element to array
- assert(m_value.array != nullptr);
m_value.array->push_back(val);
}
@@ -4845,7 +11784,7 @@ class basic_json
@param[in] val the value to add to the JSON object
- @throw std::domain_error when called on a type other than JSON object or
+ @throw type_error.308 when called on a type other than JSON object or
null; example: `"cannot use push_back() with number"`
@complexity Logarithmic in the size of the container, O(log(`size()`)).
@@ -4859,9 +11798,9 @@ class basic_json
void push_back(const typename object_t::value_type& val)
{
// push_back only works for null objects or objects
- if (not(is_null() or is_object()))
+ if (JSON_UNLIKELY(not(is_null() or is_object())))
{
- throw std::domain_error("cannot use push_back() with " + type_name());
+ JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
}
// transform null object into an object
@@ -4869,10 +11808,10 @@ class basic_json
{
m_type = value_t::object;
m_value = value_t::object;
+ assert_invariant();
}
// add element to array
- assert(m_value.object != nullptr);
m_value.object->insert(val);
}
@@ -4899,7 +11838,7 @@ class basic_json
@ref push_back(const typename object_t::value_type&). Otherwise, @a init
is converted to a JSON value and added using @ref push_back(basic_json&&).
- @param init an initializer list
+ @param[in] init an initializer list
@complexity Linear in the size of the initializer list @a init.
@@ -4911,12 +11850,13 @@ class basic_json
@liveexample{The example shows how initializer lists are treated as
objects when possible.,push_back__initializer_list}
*/
- void push_back(std::initializer_list<basic_json> init)
+ void push_back(initializer_list_t init)
{
- if (is_object() and init.size() == 2 and init.begin()->is_string())
+ if (is_object() and init.size() == 2 and (*init.begin())->is_string())
{
- const string_t key = *init.begin();
- push_back(typename object_t::value_type(key, *(init.begin() + 1)));
+ basic_json&& key = init.begin()->moved_or_copied();
+ push_back(typename object_t::value_type(
+ std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
}
else
{
@@ -4926,15 +11866,111 @@ class basic_json
/*!
@brief add an object to an object
- @copydoc push_back(std::initializer_list<basic_json>)
+ @copydoc push_back(initializer_list_t)
*/
- reference operator+=(std::initializer_list<basic_json> init)
+ reference operator+=(initializer_list_t init)
{
push_back(init);
return *this;
}
/*!
+ @brief add an object to an array
+
+ Creates a JSON value from the passed parameters @a args to the end of the
+ JSON value. If the function is called on a JSON null value, an empty array
+ is created before appending the value created from @a args.
+
+ @param[in] args arguments to forward to a constructor of @ref basic_json
+ @tparam Args compatible types to create a @ref basic_json object
+
+ @throw type_error.311 when called on a type other than JSON array or
+ null; example: `"cannot use emplace_back() with number"`
+
+ @complexity Amortized constant.
+
+ @liveexample{The example shows how `push_back()` can be used to add
+ elements to a JSON array. Note how the `null` value was silently converted
+ to a JSON array.,emplace_back}
+
+ @since version 2.0.8
+ */
+ template<class... Args>
+ void emplace_back(Args&& ... args)
+ {
+ // emplace_back only works for null objects or arrays
+ if (JSON_UNLIKELY(not(is_null() or is_array())))
+ {
+ JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name())));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_type = value_t::array;
+ m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ m_value.array->emplace_back(std::forward<Args>(args)...);
+ }
+
+ /*!
+ @brief add an object to an object if key does not exist
+
+ Inserts a new element into a JSON object constructed in-place with the
+ given @a args if there is no element with the key in the container. If the
+ function is called on a JSON null value, an empty object is created before
+ appending the value created from @a args.
+
+ @param[in] args arguments to forward to a constructor of @ref basic_json
+ @tparam Args compatible types to create a @ref basic_json object
+
+ @return a pair consisting of an iterator to the inserted element, or the
+ already-existing element if no insertion happened, and a bool
+ denoting whether the insertion took place.
+
+ @throw type_error.311 when called on a type other than JSON object or
+ null; example: `"cannot use emplace() with number"`
+
+ @complexity Logarithmic in the size of the container, O(log(`size()`)).
+
+ @liveexample{The example shows how `emplace()` can be used to add elements
+ to a JSON object. Note how the `null` value was silently converted to a
+ JSON object. Further note how no value is added if there was already one
+ value stored with the same key.,emplace}
+
+ @since version 2.0.8
+ */
+ template<class... Args>
+ std::pair<iterator, bool> emplace(Args&& ... args)
+ {
+ // emplace only works for null objects or arrays
+ if (JSON_UNLIKELY(not(is_null() or is_object())))
+ {
+ JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name())));
+ }
+
+ // transform null object into an object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value = value_t::object;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ auto res = m_value.object->emplace(std::forward<Args>(args)...);
+ // create result iterator and set iterator to the result of emplace
+ auto it = begin();
+ it.m_it.object_iterator = res.first;
+
+ // return pair of iterator and boolean
+ return {it, res.second};
+ }
+
+ /*!
@brief inserts element
Inserts element @a val before iterator @a pos.
@@ -4944,13 +11980,13 @@ class basic_json
@param[in] val element to insert
@return iterator pointing to the inserted @a val.
- @throw std::domain_error if called on JSON values other than arrays;
+ @throw type_error.309 if called on JSON values other than arrays;
example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
- @complexity Constant plus linear in the distance between pos and end of the
- container.
+ @complexity Constant plus linear in the distance between @a pos and end of
+ the container.
@liveexample{The example shows how `insert()` is used.,insert}
@@ -4959,24 +11995,21 @@ class basic_json
iterator insert(const_iterator pos, const basic_json& val)
{
// insert only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
return result;
}
- else
- {
- throw std::domain_error("cannot use insert() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
/*!
@@ -5000,10 +12033,10 @@ class basic_json
@return iterator pointing to the first element inserted, or @a pos if
`cnt==0`
- @throw std::domain_error if called on JSON values other than arrays;
- example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
+ @throw type_error.309 if called on JSON values other than arrays; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
@complexity Linear in @a cnt plus linear in the distance between @a pos
and end of the container.
@@ -5015,24 +12048,21 @@ class basic_json
iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
{
// insert only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
return result;
}
- else
- {
- throw std::domain_error("cannot use insert() with " + type_name());
- }
+
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
/*!
@@ -5045,13 +12075,13 @@ class basic_json
@param[in] first begin of the range of elements to insert
@param[in] last end of the range of elements to insert
- @throw std::domain_error if called on JSON values other than arrays;
- example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
- @throw std::domain_error if @a first and @a last do not belong to the same
- JSON value; example: `"iterators do not fit"`
- @throw std::domain_error if @a first or @a last are iterators into
+ @throw type_error.309 if called on JSON values other than arrays; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
+ @throw invalid_iterator.210 if @a first and @a last do not belong to the
+ same JSON value; example: `"iterators do not fit"`
+ @throw invalid_iterator.211 if @a first or @a last are iterators into
container for which insert is called; example: `"passed iterators may not
belong to container"`
@@ -5068,31 +12098,30 @@ class basic_json
iterator insert(const_iterator pos, const_iterator first, const_iterator last)
{
// insert only works for arrays
- if (not is_array())
+ if (JSON_UNLIKELY(not is_array()))
{
- throw std::domain_error("cannot use insert() with " + type_name());
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// check if range iterators belong to the same JSON object
- if (first.m_object != last.m_object)
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
{
- throw std::domain_error("iterators do not fit");
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
}
- if (first.m_object == this or last.m_object == this)
+ if (JSON_UNLIKELY(first.m_object == this))
{
- throw std::domain_error("passed iterators may not belong to container");
+ JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
}
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(
pos.m_it.array_iterator,
first.m_it.array_iterator,
@@ -5109,10 +12138,10 @@ class basic_json
the end() iterator
@param[in] ilist initializer list to insert the values from
- @throw std::domain_error if called on JSON values other than arrays;
- example: `"cannot use insert() with string"`
- @throw std::domain_error if @a pos is not an iterator of *this; example:
- `"iterator does not fit current value"`
+ @throw type_error.309 if called on JSON values other than arrays; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if @a pos is not an iterator of *this;
+ example: `"iterator does not fit current value"`
@return iterator pointing to the first element inserted, or @a pos if
`ilist` is empty
@@ -5124,28 +12153,177 @@ class basic_json
@since version 1.0.0
*/
- iterator insert(const_iterator pos, std::initializer_list<basic_json> ilist)
+ iterator insert(const_iterator pos, initializer_list_t ilist)
{
// insert only works for arrays
- if (not is_array())
+ if (JSON_UNLIKELY(not is_array()))
{
- throw std::domain_error("cannot use insert() with " + type_name());
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
}
// check if iterator pos fits to this JSON value
- if (pos.m_object != this)
+ if (JSON_UNLIKELY(pos.m_object != this))
{
- throw std::domain_error("iterator does not fit current value");
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
}
// insert to array and return iterator
iterator result(this);
- assert(m_value.array != nullptr);
- result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist);
+ result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist.begin(), ilist.end());
return result;
}
/*!
+ @brief inserts elements
+
+ Inserts elements from range `[first, last)`.
+
+ @param[in] first begin of the range of elements to insert
+ @param[in] last end of the range of elements to insert
+
+ @throw type_error.309 if called on JSON values other than objects; example:
+ `"cannot use insert() with string"`
+ @throw invalid_iterator.202 if iterator @a first or @a last does does not
+ point to an object; example: `"iterators first and last must point to
+ objects"`
+ @throw invalid_iterator.210 if @a first and @a last do not belong to the
+ same JSON value; example: `"iterators do not fit"`
+
+ @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number
+ of elements to insert.
+
+ @liveexample{The example shows how `insert()` is used.,insert__range_object}
+
+ @since version 3.0.0
+ */
+ void insert(const_iterator first, const_iterator last)
+ {
+ // insert only works for objects
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_UNLIKELY(not first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ }
+
+ m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+ }
+
+ /*!
+ @brief updates a JSON object from another object, overwriting existing keys
+
+ Inserts all values from JSON object @a j and overwrites existing keys.
+
+ @param[in] j JSON object to read values from
+
+ @throw type_error.312 if called on JSON values other than objects; example:
+ `"cannot use update() with string"`
+
+ @complexity O(N*log(size() + N)), where N is the number of elements to
+ insert.
+
+ @liveexample{The example shows how `update()` is used.,update}
+
+ @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
+
+ @since version 3.0.0
+ */
+ void update(const_reference j)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
+ }
+ if (JSON_UNLIKELY(not j.is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name())));
+ }
+
+ for (auto it = j.cbegin(); it != j.cend(); ++it)
+ {
+ m_value.object->operator[](it.key()) = it.value();
+ }
+ }
+
+ /*!
+ @brief updates a JSON object from another object, overwriting existing keys
+
+ Inserts all values from from range `[first, last)` and overwrites existing
+ keys.
+
+ @param[in] first begin of the range of elements to insert
+ @param[in] last end of the range of elements to insert
+
+ @throw type_error.312 if called on JSON values other than objects; example:
+ `"cannot use update() with string"`
+ @throw invalid_iterator.202 if iterator @a first or @a last does does not
+ point to an object; example: `"iterators first and last must point to
+ objects"`
+ @throw invalid_iterator.210 if @a first and @a last do not belong to the
+ same JSON value; example: `"iterators do not fit"`
+
+ @complexity O(N*log(size() + N)), where N is the number of elements to
+ insert.
+
+ @liveexample{The example shows how `update()` is used__range.,update}
+
+ @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
+
+ @since version 3.0.0
+ */
+ void update(const_iterator first, const_iterator last)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_type = value_t::object;
+ m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_UNLIKELY(not is_object()))
+ {
+ JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_UNLIKELY(not first.m_object->is_object()
+ or not first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
+ }
+
+ for (auto it = first; it != last; ++it)
+ {
+ m_value.object->operator[](it.key()) = it.value();
+ }
+ }
+
+ /*!
@brief exchanges the values
Exchanges the contents of the JSON value with those of @a other. Does not
@@ -5171,6 +12349,7 @@ class basic_json
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
+ assert_invariant();
}
/*!
@@ -5183,7 +12362,7 @@ class basic_json
@param[in,out] other array to exchange the contents with
- @throw std::domain_error when JSON value is not an array; example: `"cannot
+ @throw type_error.310 when JSON value is not an array; example: `"cannot
use swap() with string"`
@complexity Constant.
@@ -5196,14 +12375,13 @@ class basic_json
void swap(array_t& other)
{
// swap only works for arrays
- if (is_array())
+ if (JSON_LIKELY(is_array()))
{
- assert(m_value.array != nullptr);
std::swap(*(m_value.array), other);
}
else
{
- throw std::domain_error("cannot use swap() with " + type_name());
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
}
}
@@ -5217,7 +12395,7 @@ class basic_json
@param[in,out] other object to exchange the contents with
- @throw std::domain_error when JSON value is not an object; example:
+ @throw type_error.310 when JSON value is not an object; example:
`"cannot use swap() with string"`
@complexity Constant.
@@ -5230,14 +12408,13 @@ class basic_json
void swap(object_t& other)
{
// swap only works for objects
- if (is_object())
+ if (JSON_LIKELY(is_object()))
{
- assert(m_value.object != nullptr);
std::swap(*(m_value.object), other);
}
else
{
- throw std::domain_error("cannot use swap() with " + type_name());
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
}
}
@@ -5251,7 +12428,7 @@ class basic_json
@param[in,out] other string to exchange the contents with
- @throw std::domain_error when JSON value is not a string; example: `"cannot
+ @throw type_error.310 when JSON value is not a string; example: `"cannot
use swap() with boolean"`
@complexity Constant.
@@ -5264,20 +12441,19 @@ class basic_json
void swap(string_t& other)
{
// swap only works for strings
- if (is_string())
+ if (JSON_LIKELY(is_string()))
{
- assert(m_value.string != nullptr);
std::swap(*(m_value.string), other);
}
else
{
- throw std::domain_error("cannot use swap() with " + type_name());
+ JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
}
}
/// @}
-
+ public:
//////////////////////////////////////////
// lexicographical comparison operators //
//////////////////////////////////////////
@@ -5285,56 +12461,38 @@ class basic_json
/// @name lexicographical comparison operators
/// @{
- private:
- /*!
- @brief comparison operator for JSON types
-
- Returns an ordering that is similar to Python:
- - order: null < boolean < number < object < array < string
- - furthermore, each type is not smaller than itself
-
- @since version 1.0.0
- */
- friend bool operator<(const value_t lhs, const value_t rhs) noexcept
- {
- static constexpr std::array<uint8_t, 8> order = {{
- 0, // null
- 3, // object
- 4, // array
- 5, // string
- 1, // boolean
- 2, // integer
- 2, // unsigned
- 2, // float
- }
- };
-
- // discarded values are not comparable
- if (lhs == value_t::discarded or rhs == value_t::discarded)
- {
- return false;
- }
-
- return order[static_cast<std::size_t>(lhs)] < order[static_cast<std::size_t>(rhs)];
- }
-
- public:
/*!
@brief comparison: equal
Compares two JSON values for equality according to the following rules:
- Two JSON values are equal if (1) they are from the same type and (2)
- their stored values are the same.
+ their stored values are the same according to their respective
+ `operator==`.
- Integer and floating-point numbers are automatically converted before
- comparison. Floating-point numbers are compared indirectly: two
- floating-point numbers `f1` and `f2` are considered equal if neither
- `f1 > f2` nor `f2 > f1` holds.
+ comparison. Note than two NaN values are always treated as unequal.
- Two JSON null values are equal.
+ @note Floating-point inside JSON values numbers are compared with
+ `json::number_float_t::operator==` which is `double::operator==` by
+ default. To compare floating-point while respecting an epsilon, an alternative
+ [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39)
+ could be used, for instance
+ @code {.cpp}
+ template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>
+ inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept
+ {
+ return std::abs(a - b) <= epsilon;
+ }
+ @endcode
+
+ @note NaN values never compare equal to themselves or to other NaN values.
+
@param[in] lhs first JSON value to consider
@param[in] rhs second JSON value to consider
@return whether the values @a lhs and @a rhs are equal
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@complexity Linear.
@liveexample{The example demonstrates comparing several JSON
@@ -5352,72 +12510,56 @@ class basic_json
switch (lhs_type)
{
case value_t::array:
- {
- assert(lhs.m_value.array != nullptr);
- assert(rhs.m_value.array != nullptr);
- return *lhs.m_value.array == *rhs.m_value.array;
- }
+ return (*lhs.m_value.array == *rhs.m_value.array);
+
case value_t::object:
- {
- assert(lhs.m_value.object != nullptr);
- assert(rhs.m_value.object != nullptr);
- return *lhs.m_value.object == *rhs.m_value.object;
- }
+ return (*lhs.m_value.object == *rhs.m_value.object);
+
case value_t::null:
- {
return true;
- }
+
case value_t::string:
- {
- assert(lhs.m_value.string != nullptr);
- assert(rhs.m_value.string != nullptr);
- return *lhs.m_value.string == *rhs.m_value.string;
- }
+ return (*lhs.m_value.string == *rhs.m_value.string);
+
case value_t::boolean:
- {
- return lhs.m_value.boolean == rhs.m_value.boolean;
- }
+ return (lhs.m_value.boolean == rhs.m_value.boolean);
+
case value_t::number_integer:
- {
- return lhs.m_value.number_integer == rhs.m_value.number_integer;
- }
+ return (lhs.m_value.number_integer == rhs.m_value.number_integer);
+
case value_t::number_unsigned:
- {
- return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;
- }
+ return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);
+
case value_t::number_float:
- {
- return lhs.m_value.number_float == rhs.m_value.number_float;
- }
+ return (lhs.m_value.number_float == rhs.m_value.number_float);
+
default:
- {
return false;
- }
}
}
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
{
- return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;
+ return (static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float);
}
else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)
{
- return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);
+ return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer));
}
else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)
{
- return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;
+ return (static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);
}
else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)
{
- return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);
+ return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned));
}
else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)
{
- return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;
+ return (static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);
}
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)
{
- return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);
+ return (lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned));
}
return false;
@@ -5425,34 +12567,24 @@ class basic_json
/*!
@brief comparison: equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__equal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
{
- return v.is_null();
+ return (lhs == basic_json(rhs));
}
/*!
@brief comparison: equal
- @copydoc operator==(const_reference, std::nullptr_t)
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
{
- return v.is_null();
+ return (basic_json(lhs) == rhs);
}
/*!
@@ -5466,6 +12598,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__notequal}
@@ -5478,34 +12612,24 @@ class basic_json
/*!
@brief comparison: not equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `not v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is not null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__notequal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
{
- return not v.is_null();
+ return (lhs != basic_json(rhs));
}
/*!
@brief comparison: not equal
- @copydoc operator!=(const_reference, std::nullptr_t)
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
{
- return not v.is_null();
+ return (basic_json(lhs) != rhs);
}
/*!
@@ -5527,6 +12651,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__less}
@@ -5542,47 +12668,31 @@ class basic_json
switch (lhs_type)
{
case value_t::array:
- {
- assert(lhs.m_value.array != nullptr);
- assert(rhs.m_value.array != nullptr);
- return *lhs.m_value.array < *rhs.m_value.array;
- }
+ return (*lhs.m_value.array) < (*rhs.m_value.array);
+
case value_t::object:
- {
- assert(lhs.m_value.object != nullptr);
- assert(rhs.m_value.object != nullptr);
return *lhs.m_value.object < *rhs.m_value.object;
- }
+
case value_t::null:
- {
return false;
- }
+
case value_t::string:
- {
- assert(lhs.m_value.string != nullptr);
- assert(rhs.m_value.string != nullptr);
return *lhs.m_value.string < *rhs.m_value.string;
- }
+
case value_t::boolean:
- {
return lhs.m_value.boolean < rhs.m_value.boolean;
- }
+
case value_t::number_integer:
- {
return lhs.m_value.number_integer < rhs.m_value.number_integer;
- }
+
case value_t::number_unsigned:
- {
return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;
- }
+
case value_t::number_float:
- {
return lhs.m_value.number_float < rhs.m_value.number_float;
- }
+
default:
- {
return false;
- }
}
}
else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)
@@ -5617,6 +12727,28 @@ class basic_json
}
/*!
+ @brief comparison: less than
+ @copydoc operator<(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs < basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: less than
+ @copydoc operator<(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) < rhs);
+ }
+
+ /*!
@brief comparison: less than or equal
Compares whether one JSON value @a lhs is less than or equal to another
@@ -5628,6 +12760,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__greater}
@@ -5639,6 +12773,28 @@ class basic_json
}
/*!
+ @brief comparison: less than or equal
+ @copydoc operator<=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs <= basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: less than or equal
+ @copydoc operator<=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) <= rhs);
+ }
+
+ /*!
@brief comparison: greater than
Compares whether one JSON value @a lhs is greater than another
@@ -5650,6 +12806,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__lessequal}
@@ -5661,6 +12819,28 @@ class basic_json
}
/*!
+ @brief comparison: greater than
+ @copydoc operator>(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs > basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: greater than
+ @copydoc operator>(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) > rhs);
+ }
+
+ /*!
@brief comparison: greater than or equal
Compares whether one JSON value @a lhs is greater than or equal to another
@@ -5672,6 +12852,8 @@ class basic_json
@complexity Linear.
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
+
@liveexample{The example demonstrates comparing several JSON
types.,operator__greaterequal}
@@ -5682,8 +12864,29 @@ class basic_json
return not (lhs < rhs);
}
- /// @}
+ /*!
+ @brief comparison: greater than or equal
+ @copydoc operator>=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept
+ {
+ return (lhs >= basic_json(rhs));
+ }
+
+ /*!
+ @brief comparison: greater than or equal
+ @copydoc operator>=(const_reference, const_reference)
+ */
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept
+ {
+ return (basic_json(lhs) >= rhs);
+ }
+ /// @}
///////////////////
// serialization //
@@ -5696,23 +12899,32 @@ class basic_json
@brief serialize to stream
Serialize the given JSON value @a j to the output stream @a o. The JSON
- value will be serialized using the @ref dump member function. The
- indentation of the output can be controlled with the member variable
- `width` of the output stream @a o. For instance, using the manipulator
- `std::setw(4)` on @a o sets the indentation level to `4` and the
- serialization result is the same as calling `dump(4)`.
+ value will be serialized using the @ref dump member function.
+
+ - The indentation of the output can be controlled with the member variable
+ `width` of the output stream @a o. For instance, using the manipulator
+ `std::setw(4)` on @a o sets the indentation level to `4` and the
+ serialization result is the same as calling `dump(4)`.
+
+ - The indentation character can be controlled with the member variable
+ `fill` of the output stream @a o. For instance, the manipulator
+ `std::setfill('\\t')` sets indentation to use a tab character rather than
+ the default space character.
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@return the stream @a o
+ @throw type_error.316 if a string stored inside the JSON value is not
+ UTF-8 encoded
+
@complexity Linear.
@liveexample{The example below shows the serialization with different
parameters to `width` to adjust the indentation level.,operator_serialize}
- @since version 1.0.0
+ @since version 1.0.0; indentation character added in version 3.0.0
*/
friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
{
@@ -5724,14 +12936,20 @@ class basic_json
o.width(0);
// do the actual serialization
- j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
+ serializer s(detail::output_adapter<char>(o), o.fill());
+ s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
return o;
}
/*!
@brief serialize to stream
- @copydoc operator<<(std::ostream&, const basic_json&)
+ @deprecated This stream operator is deprecated and will be removed in a
+ future version of the library. Please use
+ @ref operator<<(std::ostream&, const basic_json&)
+ instead; that is, replace calls like `j >> o;` with `o << j;`.
+ @since version 1.0.0; deprecated since version 3.0.0
*/
+ JSON_DEPRECATED
friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
{
return o << j;
@@ -5748,69 +12966,179 @@ class basic_json
/// @{
/*!
- @brief deserialize from string
-
- @param[in] s string to read a serialized JSON value from
- @param[in] cb a parser callback function of type @ref parser_callback_t
+ @brief deserialize from a compatible input
+
+ This function reads from a compatible input. Examples are:
+ - an array of 1-byte values
+ - strings with character/literal type with size of 1 byte
+ - input streams
+ - container with contiguous storage of 1-byte values. Compatible container
+ types include `std::vector`, `std::string`, `std::array`,
+ `std::valarray`, and `std::initializer_list`. Furthermore, C-style
+ arrays can be used with `std::begin()`/`std::end()`. User-defined
+ containers can be used as long as they implement random-access iterators
+ and a contiguous storage.
+
+ @pre Each element of the container has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+
+ @pre The container storage is contiguous. Violating this precondition
+ yields undefined behavior. **This precondition is enforced with an
+ assertion.**
+ @pre Each element of the container has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+
+ @warning There is no way to enforce all preconditions at compile-time. If
+ the function is called with a noncompliant container and with
+ assertions switched off, the behavior is undefined and will most
+ likely yield segmentation violation.
+
+ @param[in] i input to read from
+ @param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@return result of the deserialization
+ @throw parse_error.101 if a parse error occurs; example: `""unexpected end
+ of input; expected string literal""`
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@note A UTF-8 byte order mark is silently ignored.
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from an array.,parse__array__parser_callback_t}
+
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__string__parser_callback_t}
- @sa @ref parse(std::istream&, parser_callback_t) for a version that reads
- from an input stream
+ @liveexample{The example below demonstrates the `parse()` function with
+ and without callback function.,parse__istream__parser_callback_t}
+
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
- @since version 1.0.0
+ @since version 2.0.3 (contiguous containers)
*/
- static basic_json parse(const string_t& s, parser_callback_t cb = nullptr)
+ static basic_json parse(detail::input_adapter i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true)
{
- return parser(s, cb).parse();
+ basic_json result;
+ parser(i, cb, allow_exceptions).parse(true, result);
+ return result;
}
/*!
- @brief deserialize from stream
+ @copydoc basic_json parse(detail::input_adapter, const parser_callback_t)
+ */
+ static basic_json parse(detail::input_adapter& i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ parser(i, cb, allow_exceptions).parse(true, result);
+ return result;
+ }
+
+ static bool accept(detail::input_adapter i)
+ {
+ return parser(i).accept(true);
+ }
+
+ static bool accept(detail::input_adapter& i)
+ {
+ return parser(i).accept(true);
+ }
- @param[in,out] i stream to read a serialized JSON value from
- @param[in] cb a parser callback function of type @ref parser_callback_t
+ /*!
+ @brief deserialize from an iterator range with contiguous storage
+
+ This function reads from an iterator range of a container with contiguous
+ storage of 1-byte values. Compatible container types include
+ `std::vector`, `std::string`, `std::array`, `std::valarray`, and
+ `std::initializer_list`. Furthermore, C-style arrays can be used with
+ `std::begin()`/`std::end()`. User-defined containers can be used as long
+ as they implement random-access iterators and a contiguous storage.
+
+ @pre The iterator range is contiguous. Violating this precondition yields
+ undefined behavior. **This precondition is enforced with an assertion.**
+ @pre Each element in the range has a size of 1 byte. Violating this
+ precondition yields undefined behavior. **This precondition is enforced
+ with a static assertion.**
+
+ @warning There is no way to enforce all preconditions at compile-time. If
+ the function is called with noncompliant iterators and with
+ assertions switched off, the behavior is undefined and will most
+ likely yield segmentation violation.
+
+ @tparam IteratorType iterator of container with contiguous storage
+ @param[in] first begin of the range to parse (included)
+ @param[in] last end of the range to parse (excluded)
+ @param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
+ @param[in] allow_exceptions whether to throw exceptions in case of a
+ parse error (optional, true by default)
@return result of the deserialization
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@note A UTF-8 byte order mark is silently ignored.
- @liveexample{The example below demonstrates the `parse()` function with
- and without callback function.,parse__istream__parser_callback_t}
-
- @sa @ref parse(const string_t&, parser_callback_t) for a version that
- reads from a string
+ @liveexample{The example below demonstrates the `parse()` function reading
+ from an iterator range.,parse__iteratortype__parser_callback_t}
- @since version 1.0.0
+ @since version 2.0.3
*/
- static basic_json parse(std::istream& i, parser_callback_t cb = nullptr)
+ template<class IteratorType, typename std::enable_if<
+ std::is_base_of<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
+ static basic_json parse(IteratorType first, IteratorType last,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result);
+ return result;
+ }
+
+ template<class IteratorType, typename std::enable_if<
+ std::is_base_of<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
+ static bool accept(IteratorType first, IteratorType last)
{
- return parser(i, cb).parse();
+ return parser(detail::input_adapter(first, last)).accept(true);
}
/*!
- @copydoc parse(std::istream&, parser_callback_t)
+ @brief deserialize from stream
+ @deprecated This stream operator is deprecated and will be removed in a
+ future version of the library. Please use
+ @ref operator>>(std::istream&, basic_json&)
+ instead; that is, replace calls like `j << i;` with `i >> j;`.
+ @since version 1.0.0; deprecated since version 3.0.0
*/
- static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr)
+ JSON_DEPRECATED
+ friend std::istream& operator<<(basic_json& j, std::istream& i)
{
- return parser(i, cb).parse();
+ return operator>>(i, j);
}
/*!
@@ -5821,7 +13149,9 @@ class basic_json
@param[in,out] i input stream to read a serialized JSON value from
@param[in,out] j JSON value to write the deserialized input to
- @throw std::invalid_argument in case of parse errors
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser.
@@ -5831,448 +13161,76 @@ class basic_json
@liveexample{The example below shows how a JSON value is constructed by
reading a serialization from a stream.,operator_deserialize}
- @sa parse(std::istream&, parser_callback_t) for a variant with a parser
- callback function to filter values while parsing
+ @sa parse(std::istream&, const parser_callback_t) for a variant with a
+ parser callback function to filter values while parsing
@since version 1.0.0
*/
- friend std::istream& operator<<(basic_json& j, std::istream& i)
- {
- j = parser(i).parse();
- return i;
- }
-
- /*!
- @brief deserialize from stream
- @copydoc operator<<(basic_json&, std::istream&)
- */
friend std::istream& operator>>(std::istream& i, basic_json& j)
{
- j = parser(i).parse();
+ parser(detail::input_adapter(i)).parse(false, j);
return i;
}
/// @}
-
- private:
///////////////////////////
// convenience functions //
///////////////////////////
- /// return the type as string
- string_t type_name() const noexcept
- {
- switch (m_type)
- {
- case value_t::null:
- return "null";
- case value_t::object:
- return "object";
- case value_t::array:
- return "array";
- case value_t::string:
- return "string";
- case value_t::boolean:
- return "boolean";
- case value_t::discarded:
- return "discarded";
- default:
- return "number";
- }
- }
-
/*!
- @brief calculates the extra space to escape a JSON string
-
- @param[in] s the string to escape
- @return the number of characters required to escape string @a s
-
- @complexity Linear in the length of string @a s.
- */
- static std::size_t extra_space(const string_t& s) noexcept
- {
- std::size_t result = 0;
+ @brief return the type as string
- for (const auto& c : s)
- {
- switch (c)
- {
- case '"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- {
- // from c (1 byte) to \x (2 bytes)
- result += 1;
- break;
- }
+ Returns the type name as string to be used in error messages - usually to
+ indicate that a function was called on a wrong JSON type.
- default:
- {
- if (c >= 0x00 and c <= 0x1f)
- {
- // from c (1 byte) to \uxxxx (6 bytes)
- result += 5;
- }
- break;
- }
- }
- }
+ @return a string representation of a the @a m_type member:
+ Value type | return value
+ ----------- | -------------
+ null | `"null"`
+ boolean | `"boolean"`
+ string | `"string"`
+ number | `"number"` (for all number types)
+ object | `"object"`
+ array | `"array"`
+ discarded | `"discarded"`
- return result;
- }
+ @exceptionsafety No-throw guarantee: this function never throws exceptions.
- /*!
- @brief escape a string
+ @complexity Constant.
- Escape a string by replacing certain special characters by a sequence of
- an escape character (backslash) and another character and other control
- characters by a sequence of "\u" followed by a four-digit hex
- representation.
+ @liveexample{The following code exemplifies `type_name()` for all JSON
+ types.,type_name}
- @param[in] s the string to escape
- @return the escaped string
+ @sa @ref type() -- return the type of the JSON value
+ @sa @ref operator value_t() -- return the type of the JSON value (implicit)
- @complexity Linear in the length of string @a s.
+ @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`
+ since 3.0.0
*/
- static string_t escape_string(const string_t& s)
+ const char* type_name() const noexcept
{
- const auto space = extra_space(s);
- if (space == 0)
{
- return s;
- }
-
- // create a result string of necessary size
- string_t result(s.size() + space, '\\');
- std::size_t pos = 0;
-
- for (const auto& c : s)
- {
- switch (c)
+ switch (m_type)
{
- // quotation mark (0x22)
- case '"':
- {
- result[pos + 1] = '"';
- pos += 2;
- break;
- }
-
- // reverse solidus (0x5c)
- case '\\':
- {
- // nothing to change
- pos += 2;
- break;
- }
-
- // backspace (0x08)
- case '\b':
- {
- result[pos + 1] = 'b';
- pos += 2;
- break;
- }
-
- // formfeed (0x0c)
- case '\f':
- {
- result[pos + 1] = 'f';
- pos += 2;
- break;
- }
-
- // newline (0x0a)
- case '\n':
- {
- result[pos + 1] = 'n';
- pos += 2;
- break;
- }
-
- // carriage return (0x0d)
- case '\r':
- {
- result[pos + 1] = 'r';
- pos += 2;
- break;
- }
-
- // horizontal tab (0x09)
- case '\t':
- {
- result[pos + 1] = 't';
- pos += 2;
- break;
- }
-
+ case value_t::null:
+ return "null";
+ case value_t::object:
+ return "object";
+ case value_t::array:
+ return "array";
+ case value_t::string:
+ return "string";
+ case value_t::boolean:
+ return "boolean";
+ case value_t::discarded:
+ return "discarded";
default:
- {
- if (c >= 0x00 and c <= 0x1f)
- {
- // convert a number 0..15 to its hex representation
- // (0..f)
- const auto hexify = [](const int v) -> char
- {
- return (v < 10)
- ? ('0' + static_cast<char>(v))
- : ('a' + static_cast<char>((v - 10) & 0x1f));
- };
-
- // print character c as \uxxxx
- for (const char m :
- { 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f)
- })
- {
- result[++pos] = m;
- }
-
- ++pos;
- }
- else
- {
- // all other characters are added as-is
- result[pos++] = c;
- }
- break;
- }
+ return "number";
}
}
-
- return result;
}
- /*!
- @brief internal implementation of the serialization function
-
- This function is called by the public member function dump and organizes
- the serialization internally. The indentation level is propagated as
- additional parameter. In case of arrays and objects, the function is
- called recursively. Note that
-
- - strings and object keys are escaped using `escape_string()`
- - integer numbers are converted implicitly via `operator<<`
- - floating-point numbers are converted to a string using `"%g"` format
-
- @param[out] o stream to write to
- @param[in] pretty_print whether the output shall be pretty-printed
- @param[in] indent_step the indent level
- @param[in] current_indent the current indent level (only used internally)
- */
- void dump(std::ostream& o,
- const bool pretty_print,
- const unsigned int indent_step,
- const unsigned int current_indent = 0) const
- {
- // variable to hold indentation for recursive calls
- unsigned int new_indent = current_indent;
-
- switch (m_type)
- {
- case value_t::object:
- {
- assert(m_value.object != nullptr);
-
- if (m_value.object->empty())
- {
- o << "{}";
- return;
- }
-
- o << "{";
-
- // increase indentation
- if (pretty_print)
- {
- new_indent += indent_step;
- o << "\n";
- }
-
- for (auto i = m_value.object->cbegin(); i != m_value.object->cend(); ++i)
- {
- if (i != m_value.object->cbegin())
- {
- o << (pretty_print ? ",\n" : ",");
- }
- o << string_t(new_indent, ' ') << "\""
- << escape_string(i->first) << "\":"
- << (pretty_print ? " " : "");
- i->second.dump(o, pretty_print, indent_step, new_indent);
- }
-
- // decrease indentation
- if (pretty_print)
- {
- new_indent -= indent_step;
- o << "\n";
- }
-
- o << string_t(new_indent, ' ') + "}";
- return;
- }
-
- case value_t::array:
- {
- assert(m_value.array != nullptr);
-
- if (m_value.array->empty())
- {
- o << "[]";
- return;
- }
-
- o << "[";
-
- // increase indentation
- if (pretty_print)
- {
- new_indent += indent_step;
- o << "\n";
- }
-
- for (auto i = m_value.array->cbegin(); i != m_value.array->cend(); ++i)
- {
- if (i != m_value.array->cbegin())
- {
- o << (pretty_print ? ",\n" : ",");
- }
- o << string_t(new_indent, ' ');
- i->dump(o, pretty_print, indent_step, new_indent);
- }
-
- // decrease indentation
- if (pretty_print)
- {
- new_indent -= indent_step;
- o << "\n";
- }
-
- o << string_t(new_indent, ' ') << "]";
- return;
- }
-
- case value_t::string:
- {
- assert(m_value.string != nullptr);
- o << string_t("\"") << escape_string(*m_value.string) << "\"";
- return;
- }
-
- case value_t::boolean:
- {
- o << (m_value.boolean ? "true" : "false");
- return;
- }
-
- case value_t::number_integer:
- {
- o << m_value.number_integer;
- return;
- }
-
- case value_t::number_unsigned:
- {
- o << m_value.number_unsigned;
- return;
- }
-
- case value_t::number_float:
- {
- // check if number was parsed from a string
- if (m_type.bits.parsed)
- {
- // check if parsed number had an exponent given
- if (m_type.bits.has_exp)
- {
- // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
- char buf[263];
- int len;
-
- // handle capitalization of the exponent
- if (m_type.bits.exp_cap)
- {
- len = snprintf(buf, sizeof(buf), "%.*E",
- m_type.bits.precision, m_value.number_float) + 1;
- }
- else
- {
- len = snprintf(buf, sizeof(buf), "%.*e",
- m_type.bits.precision, m_value.number_float) + 1;
- }
-
- // remove '+' sign from the exponent if necessary
- if (not m_type.bits.exp_plus)
- {
- if (len > static_cast<int>(sizeof(buf)))
- {
- len = sizeof(buf);
- }
- for (int i = 0; i < len; i++)
- {
- if (buf[i] == '+')
- {
- for (; i + 1 < len; i++)
- {
- buf[i] = buf[i + 1];
- }
- }
- }
- }
-
- o << buf;
- }
- else
- {
- // no exponent - output as a decimal
- std::stringstream ss;
- ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
- ss << std::setprecision(m_type.bits.precision)
- << std::fixed << m_value.number_float;
- o << ss.str();
- }
- }
- else
- {
- if (m_value.number_float == 0)
- {
- // special case for zero to get "0.0"/"-0.0"
- o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
- }
- else
- {
- // Otherwise 6, 15 or 16 digits of precision allows
- // round-trip IEEE 754 string->float->string,
- // string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- std::stringstream ss;
- ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
- ss << std::setprecision(std::numeric_limits<double>::digits10)
- << m_value.number_float;
- o << ss.str();
- }
- }
- return;
- }
-
- case value_t::discarded:
- {
- o << "<discarded>";
- return;
- }
-
- case value_t::null:
- {
- o << "null";
- return;
- }
- }
- }
private:
//////////////////////
@@ -6280,3163 +13238,412 @@ class basic_json
//////////////////////
/// the type of the current element
- type_data_t m_type = value_t::null;
+ value_t m_type = value_t::null;
/// the value of the current element
json_value m_value = {};
+ //////////////////////////////////////////
+ // binary serialization/deserialization //
+ //////////////////////////////////////////
- private:
- ///////////////
- // iterators //
- ///////////////
+ /// @name binary serialization/deserialization support
+ /// @{
+ public:
/*!
- @brief an iterator for primitive JSON types
-
- This class models an iterator for primitive JSON types (boolean, number,
- string). It's only purpose is to allow the iterator/const_iterator classes
- to "iterate" over primitive values. Internally, the iterator is modeled by
- a `difference_type` variable. Value begin_value (`0`) models the begin,
- end_value (`1`) models past the end.
- */
- class primitive_iterator_t
- {
- public:
- /// set iterator to a defined beginning
- void set_begin() noexcept
- {
- m_it = begin_value;
- }
-
- /// set iterator to a defined past the end
- void set_end() noexcept
- {
- m_it = end_value;
- }
-
- /// return whether the iterator can be dereferenced
- constexpr bool is_begin() const noexcept
- {
- return (m_it == begin_value);
- }
-
- /// return whether the iterator is at end
- constexpr bool is_end() const noexcept
- {
- return (m_it == end_value);
- }
+ @brief create a CBOR serialization of a given JSON value
+
+ Serializes a given JSON value @a j to a byte vector using the CBOR (Concise
+ Binary Object Representation) serialization format. CBOR is a binary
+ serialization format which aims to be more compact than JSON itself, yet
+ more efficient to parse.
+
+ The library uses the following mapping from JSON values types to
+ CBOR types according to the CBOR specification (RFC 7049):
+
+ JSON value type | value/range | CBOR type | first byte
+ --------------- | ------------------------------------------ | ---------------------------------- | ---------------
+ null | `null` | Null | 0xF6
+ boolean | `true` | True | 0xF5
+ boolean | `false` | False | 0xF4
+ number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B
+ number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A
+ number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39
+ number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38
+ number_integer | -24..-1 | Negative integer | 0x20..0x37
+ number_integer | 0..23 | Integer | 0x00..0x17
+ number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18
+ number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19
+ number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A
+ number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B
+ number_unsigned | 0..23 | Integer | 0x00..0x17
+ number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18
+ number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19
+ number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A
+ number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B
+ number_float | *any value* | Double-Precision Float | 0xFB
+ string | *length*: 0..23 | UTF-8 string | 0x60..0x77
+ string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78
+ string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79
+ string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A
+ string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B
+ array | *size*: 0..23 | array | 0x80..0x97
+ array | *size*: 23..255 | array (1 byte follow) | 0x98
+ array | *size*: 256..65535 | array (2 bytes follow) | 0x99
+ array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A
+ array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B
+ object | *size*: 0..23 | map | 0xA0..0xB7
+ object | *size*: 23..255 | map (1 byte follow) | 0xB8
+ object | *size*: 256..65535 | map (2 bytes follow) | 0xB9
+ object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA
+ object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB
+
+ @note The mapping is **complete** in the sense that any JSON value type
+ can be converted to a CBOR value.
+
+ @note If NaN or Infinity are stored inside a JSON number, they are
+ serialized properly. This behavior differs from the @ref dump()
+ function which serializes NaN or Infinity to `null`.
+
+ @note The following CBOR types are not used in the conversion:
+ - byte strings (0x40..0x5F)
+ - UTF-8 strings terminated by "break" (0x7F)
+ - arrays terminated by "break" (0x9F)
+ - maps terminated by "break" (0xBF)
+ - date/time (0xC0..0xC1)
+ - bignum (0xC2..0xC3)
+ - decimal fraction (0xC4)
+ - bigfloat (0xC5)
+ - tagged items (0xC6..0xD4, 0xD8..0xDB)
+ - expected conversions (0xD5..0xD7)
+ - simple values (0xE0..0xF3, 0xF8)
+ - undefined (0xF7)
+ - half and single-precision floats (0xF9-0xFA)
+ - break (0xFF)
- /// return reference to the value to change and compare
- operator difference_type& () noexcept
- {
- return m_it;
- }
-
- /// return value to compare
- constexpr operator difference_type () const noexcept
- {
- return m_it;
- }
+ @param[in] j JSON value to serialize
+ @return MessagePack serialization as byte vector
- private:
- static constexpr difference_type begin_value = 0;
- static constexpr difference_type end_value = begin_value + 1;
+ @complexity Linear in the size of the JSON value @a j.
- /// iterator as signed integer type
- difference_type m_it = std::numeric_limits<std::ptrdiff_t>::denorm_min();
- };
+ @liveexample{The example shows the serialization of a JSON value to a byte
+ vector in CBOR format.,to_cbor}
- /*!
- @brief an iterator value
+ @sa http://cbor.io
+ @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
+ @sa @ref to_msgpack(const basic_json&) for the related MessagePack format
- @note This structure could easily be a union, but MSVC currently does not
- allow unions members with complex constructors, see
- https://github.com/nlohmann/json/pull/105.
+ @since version 2.0.9
*/
- struct internal_iterator
- {
- /// iterator for JSON objects
- typename object_t::iterator object_iterator;
- /// iterator for JSON arrays
- typename array_t::iterator array_iterator;
- /// generic iterator for all other types
- primitive_iterator_t primitive_iterator;
-
- /// create an uninitialized internal_iterator
- internal_iterator() noexcept
- : object_iterator(), array_iterator(), primitive_iterator()
- {}
- };
-
- /// proxy class for the iterator_wrapper functions
- template<typename IteratorType>
- class iteration_proxy
+ static std::vector<uint8_t> to_cbor(const basic_json& j)
{
- private:
- /// helper class for iteration
- class iteration_proxy_internal
- {
- private:
- /// the iterator
- IteratorType anchor;
- /// an index for arrays (used to create key names)
- size_t array_index = 0;
-
- public:
- explicit iteration_proxy_internal(IteratorType it) noexcept
- : anchor(it)
- {}
-
- /// dereference operator (needed for range-based for)
- iteration_proxy_internal& operator*()
- {
- return *this;
- }
-
- /// increment operator (needed for range-based for)
- iteration_proxy_internal& operator++()
- {
- ++anchor;
- ++array_index;
-
- return *this;
- }
-
- /// inequality operator (needed for range-based for)
- bool operator!= (const iteration_proxy_internal& o) const
- {
- return anchor != o.anchor;
- }
-
- /// return key of the iterator
- typename basic_json::string_t key() const
- {
- assert(anchor.m_object != nullptr);
-
- switch (anchor.m_object->type())
- {
- // use integer array index as key
- case value_t::array:
- {
- return std::to_string(array_index);
- }
-
- // use key from the object
- case value_t::object:
- {
- return anchor.key();
- }
-
- // use an empty key for all primitive types
- default:
- {
- return "";
- }
- }
- }
-
- /// return value of the iterator
- typename IteratorType::reference value() const
- {
- return anchor.value();
- }
- };
-
- /// the container to iterate
- typename IteratorType::reference container;
-
- public:
- /// construct iteration proxy from a container
- explicit iteration_proxy(typename IteratorType::reference cont)
- : container(cont)
- {}
-
- /// return iterator begin (needed for range-based for)
- iteration_proxy_internal begin() noexcept
- {
- return iteration_proxy_internal(container.begin());
- }
-
- /// return iterator end (needed for range-based for)
- iteration_proxy_internal end() noexcept
- {
- return iteration_proxy_internal(container.end());
- }
- };
-
- public:
- /*!
- @brief a const random access iterator for the @ref basic_json class
-
- This class implements a const iterator for the @ref basic_json class. From
- this class, the @ref iterator class is derived.
-
- @requirement The class satisfies the following concept requirements:
- - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
- The iterator that can be moved to point (forward and backward) to any
- element in constant time.
+ std::vector<uint8_t> result;
+ to_cbor(j, result);
+ return result;
+ }
- @since version 1.0.0
- */
- class const_iterator : public std::iterator<std::random_access_iterator_tag, const basic_json>
+ static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)
{
- /// allow basic_json to access private members
- friend class basic_json;
-
- public:
- /// the type of the values when the iterator is dereferenced
- using value_type = typename basic_json::value_type;
- /// a type to represent differences between iterators
- using difference_type = typename basic_json::difference_type;
- /// defines a pointer to the type iterated over (value_type)
- using pointer = typename basic_json::const_pointer;
- /// defines a reference to the type iterated over (value_type)
- using reference = typename basic_json::const_reference;
- /// the category of the iterator
- using iterator_category = std::bidirectional_iterator_tag;
-
- /// default constructor
- const_iterator() = default;
-
- /// constructor for a given JSON instance
- explicit const_iterator(pointer object) noexcept
- : m_object(object)
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- m_it.object_iterator = typename object_t::iterator();
- break;
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator = typename array_t::iterator();
- break;
- }
-
- default:
- {
- m_it.primitive_iterator = primitive_iterator_t();
- break;
- }
- }
- }
-
- /// copy constructor given a nonconst iterator
- explicit const_iterator(const iterator& other) noexcept
- : m_object(other.m_object)
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- m_it.object_iterator = other.m_it.object_iterator;
- break;
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator = other.m_it.array_iterator;
- break;
- }
-
- default:
- {
- m_it.primitive_iterator = other.m_it.primitive_iterator;
- break;
- }
- }
- }
-
- /// copy constructor
- const_iterator(const const_iterator& other) noexcept
- : m_object(other.m_object), m_it(other.m_it)
- {}
-
- /// copy assignment
- const_iterator& operator=(const_iterator other) noexcept(
- std::is_nothrow_move_constructible<pointer>::value and
- std::is_nothrow_move_assignable<pointer>::value and
- std::is_nothrow_move_constructible<internal_iterator>::value and
- std::is_nothrow_move_assignable<internal_iterator>::value
- )
- {
- std::swap(m_object, other.m_object);
- std::swap(m_it, other.m_it);
- return *this;
- }
-
- private:
- /// set the iterator to the first value
- void set_begin() noexcept
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- assert(m_object->m_value.object != nullptr);
- m_it.object_iterator = m_object->m_value.object->begin();
- break;
- }
-
- case basic_json::value_t::array:
- {
- assert(m_object->m_value.array != nullptr);
- m_it.array_iterator = m_object->m_value.array->begin();
- break;
- }
-
- case basic_json::value_t::null:
- {
- // set to end so begin()==end() is true: null is empty
- m_it.primitive_iterator.set_end();
- break;
- }
-
- default:
- {
- m_it.primitive_iterator.set_begin();
- break;
- }
- }
- }
-
- /// set the iterator past the last value
- void set_end() noexcept
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- assert(m_object->m_value.object != nullptr);
- m_it.object_iterator = m_object->m_value.object->end();
- break;
- }
-
- case basic_json::value_t::array:
- {
- assert(m_object->m_value.array != nullptr);
- m_it.array_iterator = m_object->m_value.array->end();
- break;
- }
-
- default:
- {
- m_it.primitive_iterator.set_end();
- break;
- }
- }
- }
-
- public:
- /// return a reference to the value pointed to by the iterator
- reference operator*() const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- assert(m_object->m_value.object);
- assert(m_it.object_iterator != m_object->m_value.object->end());
- return m_it.object_iterator->second;
- }
-
- case basic_json::value_t::array:
- {
- assert(m_object->m_value.array);
- assert(m_it.array_iterator != m_object->m_value.array->end());
- return *m_it.array_iterator;
- }
-
- case basic_json::value_t::null:
- {
- throw std::out_of_range("cannot get value");
- }
-
- default:
- {
- if (m_it.primitive_iterator.is_begin())
- {
- return *m_object;
- }
- else
- {
- throw std::out_of_range("cannot get value");
- }
- }
- }
- }
-
- /// dereference the iterator
- pointer operator->() const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- assert(m_object->m_value.object);
- assert(m_it.object_iterator != m_object->m_value.object->end());
- return &(m_it.object_iterator->second);
- }
-
- case basic_json::value_t::array:
- {
- assert(m_object->m_value.array);
- assert(m_it.array_iterator != m_object->m_value.array->end());
- return &*m_it.array_iterator;
- }
-
- default:
- {
- if (m_it.primitive_iterator.is_begin())
- {
- return m_object;
- }
- else
- {
- throw std::out_of_range("cannot get value");
- }
- }
- }
- }
-
- /// post-increment (it++)
- const_iterator operator++(int)
- {
- auto result = *this;
- ++(*this);
- return result;
- }
-
- /// pre-increment (++it)
- const_iterator& operator++()
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- ++m_it.object_iterator;
- break;
- }
-
- case basic_json::value_t::array:
- {
- ++m_it.array_iterator;
- break;
- }
-
- default:
- {
- ++m_it.primitive_iterator;
- break;
- }
- }
-
- return *this;
- }
-
- /// post-decrement (it--)
- const_iterator operator--(int)
- {
- auto result = *this;
- --(*this);
- return result;
- }
-
- /// pre-decrement (--it)
- const_iterator& operator--()
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- --m_it.object_iterator;
- break;
- }
-
- case basic_json::value_t::array:
- {
- --m_it.array_iterator;
- break;
- }
-
- default:
- {
- --m_it.primitive_iterator;
- break;
- }
- }
-
- return *this;
- }
-
- /// comparison: equal
- bool operator==(const const_iterator& other) const
- {
- // if objects are not the same, the comparison is undefined
- if (m_object != other.m_object)
- {
- throw std::domain_error("cannot compare iterators of different containers");
- }
-
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- return (m_it.object_iterator == other.m_it.object_iterator);
- }
-
- case basic_json::value_t::array:
- {
- return (m_it.array_iterator == other.m_it.array_iterator);
- }
-
- default:
- {
- return (m_it.primitive_iterator == other.m_it.primitive_iterator);
- }
- }
- }
-
- /// comparison: not equal
- bool operator!=(const const_iterator& other) const
- {
- return not operator==(other);
- }
-
- /// comparison: smaller
- bool operator<(const const_iterator& other) const
- {
- // if objects are not the same, the comparison is undefined
- if (m_object != other.m_object)
- {
- throw std::domain_error("cannot compare iterators of different containers");
- }
-
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot compare order of object iterators");
- }
-
- case basic_json::value_t::array:
- {
- return (m_it.array_iterator < other.m_it.array_iterator);
- }
-
- default:
- {
- return (m_it.primitive_iterator < other.m_it.primitive_iterator);
- }
- }
- }
-
- /// comparison: less than or equal
- bool operator<=(const const_iterator& other) const
- {
- return not other.operator < (*this);
- }
-
- /// comparison: greater than
- bool operator>(const const_iterator& other) const
- {
- return not operator<=(other);
- }
-
- /// comparison: greater than or equal
- bool operator>=(const const_iterator& other) const
- {
- return not operator<(other);
- }
-
- /// add to iterator
- const_iterator& operator+=(difference_type i)
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot use offsets with object iterators");
- }
-
- case basic_json::value_t::array:
- {
- m_it.array_iterator += i;
- break;
- }
-
- default:
- {
- m_it.primitive_iterator += i;
- break;
- }
- }
-
- return *this;
- }
-
- /// subtract from iterator
- const_iterator& operator-=(difference_type i)
- {
- return operator+=(-i);
- }
-
- /// add to iterator
- const_iterator operator+(difference_type i)
- {
- auto result = *this;
- result += i;
- return result;
- }
-
- /// subtract from iterator
- const_iterator operator-(difference_type i)
- {
- auto result = *this;
- result -= i;
- return result;
- }
-
- /// return difference
- difference_type operator-(const const_iterator& other) const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot use offsets with object iterators");
- }
-
- case basic_json::value_t::array:
- {
- return m_it.array_iterator - other.m_it.array_iterator;
- }
-
- default:
- {
- return m_it.primitive_iterator - other.m_it.primitive_iterator;
- }
- }
- }
-
- /// access to successor
- reference operator[](difference_type n) const
- {
- assert(m_object != nullptr);
-
- switch (m_object->m_type)
- {
- case basic_json::value_t::object:
- {
- throw std::domain_error("cannot use operator[] for object iterators");
- }
-
- case basic_json::value_t::array:
- {
- return *(m_it.array_iterator + n);
- }
-
- case basic_json::value_t::null:
- {
- throw std::out_of_range("cannot get value");
- }
-
- default:
- {
- if (m_it.primitive_iterator == -n)
- {
- return *m_object;
- }
- else
- {
- throw std::out_of_range("cannot get value");
- }
- }
- }
- }
-
- /// return the key of an object iterator
- typename object_t::key_type key() const
- {
- assert(m_object != nullptr);
-
- if (m_object->is_object())
- {
- return m_it.object_iterator->first;
- }
- else
- {
- throw std::domain_error("cannot use key() for non-object iterators");
- }
- }
-
- /// return the value of an iterator
- reference value() const
- {
- return operator*();
- }
-
- private:
- /// associated JSON instance
- pointer m_object = nullptr;
- /// the actual iterator of the associated instance
- internal_iterator m_it = internal_iterator();
- };
-
- /*!
- @brief a mutable random access iterator for the @ref basic_json class
-
- @requirement The class satisfies the following concept requirements:
- - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
- The iterator that can be moved to point (forward and backward) to any
- element in constant time.
- - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
- It is possible to write to the pointed-to element.
+ binary_writer<uint8_t>(o).write_cbor(j);
+ }
- @since version 1.0.0
- */
- class iterator : public const_iterator
+ static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
{
- public:
- using base_iterator = const_iterator;
- using pointer = typename basic_json::pointer;
- using reference = typename basic_json::reference;
-
- /// default constructor
- iterator() = default;
-
- /// constructor for a given JSON instance
- explicit iterator(pointer object) noexcept
- : base_iterator(object)
- {}
-
- /// copy constructor
- iterator(const iterator& other) noexcept
- : base_iterator(other)
- {}
-
- /// copy assignment
- iterator& operator=(iterator other) noexcept(
- std::is_nothrow_move_constructible<pointer>::value and
- std::is_nothrow_move_assignable<pointer>::value and
- std::is_nothrow_move_constructible<internal_iterator>::value and
- std::is_nothrow_move_assignable<internal_iterator>::value
- )
- {
- base_iterator::operator=(other);
- return *this;
- }
-
- /// return a reference to the value pointed to by the iterator
- reference operator*() const
- {
- return const_cast<reference>(base_iterator::operator*());
- }
-
- /// dereference the iterator
- pointer operator->() const
- {
- return const_cast<pointer>(base_iterator::operator->());
- }
-
- /// post-increment (it++)
- iterator operator++(int)
- {
- iterator result = *this;
- base_iterator::operator++();
- return result;
- }
-
- /// pre-increment (++it)
- iterator& operator++()
- {
- base_iterator::operator++();
- return *this;
- }
-
- /// post-decrement (it--)
- iterator operator--(int)
- {
- iterator result = *this;
- base_iterator::operator--();
- return result;
- }
-
- /// pre-decrement (--it)
- iterator& operator--()
- {
- base_iterator::operator--();
- return *this;
- }
-
- /// add to iterator
- iterator& operator+=(difference_type i)
- {
- base_iterator::operator+=(i);
- return *this;
- }
-
- /// subtract from iterator
- iterator& operator-=(difference_type i)
- {
- base_iterator::operator-=(i);
- return *this;
- }
-
- /// add to iterator
- iterator operator+(difference_type i)
- {
- auto result = *this;
- result += i;
- return result;
- }
-
- /// subtract from iterator
- iterator operator-(difference_type i)
- {
- auto result = *this;
- result -= i;
- return result;
- }
-
- /// return difference
- difference_type operator-(const iterator& other) const
- {
- return base_iterator::operator-(other);
- }
-
- /// access to successor
- reference operator[](difference_type n) const
- {
- return const_cast<reference>(base_iterator::operator[](n));
- }
-
- /// return the value of an iterator
- reference value() const
- {
- return const_cast<reference>(base_iterator::value());
- }
- };
+ binary_writer<char>(o).write_cbor(j);
+ }
/*!
- @brief a template for a reverse iterator class
-
- @tparam Base the base iterator type to reverse. Valid types are @ref
- iterator (to create @ref reverse_iterator) and @ref const_iterator (to
- create @ref const_reverse_iterator).
-
- @requirement The class satisfies the following concept requirements:
- - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
- The iterator that can be moved to point (forward and backward) to any
- element in constant time.
- - [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
- It is possible to write to the pointed-to element (only if @a Base is
- @ref iterator).
-
- @since version 1.0.0
- */
- template<typename Base>
- class json_reverse_iterator : public std::reverse_iterator<Base>
- {
- public:
- /// shortcut to the reverse iterator adaptor
- using base_iterator = std::reverse_iterator<Base>;
- /// the reference type for the pointed-to element
- using reference = typename Base::reference;
-
- /// create reverse iterator from iterator
- json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
- : base_iterator(it)
- {}
-
- /// create reverse iterator from base class
- json_reverse_iterator(const base_iterator& it) noexcept
- : base_iterator(it)
- {}
+ @brief create a MessagePack serialization of a given JSON value
- /// post-increment (it++)
- json_reverse_iterator operator++(int)
- {
- return base_iterator::operator++(1);
- }
+ Serializes a given JSON value @a j to a byte vector using the MessagePack
+ serialization format. MessagePack is a binary serialization format which
+ aims to be more compact than JSON itself, yet more efficient to parse.
- /// pre-increment (++it)
- json_reverse_iterator& operator++()
- {
- base_iterator::operator++();
- return *this;
- }
+ The library uses the following mapping from JSON values types to
+ MessagePack types according to the MessagePack specification:
- /// post-decrement (it--)
- json_reverse_iterator operator--(int)
- {
- return base_iterator::operator--(1);
- }
+ JSON value type | value/range | MessagePack type | first byte
+ --------------- | --------------------------------- | ---------------- | ----------
+ null | `null` | nil | 0xC0
+ boolean | `true` | true | 0xC3
+ boolean | `false` | false | 0xC2
+ number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3
+ number_integer | -2147483648..-32769 | int32 | 0xD2
+ number_integer | -32768..-129 | int16 | 0xD1
+ number_integer | -128..-33 | int8 | 0xD0
+ number_integer | -32..-1 | negative fixint | 0xE0..0xFF
+ number_integer | 0..127 | positive fixint | 0x00..0x7F
+ number_integer | 128..255 | uint 8 | 0xCC
+ number_integer | 256..65535 | uint 16 | 0xCD
+ number_integer | 65536..4294967295 | uint 32 | 0xCE
+ number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF
+ number_unsigned | 0..127 | positive fixint | 0x00..0x7F
+ number_unsigned | 128..255 | uint 8 | 0xCC
+ number_unsigned | 256..65535 | uint 16 | 0xCD
+ number_unsigned | 65536..4294967295 | uint 32 | 0xCE
+ number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF
+ number_float | *any value* | float 64 | 0xCB
+ string | *length*: 0..31 | fixstr | 0xA0..0xBF
+ string | *length*: 32..255 | str 8 | 0xD9
+ string | *length*: 256..65535 | str 16 | 0xDA
+ string | *length*: 65536..4294967295 | str 32 | 0xDB
+ array | *size*: 0..15 | fixarray | 0x90..0x9F
+ array | *size*: 16..65535 | array 16 | 0xDC
+ array | *size*: 65536..4294967295 | array 32 | 0xDD
+ object | *size*: 0..15 | fix map | 0x80..0x8F
+ object | *size*: 16..65535 | map 16 | 0xDE
+ object | *size*: 65536..4294967295 | map 32 | 0xDF
- /// pre-decrement (--it)
- json_reverse_iterator& operator--()
- {
- base_iterator::operator--();
- return *this;
- }
+ @note The mapping is **complete** in the sense that any JSON value type
+ can be converted to a MessagePack value.
- /// add to iterator
- json_reverse_iterator& operator+=(difference_type i)
- {
- base_iterator::operator+=(i);
- return *this;
- }
+ @note The following values can **not** be converted to a MessagePack value:
+ - strings with more than 4294967295 bytes
+ - arrays with more than 4294967295 elements
+ - objects with more than 4294967295 elements
- /// add to iterator
- json_reverse_iterator operator+(difference_type i) const
- {
- auto result = *this;
- result += i;
- return result;
- }
+ @note The following MessagePack types are not used in the conversion:
+ - bin 8 - bin 32 (0xC4..0xC6)
+ - ext 8 - ext 32 (0xC7..0xC9)
+ - float 32 (0xCA)
+ - fixext 1 - fixext 16 (0xD4..0xD8)
- /// subtract from iterator
- json_reverse_iterator operator-(difference_type i) const
- {
- auto result = *this;
- result -= i;
- return result;
- }
+ @note Any MessagePack output created @ref to_msgpack can be successfully
+ parsed by @ref from_msgpack.
- /// return difference
- difference_type operator-(const json_reverse_iterator& other) const
- {
- return this->base() - other.base();
- }
+ @note If NaN or Infinity are stored inside a JSON number, they are
+ serialized properly. This behavior differs from the @ref dump()
+ function which serializes NaN or Infinity to `null`.
- /// access to successor
- reference operator[](difference_type n) const
- {
- return *(this->operator+(n));
- }
-
- /// return the key of an object iterator
- typename object_t::key_type key() const
- {
- auto it = --this->base();
- return it.key();
- }
+ @param[in] j JSON value to serialize
+ @return MessagePack serialization as byte vector
- /// return the value of an iterator
- reference value() const
- {
- auto it = --this->base();
- return it.operator * ();
- }
- };
+ @complexity Linear in the size of the JSON value @a j.
+ @liveexample{The example shows the serialization of a JSON value to a byte
+ vector in MessagePack format.,to_msgpack}
- private:
- //////////////////////
- // lexer and parser //
- //////////////////////
+ @sa http://msgpack.org
+ @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
+ @sa @ref to_cbor(const basic_json& for the related CBOR format
- /*!
- @brief lexical analysis
-
- This class organizes the lexical analysis during JSON deserialization. The
- core of it is a scanner generated by [re2c](http://re2c.org) that
- processes a buffer and recognizes tokens according to RFC 7159.
+ @since version 2.0.9
*/
- class lexer
+ static std::vector<uint8_t> to_msgpack(const basic_json& j)
{
- public:
- /// token types for the parser
- enum class token_type
- {
- uninitialized, ///< indicating the scanner is uninitialized
- literal_true, ///< the `true` literal
- literal_false, ///< the `false` literal
- literal_null, ///< the `null` literal
- value_string, ///< a string -- use get_string() for actual value
- value_number, ///< a number -- use get_number() for actual value
- begin_array, ///< the character for array begin `[`
- begin_object, ///< the character for object begin `{`
- end_array, ///< the character for array end `]`
- end_object, ///< the character for object end `}`
- name_separator, ///< the name separator `:`
- value_separator, ///< the value separator `,`
- parse_error, ///< indicating a parse error
- end_of_input ///< indicating the end of the input buffer
- };
-
- /// the char type to use in the lexer
- using lexer_char_t = unsigned char;
-
- /// constructor with a given buffer
- explicit lexer(const string_t& s) noexcept
- : m_stream(nullptr), m_buffer(s)
- {
- m_content = reinterpret_cast<const lexer_char_t*>(s.c_str());
- assert(m_content != nullptr);
- m_start = m_cursor = m_content;
- m_limit = m_content + s.size();
- }
-
- /// constructor with a given stream
- explicit lexer(std::istream* s) noexcept
- : m_stream(s), m_buffer()
- {
- assert(m_stream != nullptr);
- getline(*m_stream, m_buffer);
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
- assert(m_content != nullptr);
- m_start = m_cursor = m_content;
- m_limit = m_content + m_buffer.size();
- }
-
- /// default constructor
- lexer() = default;
-
- // switch off unwanted functions
- lexer(const lexer&) = delete;
- lexer operator=(const lexer&) = delete;
-
- /*!
- @brief create a string from a Unicode code point
-
- @param[in] codepoint1 the code point (can be high surrogate)
- @param[in] codepoint2 the code point (can be low surrogate or 0)
-
- @return string representation of the code point
-
- @throw std::out_of_range if code point is > 0x10ffff; example: `"code
- points above 0x10FFFF are invalid"`
- @throw std::invalid_argument if the low surrogate is invalid; example:
- `""missing or wrong low surrogate""`
-
- @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code>
- */
- static string_t to_unicode(const std::size_t codepoint1,
- const std::size_t codepoint2 = 0)
- {
- // calculate the codepoint from the given code points
- std::size_t codepoint = codepoint1;
-
- // check if codepoint1 is a high surrogate
- if (codepoint1 >= 0xD800 and codepoint1 <= 0xDBFF)
- {
- // check if codepoint2 is a low surrogate
- if (codepoint2 >= 0xDC00 and codepoint2 <= 0xDFFF)
- {
- codepoint =
- // high surrogate occupies the most significant 22 bits
- (codepoint1 << 10)
- // low surrogate occupies the least significant 15 bits
- + codepoint2
- // there is still the 0xD800, 0xDC00 and 0x10000 noise
- // in the result so we have to subtract with:
- // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
- - 0x35FDC00;
- }
- else
- {
- throw std::invalid_argument("missing or wrong low surrogate");
- }
- }
-
- string_t result;
-
- if (codepoint < 0x80)
- {
- // 1-byte characters: 0xxxxxxx (ASCII)
- result.append(1, static_cast<typename string_t::value_type>(codepoint));
- }
- else if (codepoint <= 0x7ff)
- {
- // 2-byte characters: 110xxxxx 10xxxxxx
- result.append(1, static_cast<typename string_t::value_type>(0xC0 | ((codepoint >> 6) & 0x1F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F)));
- }
- else if (codepoint <= 0xffff)
- {
- // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
- result.append(1, static_cast<typename string_t::value_type>(0xE0 | ((codepoint >> 12) & 0x0F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F)));
- }
- else if (codepoint <= 0x10ffff)
- {
- // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- result.append(1, static_cast<typename string_t::value_type>(0xF0 | ((codepoint >> 18) & 0x07)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 12) & 0x3F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | ((codepoint >> 6) & 0x3F)));
- result.append(1, static_cast<typename string_t::value_type>(0x80 | (codepoint & 0x3F)));
- }
- else
- {
- throw std::out_of_range("code points above 0x10FFFF are invalid");
- }
-
- return result;
- }
-
- /// return name of values of type token_type (only used for errors)
- static std::string token_type_name(token_type t)
- {
- switch (t)
- {
- case token_type::uninitialized:
- return "<uninitialized>";
- case token_type::literal_true:
- return "true literal";
- case token_type::literal_false:
- return "false literal";
- case token_type::literal_null:
- return "null literal";
- case token_type::value_string:
- return "string literal";
- case token_type::value_number:
- return "number literal";
- case token_type::begin_array:
- return "'['";
- case token_type::begin_object:
- return "'{'";
- case token_type::end_array:
- return "']'";
- case token_type::end_object:
- return "'}'";
- case token_type::name_separator:
- return "':'";
- case token_type::value_separator:
- return "','";
- case token_type::parse_error:
- return "<parse error>";
- case token_type::end_of_input:
- return "end of input";
- default:
- {
- // catch non-enum values
- return "unknown token"; // LCOV_EXCL_LINE
- }
- }
- }
-
- /*!
- This function implements a scanner for JSON. It is specified using
- regular expressions that try to follow RFC 7159 as close as possible.
- These regular expressions are then translated into a minimized
- deterministic finite automaton (DFA) by the tool
- [re2c](http://re2c.org). As a result, the translated code for this
- function consists of a large block of code with `goto` jumps.
-
- @return the class of the next token read from the buffer
- */
- token_type scan() noexcept
- {
- // pointer for backtracking information
- m_marker = nullptr;
-
- // remember the begin of the token
- m_start = m_cursor;
- assert(m_start != nullptr);
-
-
- {
- lexer_char_t yych;
- unsigned int yyaccept = 0;
- static const unsigned char yybm[] =
- {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 32, 32, 0, 0, 32, 0, 0,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 160, 128, 0, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 192, 192, 192, 192, 192, 192, 192, 192,
- 192, 192, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 0, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- };
- if ((m_limit - m_cursor) < 5)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 32)
- {
- goto basic_json_parser_6;
- }
- if (yych <= '\\')
- {
- if (yych <= '-')
- {
- if (yych <= '"')
- {
- if (yych <= 0x00)
- {
- goto basic_json_parser_2;
- }
- if (yych <= '!')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_9;
- }
- else
- {
- if (yych <= '+')
- {
- goto basic_json_parser_4;
- }
- if (yych <= ',')
- {
- goto basic_json_parser_10;
- }
- goto basic_json_parser_12;
- }
- }
- else
- {
- if (yych <= '9')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_4;
- }
- if (yych <= '0')
- {
- goto basic_json_parser_13;
- }
- goto basic_json_parser_15;
- }
- else
- {
- if (yych <= ':')
- {
- goto basic_json_parser_17;
- }
- if (yych == '[')
- {
- goto basic_json_parser_19;
- }
- goto basic_json_parser_4;
- }
- }
- }
- else
- {
- if (yych <= 't')
- {
- if (yych <= 'f')
- {
- if (yych <= ']')
- {
- goto basic_json_parser_21;
- }
- if (yych <= 'e')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_23;
- }
- else
- {
- if (yych == 'n')
- {
- goto basic_json_parser_24;
- }
- if (yych <= 's')
- {
- goto basic_json_parser_4;
- }
- goto basic_json_parser_25;
- }
- }
- else
- {
- if (yych <= '|')
- {
- if (yych == '{')
- {
- goto basic_json_parser_26;
- }
- goto basic_json_parser_4;
- }
- else
- {
- if (yych <= '}')
- {
- goto basic_json_parser_28;
- }
- if (yych == 0xEF)
- {
- goto basic_json_parser_30;
- }
- goto basic_json_parser_4;
- }
- }
- }
-basic_json_parser_2:
- ++m_cursor;
- {
- return token_type::end_of_input;
- }
-basic_json_parser_4:
- ++m_cursor;
-basic_json_parser_5:
- {
- return token_type::parse_error;
- }
-basic_json_parser_6:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 32)
- {
- goto basic_json_parser_6;
- }
- {
- return scan();
- }
-basic_json_parser_9:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych <= 0x0F)
- {
- goto basic_json_parser_5;
- }
- goto basic_json_parser_32;
-basic_json_parser_10:
- ++m_cursor;
- {
- return token_type::value_separator;
- }
-basic_json_parser_12:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_5;
- }
- if (yych <= '0')
- {
- goto basic_json_parser_13;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_15;
- }
- goto basic_json_parser_5;
-basic_json_parser_13:
- yyaccept = 1;
- yych = *(m_marker = ++m_cursor);
- if (yych <= 'D')
- {
- if (yych == '.')
- {
- goto basic_json_parser_37;
- }
- }
- else
- {
- if (yych <= 'E')
- {
- goto basic_json_parser_38;
- }
- if (yych == 'e')
- {
- goto basic_json_parser_38;
- }
- }
-basic_json_parser_14:
- {
- return token_type::value_number;
- }
-basic_json_parser_15:
- yyaccept = 1;
- m_marker = ++m_cursor;
- if ((m_limit - m_cursor) < 3)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yybm[0 + yych] & 64)
- {
- goto basic_json_parser_15;
- }
- if (yych <= 'D')
- {
- if (yych == '.')
- {
- goto basic_json_parser_37;
- }
- goto basic_json_parser_14;
- }
- else
- {
- if (yych <= 'E')
- {
- goto basic_json_parser_38;
- }
- if (yych == 'e')
- {
- goto basic_json_parser_38;
- }
- goto basic_json_parser_14;
- }
-basic_json_parser_17:
- ++m_cursor;
- {
- return token_type::name_separator;
- }
-basic_json_parser_19:
- ++m_cursor;
- {
- return token_type::begin_array;
- }
-basic_json_parser_21:
- ++m_cursor;
- {
- return token_type::end_array;
- }
-basic_json_parser_23:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'a')
- {
- goto basic_json_parser_39;
- }
- goto basic_json_parser_5;
-basic_json_parser_24:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'u')
- {
- goto basic_json_parser_40;
- }
- goto basic_json_parser_5;
-basic_json_parser_25:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 'r')
- {
- goto basic_json_parser_41;
- }
- goto basic_json_parser_5;
-basic_json_parser_26:
- ++m_cursor;
- {
- return token_type::begin_object;
- }
-basic_json_parser_28:
- ++m_cursor;
- {
- return token_type::end_object;
- }
-basic_json_parser_30:
- yyaccept = 0;
- yych = *(m_marker = ++m_cursor);
- if (yych == 0xBB)
- {
- goto basic_json_parser_42;
- }
- goto basic_json_parser_5;
-basic_json_parser_31:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
-basic_json_parser_32:
- if (yybm[0 + yych] & 128)
- {
- goto basic_json_parser_31;
- }
- if (yych <= 0x0F)
- {
- goto basic_json_parser_33;
- }
- if (yych <= '"')
- {
- goto basic_json_parser_34;
- }
- goto basic_json_parser_36;
-basic_json_parser_33:
- m_cursor = m_marker;
- if (yyaccept == 0)
- {
- goto basic_json_parser_5;
- }
- else
- {
- goto basic_json_parser_14;
- }
-basic_json_parser_34:
- ++m_cursor;
- {
- return token_type::value_string;
- }
-basic_json_parser_36:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= 'e')
- {
- if (yych <= '/')
- {
- if (yych == '"')
- {
- goto basic_json_parser_31;
- }
- if (yych <= '.')
- {
- goto basic_json_parser_33;
- }
- goto basic_json_parser_31;
- }
- else
- {
- if (yych <= '\\')
- {
- if (yych <= '[')
- {
- goto basic_json_parser_33;
- }
- goto basic_json_parser_31;
- }
- else
- {
- if (yych == 'b')
- {
- goto basic_json_parser_31;
- }
- goto basic_json_parser_33;
- }
- }
- }
- else
- {
- if (yych <= 'q')
- {
- if (yych <= 'f')
- {
- goto basic_json_parser_31;
- }
- if (yych == 'n')
- {
- goto basic_json_parser_31;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 's')
- {
- if (yych <= 'r')
- {
- goto basic_json_parser_31;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 't')
- {
- goto basic_json_parser_31;
- }
- if (yych <= 'u')
- {
- goto basic_json_parser_43;
- }
- goto basic_json_parser_33;
- }
- }
- }
-basic_json_parser_37:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_44;
- }
- goto basic_json_parser_33;
-basic_json_parser_38:
- yych = *++m_cursor;
- if (yych <= ',')
- {
- if (yych == '+')
- {
- goto basic_json_parser_46;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= '-')
- {
- goto basic_json_parser_46;
- }
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_47;
- }
- goto basic_json_parser_33;
- }
-basic_json_parser_39:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_49;
- }
- goto basic_json_parser_33;
-basic_json_parser_40:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_50;
- }
- goto basic_json_parser_33;
-basic_json_parser_41:
- yych = *++m_cursor;
- if (yych == 'u')
- {
- goto basic_json_parser_51;
- }
- goto basic_json_parser_33;
-basic_json_parser_42:
- yych = *++m_cursor;
- if (yych == 0xBF)
- {
- goto basic_json_parser_52;
- }
- goto basic_json_parser_33;
-basic_json_parser_43:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_54;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_54;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_33;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_54;
- }
- goto basic_json_parser_33;
- }
-basic_json_parser_44:
- yyaccept = 1;
- m_marker = ++m_cursor;
- if ((m_limit - m_cursor) < 3)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= 'D')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_14;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_44;
- }
- goto basic_json_parser_14;
- }
- else
- {
- if (yych <= 'E')
- {
- goto basic_json_parser_38;
- }
- if (yych == 'e')
- {
- goto basic_json_parser_38;
- }
- goto basic_json_parser_14;
- }
-basic_json_parser_46:
- yych = *++m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych >= ':')
- {
- goto basic_json_parser_33;
- }
-basic_json_parser_47:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '/')
- {
- goto basic_json_parser_14;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_47;
- }
- goto basic_json_parser_14;
-basic_json_parser_49:
- yych = *++m_cursor;
- if (yych == 's')
- {
- goto basic_json_parser_55;
- }
- goto basic_json_parser_33;
-basic_json_parser_50:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_56;
- }
- goto basic_json_parser_33;
-basic_json_parser_51:
- yych = *++m_cursor;
- if (yych == 'e')
- {
- goto basic_json_parser_58;
- }
- goto basic_json_parser_33;
-basic_json_parser_52:
- ++m_cursor;
- {
- return scan();
- }
-basic_json_parser_54:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_60;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_60;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_33;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_60;
- }
- goto basic_json_parser_33;
- }
-basic_json_parser_55:
- yych = *++m_cursor;
- if (yych == 'e')
- {
- goto basic_json_parser_61;
- }
- goto basic_json_parser_33;
-basic_json_parser_56:
- ++m_cursor;
- {
- return token_type::literal_null;
- }
-basic_json_parser_58:
- ++m_cursor;
- {
- return token_type::literal_true;
- }
-basic_json_parser_60:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_63;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_63;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_33;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_63;
- }
- goto basic_json_parser_33;
- }
-basic_json_parser_61:
- ++m_cursor;
- {
- return token_type::literal_false;
- }
-basic_json_parser_63:
- ++m_cursor;
- if (m_limit <= m_cursor)
- {
- yyfill(); // LCOV_EXCL_LINE;
- }
- yych = *m_cursor;
- if (yych <= '@')
- {
- if (yych <= '/')
- {
- goto basic_json_parser_33;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_31;
- }
- goto basic_json_parser_33;
- }
- else
- {
- if (yych <= 'F')
- {
- goto basic_json_parser_31;
- }
- if (yych <= '`')
- {
- goto basic_json_parser_33;
- }
- if (yych <= 'f')
- {
- goto basic_json_parser_31;
- }
- goto basic_json_parser_33;
- }
- }
-
- }
-
- /// append data from the stream to the internal buffer
- void yyfill() noexcept
- {
- if (m_stream == nullptr or not * m_stream)
- {
- return;
- }
-
- const auto offset_start = m_start - m_content;
- const auto offset_marker = m_marker - m_start;
- const auto offset_cursor = m_cursor - m_start;
-
- m_buffer.erase(0, static_cast<size_t>(offset_start));
- std::string line;
- assert(m_stream != nullptr);
- std::getline(*m_stream, line);
- m_buffer += "\n" + line; // add line with newline symbol
-
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
- assert(m_content != nullptr);
- m_start = m_content;
- m_marker = m_start + offset_marker;
- m_cursor = m_start + offset_cursor;
- m_limit = m_start + m_buffer.size() - 1;
- }
-
- /// return string representation of last read token
- string_t get_token() const
- {
- assert(m_start != nullptr);
- return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start),
- static_cast<size_t>(m_cursor - m_start));
- }
-
- /*!
- @brief return string value for string tokens
-
- The function iterates the characters between the opening and closing
- quotes of the string value. The complete string is the range
- [m_start,m_cursor). Consequently, we iterate from m_start+1 to
- m_cursor-1.
-
- We differentiate two cases:
-
- 1. Escaped characters. In this case, a new character is constructed
- according to the nature of the escape. Some escapes create new
- characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied
- as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape
- `"\\uxxxx"` need special care. In this case, to_unicode takes care
- of the construction of the values.
- 2. Unescaped characters are copied as is.
-
- @return string value of current token without opening and closing
- quotes
- @throw std::out_of_range if to_unicode fails
- */
- string_t get_string() const
- {
- string_t result;
- result.reserve(static_cast<size_t>(m_cursor - m_start - 2));
-
- // iterate the result between the quotes
- for (const lexer_char_t* i = m_start + 1; i < m_cursor - 1; ++i)
- {
- // process escaped characters
- if (*i == '\\')
- {
- // read next character
- ++i;
-
- switch (*i)
- {
- // the default escapes
- case 't':
- {
- result += "\t";
- break;
- }
- case 'b':
- {
- result += "\b";
- break;
- }
- case 'f':
- {
- result += "\f";
- break;
- }
- case 'n':
- {
- result += "\n";
- break;
- }
- case 'r':
- {
- result += "\r";
- break;
- }
- case '\\':
- {
- result += "\\";
- break;
- }
- case '/':
- {
- result += "/";
- break;
- }
- case '"':
- {
- result += "\"";
- break;
- }
-
- // unicode
- case 'u':
- {
- // get code xxxx from uxxxx
- auto codepoint = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>(i + 1),
- 4).c_str(), nullptr, 16);
-
- // check if codepoint is a high surrogate
- if (codepoint >= 0xD800 and codepoint <= 0xDBFF)
- {
- // make sure there is a subsequent unicode
- if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
- {
- throw std::invalid_argument("missing low surrogate");
- }
-
- // get code yyyy from uxxxx\uyyyy
- auto codepoint2 = std::strtoul(std::string(reinterpret_cast<typename string_t::const_pointer>
- (i + 7), 4).c_str(), nullptr, 16);
- result += to_unicode(codepoint, codepoint2);
- // skip the next 10 characters (xxxx\uyyyy)
- i += 10;
- }
- else
- {
- // add unicode character(s)
- result += to_unicode(codepoint);
- // skip the next four characters (xxxx)
- i += 4;
- }
- break;
- }
- }
- }
- else
- {
- // all other characters are just copied to the end of the
- // string
- result.append(1, static_cast<typename string_t::value_type>(*i));
- }
- }
-
- return result;
- }
-
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in] type the @ref number_float_t in use
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
-
- @bug This function uses `std::strtof`, `std::strtod`, or `std::strtold`
- which use the current C locale to determine which character is used as
- decimal point character. This may yield to parse errors if the locale
- does not used `.`.
- */
- long double str_to_float_t(long double* /* type */, char** endptr) const
- {
- return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
-
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in] type the @ref number_float_t in use
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- double str_to_float_t(double* /* type */, char** endptr) const
- {
- return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
-
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in] type the @ref number_float_t in use
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- float str_to_float_t(float* /* type */, char** endptr) const
- {
- return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
-
- /*!
- @brief return number value for number tokens
-
- This function translates the last token into the most appropriate
- number type (either integer, unsigned integer or floating point),
- which is passed back to the caller via the result parameter.
-
- This function parses the integer component up to the radix point or
- exponent while collecting information about the 'floating point
- representation', which it stores in the result parameter. If there is
- no radix point or exponent, and the number can fit into a @ref
- number_integer_t or @ref number_unsigned_t then it sets the result
- parameter accordingly.
-
- The 'floating point representation' includes the number of significant
- figures after the radix point, whether the number is in exponential or
- decimal form, the capitalization of the exponent marker, and if the
- optional '+' is present in the exponent. This information is necessary
- to perform accurate round trips of floating point numbers.
-
- If the number is a floating point number the number is then parsed
- using @a std:strtod (or @a std:strtof or @a std::strtold).
-
- @param[out] result @ref basic_json object to receive the number, or
- NAN if the conversion read past the current token. The latter case
- needs to be treated by the caller function.
- */
- void get_number(basic_json& result) const
- {
- assert(m_start != nullptr);
-
- const lexer::lexer_char_t* curptr = m_start;
-
- // remember this number was parsed (for later serialization)
- result.m_type.bits.parsed = true;
+ std::vector<uint8_t> result;
+ to_msgpack(j, result);
+ return result;
+ }
- // 'found_radix_point' will be set to 0xFF upon finding a radix
- // point and later used to mask in/out the precision depending
- // whether a radix is found i.e. 'precision &= found_radix_point'
- uint8_t found_radix_point = 0;
- uint8_t precision = 0;
+ static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)
+ {
+ binary_writer<uint8_t>(o).write_msgpack(j);
+ }
+
+ static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_msgpack(j);
+ }
+
+ /*!
+ @brief create a JSON value from an input in CBOR format
+
+ Deserializes a given input @a i to a JSON value using the CBOR (Concise
+ Binary Object Representation) serialization format.
- // accumulate the integer conversion result (unsigned for now)
- number_unsigned_t value = 0;
+ The library maps CBOR types to JSON value types as follows:
- // maximum absolute value of the relevant integer type
- number_unsigned_t max;
+ CBOR type | JSON value type | first byte
+ ---------------------- | --------------- | ----------
+ Integer | number_unsigned | 0x00..0x17
+ Unsigned integer | number_unsigned | 0x18
+ Unsigned integer | number_unsigned | 0x19
+ Unsigned integer | number_unsigned | 0x1A
+ Unsigned integer | number_unsigned | 0x1B
+ Negative integer | number_integer | 0x20..0x37
+ Negative integer | number_integer | 0x38
+ Negative integer | number_integer | 0x39
+ Negative integer | number_integer | 0x3A
+ Negative integer | number_integer | 0x3B
+ Negative integer | number_integer | 0x40..0x57
+ UTF-8 string | string | 0x60..0x77
+ UTF-8 string | string | 0x78
+ UTF-8 string | string | 0x79
+ UTF-8 string | string | 0x7A
+ UTF-8 string | string | 0x7B
+ UTF-8 string | string | 0x7F
+ array | array | 0x80..0x97
+ array | array | 0x98
+ array | array | 0x99
+ array | array | 0x9A
+ array | array | 0x9B
+ array | array | 0x9F
+ map | object | 0xA0..0xB7
+ map | object | 0xB8
+ map | object | 0xB9
+ map | object | 0xBA
+ map | object | 0xBB
+ map | object | 0xBF
+ False | `false` | 0xF4
+ True | `true` | 0xF5
+ Nill | `null` | 0xF6
+ Half-Precision Float | number_float | 0xF9
+ Single-Precision Float | number_float | 0xFA
+ Double-Precision Float | number_float | 0xFB
+
+ @warning The mapping is **incomplete** in the sense that not all CBOR
+ types can be converted to a JSON value. The following CBOR types
+ are not supported and will yield parse errors (parse_error.112):
+ - byte strings (0x40..0x5F)
+ - date/time (0xC0..0xC1)
+ - bignum (0xC2..0xC3)
+ - decimal fraction (0xC4)
+ - bigfloat (0xC5)
+ - tagged items (0xC6..0xD4, 0xD8..0xDB)
+ - expected conversions (0xD5..0xD7)
+ - simple values (0xE0..0xF3, 0xF8)
+ - undefined (0xF7)
+
+ @warning CBOR allows map keys of any type, whereas JSON only allows
+ strings as keys in object values. Therefore, CBOR maps with keys
+ other than UTF-8 strings are rejected (parse_error.113).
+
+ @note Any CBOR output created @ref to_cbor can be successfully parsed by
+ @ref from_cbor.
+
+ @param[in] i an input in CBOR format convertible to an input adapter
+ @param[in] strict whether to expect the input to be consumed until EOF
+ (true by default)
+ @return deserialized JSON value
+
+ @throw parse_error.110 if the given input ends prematurely or the end of
+ file was not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported features from CBOR were
+ used in the given input @a v or if the input is not valid CBOR
+ @throw parse_error.113 if a string was expected as map key, but not found
+
+ @complexity Linear in the size of the input @a i.
+
+ @liveexample{The example shows the deserialization of a byte vector in CBOR
+ format to a JSON value.,from_cbor}
+
+ @sa http://cbor.io
+ @sa @ref to_cbor(const basic_json&) for the analogous serialization
+ @sa @ref from_msgpack(detail::input_adapter, const bool) for the
+ related MessagePack format
+
+ @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
+ consume input adapters, removed start_index parameter, and added
+ @a strict parameter since 3.0.0
+ */
+ static basic_json from_cbor(detail::input_adapter i,
+ const bool strict = true)
+ {
+ return binary_reader(i).parse_cbor(strict);
+ }
+
+ /*!
+ @copydoc from_cbor(detail::input_adapter, const bool)
+ */
+ template<typename A1, typename A2,
+ detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
+ static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true)
+ {
+ return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_cbor(strict);
+ }
+
+ /*!
+ @brief create a JSON value from an input in MessagePack format
+
+ Deserializes a given input @a i to a JSON value using the MessagePack
+ serialization format.
+
+ The library maps MessagePack types to JSON value types as follows:
+
+ MessagePack type | JSON value type | first byte
+ ---------------- | --------------- | ----------
+ positive fixint | number_unsigned | 0x00..0x7F
+ fixmap | object | 0x80..0x8F
+ fixarray | array | 0x90..0x9F
+ fixstr | string | 0xA0..0xBF
+ nil | `null` | 0xC0
+ false | `false` | 0xC2
+ true | `true` | 0xC3
+ float 32 | number_float | 0xCA
+ float 64 | number_float | 0xCB
+ uint 8 | number_unsigned | 0xCC
+ uint 16 | number_unsigned | 0xCD
+ uint 32 | number_unsigned | 0xCE
+ uint 64 | number_unsigned | 0xCF
+ int 8 | number_integer | 0xD0
+ int 16 | number_integer | 0xD1
+ int 32 | number_integer | 0xD2
+ int 64 | number_integer | 0xD3
+ str 8 | string | 0xD9
+ str 16 | string | 0xDA
+ str 32 | string | 0xDB
+ array 16 | array | 0xDC
+ array 32 | array | 0xDD
+ map 16 | object | 0xDE
+ map 32 | object | 0xDF
+ negative fixint | number_integer | 0xE0-0xFF
- // temporarily store the type to avoid unecessary bitfield access
- value_t type;
+ @warning The mapping is **incomplete** in the sense that not all
+ MessagePack types can be converted to a JSON value. The following
+ MessagePack types are not supported and will yield parse errors:
+ - bin 8 - bin 32 (0xC4..0xC6)
+ - ext 8 - ext 32 (0xC7..0xC9)
+ - fixext 1 - fixext 16 (0xD4..0xD8)
- // look for sign
- if (*curptr == '-')
- {
- type = value_t::number_integer;
- max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
- curptr++;
- }
- else
- {
- type = value_t::number_unsigned;
- max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
- }
+ @note Any MessagePack output created @ref to_msgpack can be successfully
+ parsed by @ref from_msgpack.
- // count the significant figures
- for (; curptr < m_cursor; curptr++)
- {
- // quickly skip tests if a digit
- if (*curptr < '0' || *curptr > '9')
- {
- if (*curptr == '.')
- {
- // don't count '.' but change to float
- type = value_t::number_float;
+ @param[in] i an input in MessagePack format convertible to an input
+ adapter
+ @param[in] strict whether to expect the input to be consumed until EOF
+ (true by default)
- // reset precision count
- precision = 0;
- found_radix_point = 0xFF;
- continue;
- }
- // assume exponent (if not then will fail parse): change to
- // float, stop counting and record exponent details
- type = value_t::number_float;
- result.m_type.bits.has_exp = true;
+ @throw parse_error.110 if the given input ends prematurely or the end of
+ file was not reached when @a strict was set to true
+ @throw parse_error.112 if unsupported features from MessagePack were
+ used in the given input @a i or if the input is not valid MessagePack
+ @throw parse_error.113 if a string was expected as map key, but not found
- // exponent capitalization
- result.m_type.bits.exp_cap = (*curptr == 'E');
+ @complexity Linear in the size of the input @a i.
- // exponent '+' sign
- result.m_type.bits.exp_plus = (*(++curptr) == '+');
- break;
- }
+ @liveexample{The example shows the deserialization of a byte vector in
+ MessagePack format to a JSON value.,from_msgpack}
- // skip if definitely not an integer
- if (type != value_t::number_float)
- {
- // multiply last value by ten and add the new digit
- auto temp = value * 10 + *curptr - 0x30;
+ @sa http://msgpack.org
+ @sa @ref to_msgpack(const basic_json&) for the analogous serialization
+ @sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR
+ format
- // test for overflow
- if (temp < value || temp > max)
- {
- // overflow
- type = value_t::number_float;
- }
- else
- {
- // no overflow - save it
- value = temp;
- }
- }
- ++precision;
- }
-
- // If no radix point was found then precision would now be set to
- // the number of digits, which is wrong - clear it.
- result.m_type.bits.precision = precision & found_radix_point;
-
- // save the value (if not a float)
- if (type == value_t::number_unsigned)
- {
- result.m_value.number_unsigned = value;
- }
- else if (type == value_t::number_integer)
- {
- result.m_value.number_integer = -static_cast<number_integer_t>(value);
- }
- else
- {
- // parse with strtod
- result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), NULL);
- }
-
- // save the type
- result.m_type = type;
- }
-
- private:
- /// optional input stream
- std::istream* m_stream = nullptr;
- /// the buffer
- string_t m_buffer;
- /// the buffer pointer
- const lexer_char_t* m_content = nullptr;
- /// pointer to the beginning of the current symbol
- const lexer_char_t* m_start = nullptr;
- /// pointer for backtracking information
- const lexer_char_t* m_marker = nullptr;
- /// pointer to the current symbol
- const lexer_char_t* m_cursor = nullptr;
- /// pointer to the end of the buffer
- const lexer_char_t* m_limit = nullptr;
- };
-
- /*!
- @brief syntax analysis
-
- This class implements a recursive decent parser.
+ @since version 2.0.9; parameter @a start_index since 2.1.1; changed to
+ consume input adapters, removed start_index parameter, and added
+ @a strict parameter since 3.0.0
*/
- class parser
+ static basic_json from_msgpack(detail::input_adapter i,
+ const bool strict = true)
{
- public:
- /// constructor for strings
- parser(const string_t& s, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
- {
- // read first token
- get_token();
- }
-
- /// a parser reading from an input stream
- parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
- {
- // read first token
- get_token();
- }
-
- /// public parser interface
- basic_json parse()
- {
- basic_json result = parse_internal(true);
-
- expect(lexer::token_type::end_of_input);
-
- // return parser result and replace it with null in case the
- // top-level value was discarded by the callback function
- return result.is_discarded() ? basic_json() : result;
- }
-
- private:
- /// the actual parser
- basic_json parse_internal(bool keep)
- {
- auto result = basic_json(value_t::discarded);
-
- switch (last_token)
- {
- case lexer::token_type::begin_object:
- {
- if (keep and (not callback or (keep = callback(depth++, parse_event_t::object_start, result))))
- {
- // explicitly set result to object to cope with {}
- result.m_type = value_t::object;
- result.m_value = json_value(value_t::object);
- }
-
- // read next token
- get_token();
-
- // closing } -> we are done
- if (last_token == lexer::token_type::end_object)
- {
- get_token();
- if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
- {
- result = basic_json(value_t::discarded);
- }
- return result;
- }
-
- // no comma is expected here
- unexpect(lexer::token_type::value_separator);
-
- // otherwise: parse key-value pairs
- do
- {
- // ugly, but could be fixed with loop reorganization
- if (last_token == lexer::token_type::value_separator)
- {
- get_token();
- }
-
- // store key
- expect(lexer::token_type::value_string);
- const auto key = m_lexer.get_string();
-
- bool keep_tag = false;
- if (keep)
- {
- if (callback)
- {
- basic_json k(key);
- keep_tag = callback(depth, parse_event_t::key, k);
- }
- else
- {
- keep_tag = true;
- }
- }
-
- // parse separator (:)
- get_token();
- expect(lexer::token_type::name_separator);
-
- // parse and add value
- get_token();
- auto value = parse_internal(keep);
- if (keep and keep_tag and not value.is_discarded())
- {
- result[key] = std::move(value);
- }
- }
- while (last_token == lexer::token_type::value_separator);
-
- // closing }
- expect(lexer::token_type::end_object);
- get_token();
- if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
- {
- result = basic_json(value_t::discarded);
- }
-
- return result;
- }
-
- case lexer::token_type::begin_array:
- {
- if (keep and (not callback or (keep = callback(depth++, parse_event_t::array_start, result))))
- {
- // explicitly set result to object to cope with []
- result.m_type = value_t::array;
- result.m_value = json_value(value_t::array);
- }
-
- // read next token
- get_token();
-
- // closing ] -> we are done
- if (last_token == lexer::token_type::end_array)
- {
- get_token();
- if (callback and not callback(--depth, parse_event_t::array_end, result))
- {
- result = basic_json(value_t::discarded);
- }
- return result;
- }
-
- // no comma is expected here
- unexpect(lexer::token_type::value_separator);
-
- // otherwise: parse values
- do
- {
- // ugly, but could be fixed with loop reorganization
- if (last_token == lexer::token_type::value_separator)
- {
- get_token();
- }
-
- // parse value
- auto value = parse_internal(keep);
- if (keep and not value.is_discarded())
- {
- result.push_back(std::move(value));
- }
- }
- while (last_token == lexer::token_type::value_separator);
-
- // closing ]
- expect(lexer::token_type::end_array);
- get_token();
- if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
- {
- result = basic_json(value_t::discarded);
- }
-
- return result;
- }
-
- case lexer::token_type::literal_null:
- {
- get_token();
- result.m_type = value_t::null;
- break;
- }
-
- case lexer::token_type::value_string:
- {
- const auto s = m_lexer.get_string();
- get_token();
- result = basic_json(s);
- break;
- }
-
- case lexer::token_type::literal_true:
- {
- get_token();
- result.m_type = value_t::boolean;
- result.m_value = true;
- break;
- }
-
- case lexer::token_type::literal_false:
- {
- get_token();
- result.m_type = value_t::boolean;
- result.m_value = false;
- break;
- }
-
- case lexer::token_type::value_number:
- {
- m_lexer.get_number(result);
- get_token();
- break;
- }
-
- default:
- {
- // the last token was unexpected
- unexpect(last_token);
- }
- }
-
- if (keep and callback and not callback(depth, parse_event_t::value, result))
- {
- result = basic_json(value_t::discarded);
- }
- return result;
- }
-
- /// get next token from lexer
- typename lexer::token_type get_token() noexcept
- {
- last_token = m_lexer.scan();
- return last_token;
- }
-
- void expect(typename lexer::token_type t) const
- {
- if (t != last_token)
- {
- std::string error_msg = "parse error - unexpected ";
- error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") :
- lexer::token_type_name(last_token));
- error_msg += "; expected " + lexer::token_type_name(t);
- throw std::invalid_argument(error_msg);
- }
- }
-
- void unexpect(typename lexer::token_type t) const
- {
- if (t == last_token)
- {
- std::string error_msg = "parse error - unexpected ";
- error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") :
- lexer::token_type_name(last_token));
- throw std::invalid_argument(error_msg);
- }
- }
-
- private:
- /// current level of recursion
- int depth = 0;
- /// callback function
- parser_callback_t callback;
- /// the type of the last read token
- typename lexer::token_type last_token = lexer::token_type::uninitialized;
- /// the lexer
- lexer m_lexer;
- };
+ return binary_reader(i).parse_msgpack(strict);
+ }
- public:
/*!
- @brief JSON Pointer
-
- A JSON pointer defines a string syntax for identifying a specific value
- within a JSON document. It can be used with functions `at` and
- `operator[]`. Furthermore, JSON pointers are the base for JSON patches.
-
- @sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
-
- @since version 2.0.0
+ @copydoc from_msgpack(detail::input_adapter, const bool)
*/
- class json_pointer
+ template<typename A1, typename A2,
+ detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
+ static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true)
{
- /// allow basic_json to access private members
- friend class basic_json;
-
- public:
- /*!
- @brief create JSON pointer
-
- Create a JSON pointer according to the syntax described in
- [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
-
- @param[in] s string representing the JSON pointer; if omitted, the
- empty string is assumed which references the whole JSON
- value
-
- @throw std::domain_error if reference token is nonempty and does not
- begin with a slash (`/`); example: `"JSON pointer must be empty or
- begin with /"`
- @throw std::domain_error if a tilde (`~`) is not followed by `0`
- (representing `~`) or `1` (representing `/`); example: `"escape error:
- ~ must be followed with 0 or 1"`
-
- @liveexample{The example shows the construction several valid JSON
- pointers as well as the exceptional behavior.,json_pointer}
-
- @since version 2.0.0
- */
- explicit json_pointer(const std::string& s = "")
- : reference_tokens(split(s))
- {}
-
- /*!
- @brief return a string representation of the JSON pointer
-
- @invariant For each JSON pointer `ptr`, it holds:
- @code {.cpp}
- ptr == json_pointer(ptr.to_string());
- @endcode
-
- @return a string representation of the JSON pointer
-
- @liveexample{The example shows the result of `to_string`.,
- json_pointer__to_string}
-
- @since version 2.0.0
- */
- std::string to_string() const noexcept
- {
- std::string result;
-
- for (const auto& reference_token : reference_tokens)
- {
- result += "/" + escape(reference_token);
- }
-
- return result;
- }
-
- /// @copydoc to_string()
- operator std::string() const
- {
- return to_string();
- }
-
- private:
- /// remove and return last reference pointer
- std::string pop_back()
- {
- if (is_root())
- {
- throw std::domain_error("JSON pointer has no parent");
- }
-
- auto last = reference_tokens.back();
- reference_tokens.pop_back();
- return last;
- }
-
- /// return whether pointer points to the root document
- bool is_root() const
- {
- return reference_tokens.empty();
- }
-
- json_pointer top() const
- {
- if (is_root())
- {
- throw std::domain_error("JSON pointer has no parent");
- }
-
- json_pointer result = *this;
- result.reference_tokens = {reference_tokens[0]};
- return result;
- }
-
- /*!
- @brief create and return a reference to the pointed to value
- */
- reference get_and_create(reference j) const
- {
- pointer result = &j;
-
- // in case no reference tokens exist, return a reference to the
- // JSON value j which will be overwritten by a primitive value
- for (const auto& reference_token : reference_tokens)
- {
- switch (result->m_type)
- {
- case value_t::null:
- {
- if (reference_token == "0")
- {
- // start a new array if reference token is 0
- result = &result->operator[](0);
- }
- else
- {
- // start a new object otherwise
- result = &result->operator[](reference_token);
- }
- break;
- }
-
- case value_t::object:
- {
- // create an entry in the object
- result = &result->operator[](reference_token);
- break;
- }
-
- case value_t::array:
- {
- // create an entry in the array
- result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- /*
- The following code is only reached if there exists a
- reference token _and_ the current value is primitive. In
- this case, we have an error situation, because primitive
- values may only occur as single value; that is, with an
- empty list of reference tokens.
- */
- default:
- {
- throw std::domain_error("invalid value to unflatten");
- }
- }
- }
-
- return *result;
- }
-
- /*!
- @brief return a reference to the pointed to value
-
- @param[in] ptr a JSON value
-
- @return reference to the JSON value pointed to by the JSON pointer
-
- @complexity Linear in the length of the JSON pointer.
-
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
- */
- reference get_unchecked(pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case value_t::array:
- {
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- if (reference_token == "-")
- {
- // explicityly treat "-" as index beyond the end
- ptr = &ptr->operator[](ptr->m_value.array->size());
- }
- else
- {
- // convert array index to number; unchecked access
- ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
- }
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- reference get_checked(pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case value_t::array:
- {
- if (reference_token == "-")
- {
- // "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- // note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- /*!
- @brief return a const reference to the pointed to value
-
- @param[in] ptr a JSON value
-
- @return const reference to the JSON value pointed to by the JSON
- pointer
- */
- const_reference get_unchecked(const_pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // use unchecked object access
- ptr = &ptr->operator[](reference_token);
- break;
- }
-
- case value_t::array:
- {
- if (reference_token == "-")
- {
- // "-" cannot be used for const access
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- // use unchecked array access
- ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- const_reference get_checked(const_pointer ptr) const
- {
- for (const auto& reference_token : reference_tokens)
- {
- switch (ptr->m_type)
- {
- case value_t::object:
- {
- // note: at performs range check
- ptr = &ptr->at(reference_token);
- break;
- }
-
- case value_t::array:
- {
- if (reference_token == "-")
- {
- // "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
- }
-
- // error condition (cf. RFC 6901, Sect. 4)
- if (reference_token.size() > 1 and reference_token[0] == '0')
- {
- throw std::domain_error("array index must not begin with '0'");
- }
-
- // note: at performs range check
- ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
- break;
- }
-
- default:
- {
- throw std::out_of_range("unresolved reference token '" + reference_token + "'");
- }
- }
- }
-
- return *ptr;
- }
-
- /// split the string input to reference tokens
- static std::vector<std::string> split(std::string reference_string)
- {
- std::vector<std::string> result;
-
- // special case: empty reference string -> no reference tokens
- if (reference_string.empty())
- {
- return result;
- }
-
- // check if nonempty reference string begins with slash
- if (reference_string[0] != '/')
- {
- throw std::domain_error("JSON pointer must be empty or begin with '/'");
- }
-
- // extract the reference tokens:
- // - slash: position of the last read slash (or end of string)
- // - start: position after the previous slash
- for (
- // search for the first slash after the first character
- size_t slash = reference_string.find_first_of("/", 1),
- // set the beginning of the first reference token
- start = 1;
- // we can stop if start == string::npos+1 = 0
- start != 0;
- // set the beginning of the next reference token
- // (will eventually be 0 if slash == std::string::npos)
- start = slash + 1,
- // find next slash
- slash = reference_string.find_first_of("/", start))
- {
- // use the text between the beginning of the reference token
- // (start) and the last slash (slash).
- auto reference_token = reference_string.substr(start, slash - start);
-
- // check reference tokens are properly escaped
- for (size_t pos = reference_token.find_first_of("~");
- pos != std::string::npos;
- pos = reference_token.find_first_of("~", pos + 1))
- {
- assert(reference_token[pos] == '~');
-
- // ~ must be followed by 0 or 1
- if (pos == reference_token.size() - 1 or
- (reference_token[pos + 1] != '0' and
- reference_token[pos + 1] != '1'))
- {
- throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
- }
- }
-
- // finally, store the reference token
- unescape(reference_token);
- result.push_back(reference_token);
- }
-
- return result;
- }
-
- private:
- /*!
- @brief replace all occurrences of a substring by another string
-
- @param[in,out] s the string to manipulate
- @param[in] f the substring to replace with @a t
- @param[out] t the string to replace @a f
-
- @return The string @a s where all occurrences of @a f are replaced
- with @a t.
-
- @pre The search string @a f must not be empty.
-
- @since version 2.0.0
- */
- static void replace_substring(std::string& s,
- const std::string& f,
- const std::string& t)
- {
- assert(not f.empty());
-
- for (
- size_t pos = s.find(f); // find first occurrence of f
- pos != std::string::npos; // make sure f was found
- s.replace(pos, f.size(), t), // replace with t
- pos = s.find(f, pos + t.size()) // find next occurrence of f
- );
- }
-
- /// escape tilde and slash
- static std::string escape(std::string s)
- {
- // escape "~"" to "~0" and "/" to "~1"
- replace_substring(s, "~", "~0");
- replace_substring(s, "/", "~1");
- return s;
- }
-
- /// unescape tilde and slash
- static void unescape(std::string& s)
- {
- // first transform any occurrence of the sequence '~1' to '/'
- replace_substring(s, "~1", "/");
- // then transform any occurrence of the sequence '~0' to '~'
- replace_substring(s, "~0", "~");
- }
-
- /*!
- @param[in] reference_string the reference string to the current value
- @param[in] value the value to consider
- @param[in,out] result the result object to insert values to
-
- @note Empty objects or arrays are flattened to `null`.
- */
- static void flatten(const std::string& reference_string,
- const basic_json& value,
- basic_json& result)
- {
- switch (value.m_type)
- {
- case value_t::array:
- {
- if (value.m_value.array->empty())
- {
- // flatten empty array as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate array and use index as reference string
- for (size_t i = 0; i < value.m_value.array->size(); ++i)
- {
- flatten(reference_string + "/" + std::to_string(i),
- value.m_value.array->operator[](i), result);
- }
- }
- break;
- }
-
- case value_t::object:
- {
- if (value.m_value.object->empty())
- {
- // flatten empty object as null
- result[reference_string] = nullptr;
- }
- else
- {
- // iterate object and use keys as reference string
- for (const auto& element : *value.m_value.object)
- {
- flatten(reference_string + "/" + escape(element.first),
- element.second, result);
- }
- }
- break;
- }
-
- default:
- {
- // add primitive value with its reference string
- result[reference_string] = value;
- break;
- }
- }
- }
-
- /*!
- @param[in] value flattened JSON
-
- @return unflattened JSON
- */
- static basic_json unflatten(const basic_json& value)
- {
- if (not value.is_object())
- {
- throw std::domain_error("only objects can be unflattened");
- }
-
- basic_json result;
-
- // iterate the JSON object values
- for (const auto& element : *value.m_value.object)
- {
- if (not element.second.is_primitive())
- {
- throw std::domain_error("values in object must be primitive");
- }
-
- // assign value to reference pointed to by JSON pointer; Note
- // that if the JSON pointer is "" (i.e., points to the whole
- // value), function get_and_create returns a reference to
- // result itself. An assignment will then create a primitive
- // value.
- json_pointer(element.first).get_and_create(result) = element.second;
- }
-
- return result;
- }
+ return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict);
+ }
- private:
- /// the reference tokens
- std::vector<std::string> reference_tokens {};
- };
+ /// @}
//////////////////////////
// JSON Pointer support //
@@ -9470,9 +13677,9 @@ basic_json_parser_63:
@complexity Constant.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.404 if the JSON pointer can not be resolved
@liveexample{The behavior is shown in the example.,operatorjson_pointer}
@@ -9497,9 +13704,10 @@ basic_json_parser_63:
@complexity Constant.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
@liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
@@ -9520,15 +13728,33 @@ basic_json_parser_63:
@return reference to the element pointed to by @a ptr
- @complexity Constant.
+ @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
+ begins with '0'. See example below.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
+ is not a number. See example below.
- @liveexample{The behavior is shown in the example.,at_json_pointer}
+ @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
+ is out of range. See example below.
+
+ @throw out_of_range.402 if the array index '-' is used in the passed JSON
+ pointer @a ptr. As `at` provides checked access (and no elements are
+ implicitly inserted), the index '-' is always invalid. See example below.
+
+ @throw out_of_range.403 if the JSON pointer describes a key of an object
+ which cannot be found. See example below.
+
+ @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
+ See example below.
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
@since version 2.0.0
+
+ @liveexample{The behavior is shown in the example.,at_json_pointer}
*/
reference at(const json_pointer& ptr)
{
@@ -9545,15 +13771,33 @@ basic_json_parser_63:
@return reference to the element pointed to by @a ptr
- @complexity Constant.
+ @throw parse_error.106 if an array index in the passed JSON pointer @a ptr
+ begins with '0'. See example below.
- @throw std::out_of_range if the JSON pointer can not be resolved
- @throw std::domain_error if an array index begins with '0'
- @throw std::invalid_argument if an array index was not a number
+ @throw parse_error.109 if an array index in the passed JSON pointer @a ptr
+ is not a number. See example below.
- @liveexample{The behavior is shown in the example.,at_json_pointer_const}
+ @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr
+ is out of range. See example below.
+
+ @throw out_of_range.402 if the array index '-' is used in the passed JSON
+ pointer @a ptr. As `at` provides checked access (and no elements are
+ implicitly inserted), the index '-' is always invalid. See example below.
+
+ @throw out_of_range.403 if the JSON pointer describes a key of an object
+ which cannot be found. See example below.
+
+ @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.
+ See example below.
+
+ @exceptionsafety Strong guarantee: if an exception is thrown, there are no
+ changes in the JSON value.
+
+ @complexity Constant.
@since version 2.0.0
+
+ @liveexample{The behavior is shown in the example.,at_json_pointer_const}
*/
const_reference at(const json_pointer& ptr) const
{
@@ -9568,7 +13812,7 @@ basic_json_parser_63:
primitive. The original JSON value can be restored using the @ref
unflatten() function.
- @return an object that maps JSON pointers to primitve values
+ @return an object that maps JSON pointers to primitive values
@note Empty objects and arrays are flattened to `null` and will not be
reconstructed correctly by the @ref unflatten() function.
@@ -9609,6 +13853,9 @@ basic_json_parser_63:
@complexity Linear in the size the JSON value.
+ @throw type_error.314 if value is not an object
+ @throw type_error.315 if object values are not primitive
+
@liveexample{The following code shows how a flattened JSON object is
unflattened into the original nested JSON object.,unflatten}
@@ -9635,7 +13882,7 @@ basic_json_parser_63:
[JSON Patch](http://jsonpatch.com) defines a JSON document structure for
expressing a sequence of operations to apply to a JSON) document. With
- this funcion, a JSON Patch is applied to the current JSON value by
+ this function, a JSON Patch is applied to the current JSON value by
executing all operations from the patch.
@param[in] json_patch JSON patch document
@@ -9646,12 +13893,23 @@ basic_json_parser_63:
any case, the original value is not changed: the patch is applied
to a copy of the value.
- @throw std::out_of_range if a JSON pointer inside the patch could not
- be resolved successfully in the current JSON value; example: `"key baz
- not found"`
- @throw invalid_argument if the JSON patch is malformed (e.g., mandatory
+ @throw parse_error.104 if the JSON patch does not consist of an array of
+ objects
+
+ @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory
attributes are missing); example: `"operation add must have member path"`
+ @throw out_of_range.401 if an array index is out of range.
+
+ @throw out_of_range.403 if a JSON pointer inside the patch could not be
+ resolved successfully in the current JSON value; example: `"key baz not
+ found"`
+
+ @throw out_of_range.405 if JSON pointer has no parent ("add", "remove",
+ "move")
+
+ @throw other_error.501 if "test" operation was unsuccessful
+
@complexity Linear in the size of the JSON value and the length of the
JSON patch. As usually only a fraction of the JSON value is affected by
the patch, the complexity can usually be neglected.
@@ -9674,7 +13932,7 @@ basic_json_parser_63:
// the valid JSON Patch operations
enum class patch_operations {add, remove, replace, move, copy, test, invalid};
- const auto get_op = [](const std::string op)
+ const auto get_op = [](const std::string & op)
{
if (op == "add")
{
@@ -9718,7 +13976,7 @@ basic_json_parser_63:
json_pointer top_pointer = ptr.top();
if (top_pointer != ptr)
{
- basic_json& x = result.at(top_pointer);
+ result.at(top_pointer);
}
// get reference to parent of JSON pointer ptr
@@ -9744,11 +14002,11 @@ basic_json_parser_63:
}
else
{
- const auto idx = std::stoi(last_path);
- if (static_cast<size_type>(idx) > parent.size())
+ const auto idx = json_pointer::array_index(last_path);
+ if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))
{
// avoid undefined behavior
- throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+ JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
}
else
{
@@ -9780,30 +14038,29 @@ basic_json_parser_63:
{
// perform range check
auto it = parent.find(last_path);
- if (it != parent.end())
+ if (JSON_LIKELY(it != parent.end()))
{
parent.erase(it);
}
else
{
- throw std::out_of_range("key '" + last_path + "' not found");
+ JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
}
}
else if (parent.is_array())
{
// note erase performs range check
- parent.erase(static_cast<size_type>(std::stoi(last_path)));
+ parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));
}
};
- // type check
- if (not json_patch.is_array())
+ // type check: top level value must be an array
+ if (JSON_UNLIKELY(not json_patch.is_array()))
{
- // a JSON patch must be an array of objects
- throw std::invalid_argument("JSON patch must be an array of objects");
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
}
- // iterate and apply th eoperations
+ // iterate and apply the operations
for (const auto& val : json_patch)
{
// wrapper to get a value for an operation
@@ -9818,25 +14075,25 @@ basic_json_parser_63:
const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
// check if desired value is present
- if (it == val.m_value.object->end())
+ if (JSON_UNLIKELY(it == val.m_value.object->end()))
{
- throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+ JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
}
// check if result is of type string
- if (string_type and not it->second.is_string())
+ if (JSON_UNLIKELY(string_type and not it->second.is_string()))
{
- throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+ JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
}
// no error: return value
return it->second;
};
- // type check
- if (not val.is_object())
+ // type check: every element of the array must be an object
+ if (JSON_UNLIKELY(not val.is_object()))
{
- throw std::invalid_argument("JSON patch must be an array of objects");
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
}
// collect mandatory members
@@ -9884,32 +14141,37 @@ basic_json_parser_63:
case patch_operations::copy:
{
- const std::string from_path = get_value("copy", "from", true);;
+ const std::string from_path = get_value("copy", "from", true);
const json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
- result[ptr] = result.at(from_ptr);
+ basic_json v = result.at(from_ptr);
+
+ // The copy is functionally identical to an "add"
+ // operation at the target location using the value
+ // specified in the "from" member.
+ operation_add(ptr, v);
break;
}
case patch_operations::test:
{
bool success = false;
- try
+ JSON_TRY
{
// check if "value" matches the one at "path"
// the "path" location must exist - use at()
success = (result.at(ptr) == get_value("test", "value", false));
}
- catch (std::out_of_range&)
+ JSON_CATCH (out_of_range&)
{
// ignore out of range errors: success remains false
}
// throw an exception if test fails
- if (not success)
+ if (JSON_UNLIKELY(not success))
{
- throw std::domain_error("unsuccessful: " + val.dump());
+ JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
}
break;
@@ -9919,7 +14181,7 @@ basic_json_parser_63:
{
// op must be "add", "remove", "replace", "move", "copy", or
// "test"
- throw std::invalid_argument("operation value '" + op + "' is invalid");
+ JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
}
}
}
@@ -9942,8 +14204,8 @@ basic_json_parser_63:
@note Currently, only `remove`, `add`, and `replace` operations are
generated.
- @param[in] source JSON value to copare from
- @param[in] target JSON value to copare against
+ @param[in] source JSON value to compare from
+ @param[in] target JSON value to compare against
@param[in] path helper value to create JSON pointers
@return a JSON patch to convert the @a source to @a target
@@ -9959,9 +14221,8 @@ basic_json_parser_63:
@since version 2.0.0
*/
- static basic_json diff(const basic_json& source,
- const basic_json& target,
- std::string path = "")
+ static basic_json diff(const basic_json& source, const basic_json& target,
+ const std::string& path = "")
{
// the patch
basic_json result(value_t::array);
@@ -9977,9 +14238,7 @@ basic_json_parser_63:
// different types: replace value
result.push_back(
{
- {"op", "replace"},
- {"path", path},
- {"value", target}
+ {"op", "replace"}, {"path", path}, {"value", target}
});
}
else
@@ -9989,7 +14248,7 @@ basic_json_parser_63:
case value_t::array:
{
// first pass: traverse common elements
- size_t i = 0;
+ std::size_t i = 0;
while (i < source.size() and i < target.size())
{
// recursive call to compare array values at index i
@@ -10002,9 +14261,12 @@ basic_json_parser_63:
// in a second pass, traverse the remaining elements
// remove my remaining elements
+ const auto end_index = static_cast<difference_type>(result.size());
while (i < source.size())
{
- result.push_back(object(
+ // add operations in reverse order to avoid invalid
+ // indices
+ result.insert(result.begin() + end_index, object(
{
{"op", "remove"},
{"path", path + "/" + std::to_string(i)}
@@ -10030,7 +14292,7 @@ basic_json_parser_63:
case value_t::object:
{
// first pass: traverse this object's elements
- for (auto it = source.begin(); it != source.end(); ++it)
+ for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
const auto key = json_pointer::escape(it.key());
@@ -10046,14 +14308,13 @@ basic_json_parser_63:
// found a key that is not in o -> remove it
result.push_back(object(
{
- {"op", "remove"},
- {"path", path + "/" + key}
+ {"op", "remove"}, {"path", path + "/" + key}
}));
}
}
// second pass: traverse other object's elements
- for (auto it = target.begin(); it != target.end(); ++it)
+ for (auto it = target.cbegin(); it != target.cend(); ++it)
{
if (source.find(it.key()) == source.end())
{
@@ -10061,8 +14322,7 @@ basic_json_parser_63:
const auto key = json_pointer::escape(it.key());
result.push_back(
{
- {"op", "add"},
- {"path", path + "/" + key},
+ {"op", "add"}, {"path", path + "/" + key},
{"value", it.value()}
});
}
@@ -10076,9 +14336,7 @@ basic_json_parser_63:
// both primitive type: replace value
result.push_back(
{
- {"op", "replace"},
- {"path", path},
- {"value", target}
+ {"op", "replace"}, {"path", path}, {"value", target}
});
break;
}
@@ -10091,7 +14349,6 @@ basic_json_parser_63:
/// @}
};
-
/////////////
// presets //
/////////////
@@ -10105,8 +14362,402 @@ uses the standard template types.
@since version 1.0.0
*/
using json = basic_json<>;
+
+//////////////////
+// json_pointer //
+//////////////////
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ auto result = &j;
+
+ // in case no reference tokens exist, return a reference to the JSON value
+ // j which will be overwritten by a primitive value
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (result->m_type)
+ {
+ case detail::value_t::null:
+ {
+ if (reference_token == "0")
+ {
+ // start a new array if reference token is 0
+ result = &result->operator[](0);
+ }
+ else
+ {
+ // start a new object otherwise
+ result = &result->operator[](reference_token);
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ // create an entry in the object
+ result = &result->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // create an entry in the array
+ JSON_TRY
+ {
+ result = &result->operator[](static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ /*
+ The following code is only reached if there exists a reference
+ token _and_ the current value is primitive. In this case, we have
+ an error situation, because primitive values may only occur as
+ single value; that is, with an empty list of reference tokens.
+ */
+ default:
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
+ }
+ }
+
+ return *result;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ // convert null values to arrays or objects before continuing
+ if (ptr->m_type == detail::value_t::null)
+ {
+ // check if reference token is a number
+ const bool nums =
+ std::all_of(reference_token.begin(), reference_token.end(),
+ [](const char x)
+ {
+ return (x >= '0' and x <= '9');
+ });
+
+ // change value to array for numbers or "-" or to object otherwise
+ *ptr = (nums or reference_token == "-")
+ ? detail::value_t::array
+ : detail::value_t::object;
+ }
+
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ if (reference_token == "-")
+ {
+ // explicitly treat "-" as index beyond the end
+ ptr = &ptr->operator[](ptr->m_value.array->size());
+ }
+ else
+ {
+ // convert array index to number; unchecked access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+const NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" cannot be used for const access
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // use unchecked array access
+ JSON_TRY
+ {
+ ptr = &ptr->operator[](
+ static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+const NLOHMANN_BASIC_JSON_TPL&
+json_pointer::get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const
+{
+ using size_type = typename NLOHMANN_BASIC_JSON_TPL::size_type;
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->m_type)
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402,
+ "array index '-' (" + std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0,
+ "array index '" + reference_token +
+ "' must not begin with '0'"));
+ }
+
+ // note: at performs range check
+ JSON_TRY
+ {
+ ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));
+ }
+ JSON_CATCH(std::invalid_argument&)
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
+ }
+ break;
+ }
+
+ default:
+ JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
+ }
+ }
+
+ return *ptr;
+}
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+void json_pointer::flatten(const std::string& reference_string,
+ const NLOHMANN_BASIC_JSON_TPL& value,
+ NLOHMANN_BASIC_JSON_TPL& result)
+{
+ switch (value.m_type)
+ {
+ case detail::value_t::array:
+ {
+ if (value.m_value.array->empty())
+ {
+ // flatten empty array as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate array and use index as reference string
+ for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
+ {
+ flatten(reference_string + "/" + std::to_string(i),
+ value.m_value.array->operator[](i), result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ if (value.m_value.object->empty())
+ {
+ // flatten empty object as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate object and use keys as reference string
+ for (const auto& element : *value.m_value.object)
+ {
+ flatten(reference_string + "/" + escape(element.first), element.second, result);
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ // add primitive value with its reference string
+ result[reference_string] = value;
+ break;
+ }
+ }
}
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+NLOHMANN_BASIC_JSON_TPL
+json_pointer::unflatten(const NLOHMANN_BASIC_JSON_TPL& value)
+{
+ if (JSON_UNLIKELY(not value.is_object()))
+ {
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
+ }
+
+ NLOHMANN_BASIC_JSON_TPL result;
+
+ // iterate the JSON object values
+ for (const auto& element : *value.m_value.object)
+ {
+ if (JSON_UNLIKELY(not element.second.is_primitive()))
+ {
+ JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
+ }
+
+ // assign value to reference pointed to by JSON pointer; Note that if
+ // the JSON pointer is "" (i.e., points to the whole value), function
+ // get_and_create returns a reference to result itself. An assignment
+ // will then create a primitive value.
+ json_pointer(element.first).get_and_create(result) = element.second;
+ }
+
+ return result;
+}
+
+inline bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept
+{
+ return (lhs.reference_tokens == rhs.reference_tokens);
+}
+
+inline bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept
+{
+ return not (lhs == rhs);
+}
+} // namespace nlohmann
+
///////////////////////
// nonmember support //
@@ -10120,7 +14771,7 @@ namespace std
@since version 1.0.0
*/
-template <>
+template<>
inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept(
is_nothrow_move_constructible<nlohmann::json>::value and
@@ -10131,7 +14782,7 @@ inline void swap(nlohmann::json& j1,
}
/// hash value for JSON objects
-template <>
+template<>
struct hash<nlohmann::json>
{
/*!
@@ -10146,38 +14797,78 @@ struct hash<nlohmann::json>
return h(j.dump());
}
};
-}
+
+/// specialization for std::less<value_t>
+/// @note: do not remove the space after '<',
+/// see https://github.com/nlohmann/json/pull/679
+template<>
+struct less< ::nlohmann::detail::value_t>
+{
+ /*!
+ @brief compare two value_t enum values
+ @since version 3.0.0
+ */
+ bool operator()(nlohmann::detail::value_t lhs,
+ nlohmann::detail::value_t rhs) const noexcept
+ {
+ return nlohmann::detail::operator<(lhs, rhs);
+ }
+};
+
+} // namespace std
/*!
@brief user-defined string literal for JSON values
This operator implements a user-defined string literal for JSON objects. It
-can be used by adding \p "_json" to a string literal and returns a JSON object
+can be used by adding `"_json"` to a string literal and returns a JSON object
if no parse error occurred.
@param[in] s a string representation of a JSON object
+@param[in] n the length of string @a s
@return a JSON object
@since version 1.0.0
*/
-inline nlohmann::json operator "" _json(const char* s, std::size_t)
+inline nlohmann::json operator "" _json(const char* s, std::size_t n)
{
- return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
+ return nlohmann::json::parse(s, s + n);
}
/*!
@brief user-defined string literal for JSON pointer
+This operator implements a user-defined string literal for JSON Pointers. It
+can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer
+object if no parse error occurred.
+
+@param[in] s a string representation of a JSON Pointer
+@param[in] n the length of string @a s
+@return a JSON pointer object
+
@since version 2.0.0
*/
-inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
{
- return nlohmann::json::json_pointer(s);
+ return nlohmann::json::json_pointer(std::string(s, n));
}
// restore GCC/clang diagnostic settings
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
+#if defined(__clang__)
+ #pragma GCC diagnostic pop
+#endif
+
+// clean up
+#undef JSON_CATCH
+#undef JSON_THROW
+#undef JSON_TRY
+#undef JSON_LIKELY
+#undef JSON_UNLIKELY
+#undef JSON_DEPRECATED
+#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
+#undef NLOHMANN_BASIC_JSON_TPL
#endif
diff --git a/ext/librethinkdbxx/.travis.yml b/ext/librethinkdbxx/.travis.yml
new file mode 100644
index 00000000..b306a410
--- /dev/null
+++ b/ext/librethinkdbxx/.travis.yml
@@ -0,0 +1,11 @@
+sudo: required
+dist: trusty
+
+python:
+ - "3.4.3"
+
+addons:
+ rethinkdb: "2.3"
+
+script:
+ - make test
diff --git a/ext/librethinkdbxx/COPYRIGHT b/ext/librethinkdbxx/COPYRIGHT
new file mode 100644
index 00000000..c25145d5
--- /dev/null
+++ b/ext/librethinkdbxx/COPYRIGHT
@@ -0,0 +1,16 @@
+RethinkDB Language Drivers
+
+Copyright 2010-2012 RethinkDB
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this product 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.
+
diff --git a/ext/librethinkdbxx/Makefile b/ext/librethinkdbxx/Makefile
new file mode 100644
index 00000000..ba319c16
--- /dev/null
+++ b/ext/librethinkdbxx/Makefile
@@ -0,0 +1,126 @@
+# Customisable build settings
+
+CXX ?= clang++
+CXXFLAGS ?=
+INCLUDE_PYTHON_DOCS ?= no
+DEBUG ?= no
+PYTHON ?= python3
+
+# Required build settings
+
+ifneq (no,$(DEBUG))
+ CXXFLAGS += -ggdb
+else
+ CXXFLAGS += -O3 # -flto
+endif
+
+CXXFLAGS += -std=c++11 -I'build/gen' -Wall -pthread -fPIC
+
+prefix ?= /usr
+DESTDIR ?=
+
+.DELETE_ON_ERROR:
+SHELL := /bin/bash
+
+modules := connection datum json term cursor types utils
+headers := utils error exceptions types datum connection cursor term
+
+o_files := $(patsubst %, build/obj/%.o, $(modules))
+d_files := $(patsubst %, build/dep/%.d, $(modules))
+
+skip_tests := regression/1133 regression/767 regression/1005 # python-only
+skip_tests += arity # arity errors are compile-time
+skip_tests += geo # geo types not implemented yet
+skip_tests += limits # possibly broken tests: https://github.com/rethinkdb/rethinkdb/issues/5940
+
+upstream_tests := \
+ $(filter-out %.rb.%, \
+ $(filter-out $(patsubst %,test/upstream/%%, $(skip_tests)), \
+ $(filter test/upstream/$(test_filter)%, \
+ $(shell find test/upstream -name '*.yaml' | egrep -v '.(rb|js).yaml$$'))))
+upstream_tests_cc := $(patsubst %.yaml, build/tests/%.cc, $(upstream_tests))
+upstream_tests_o := $(patsubst %.cc, %.o, $(upstream_tests_cc))
+
+.PRECIOUS: $(upstream_tests_cc) $(upstream_tests_o)
+
+default: build/librethinkdb++.a build/include/rethinkdb.h build/librethinkdb++.so
+
+all: default build/test
+
+build/librethinkdb++.a: $(o_files)
+ ar rcs $@ $^
+
+build/librethinkdb++.so: $(o_files)
+ $(CXX) -o $@ $(CXXFLAGS) -shared $^
+
+build/obj/%.o: src/%.cc build/gen/protocol_defs.h
+ @mkdir -p $(dir $@)
+ @mkdir -p $(dir build/dep/$*.d)
+ $(CXX) -o $@ $(CXXFLAGS) -c $< -MP -MQ $@ -MD -MF build/dep/$*.d
+
+build/gen/protocol_defs.h: reql/ql2.proto reql/gen.py | build/gen/.
+ $(PYTHON) reql/gen.py $< > $@
+
+clean:
+ rm -rf build
+
+ifneq (no,$(INCLUDE_PYTHON_DOCS))
+build/include/rethinkdb.h: build/rethinkdb.nodocs.h reql/add_docs.py reql/python_docs.txt | build/include/.
+ $(PYTHON) reql/add_docs.py reql/python_docs.txt < $< > $@
+else
+build/include/rethinkdb.h: build/rethinkdb.nodocs.h | build/include/.
+ cp $< $@
+endif
+
+build/rethinkdb.nodocs.h: build/gen/protocol_defs.h $(patsubst %, src/%.h, $(headers))
+ ( echo "// Auto-generated file, built from $^"; \
+ echo '#pragma once'; \
+ cat $^ | \
+ grep -v '^#pragma once' | \
+ grep -v '^#include "'; \
+ ) > $@
+
+build/tests/%.cc: %.yaml test/yaml_to_cxx.py
+ @mkdir -p $(dir $@)
+ $(PYTHON) test/yaml_to_cxx.py $< > $@
+
+build/tests/upstream_tests.cc: $(upstream_tests) test/gen_index_cxx.py FORCE | build/tests/.
+ @echo '$(PYTHON) test/gen_index_cxx.py $(wordlist 1,5,$(upstream_tests)) ... > $@'
+ @$(PYTHON) test/gen_index_cxx.py $(upstream_tests) > $@
+
+build/tests/%.o: build/tests/%.cc build/include/rethinkdb.h test/testlib.h | build/tests/.
+ $(CXX) -o $@ $(CXXFLAGS) -isystem build/include -I test -c $< -Wno-unused-variable
+
+build/tests/%.o: test/%.cc test/testlib.h build/include/rethinkdb.h | build/tests/.
+ $(CXX) -o $@ $(CXXFLAGS) -isystem build/include -I test -c $<
+
+build/test: build/tests/testlib.o build/tests/test.o build/tests/upstream_tests.o $(upstream_tests_o) build/librethinkdb++.a
+ @echo $(CXX) -o $@ $(CXXFLAGS) $(wordlist 1,5,$^) ...
+ @$(CXX) -o $@ $(CXXFLAGS) build/librethinkdb++.a $^
+
+.PHONY: test
+test: build/test
+ build/test
+
+build/bench: build/tests/bench.o build/librethinkdb++.a
+ @$(CXX) -o $@ $(CXXFLAGS) -isystem build/include build/librethinkdb++.a $^
+
+.PHONY: bench
+bench: build/bench
+ build/bench
+
+.PHONY: install
+install: build/librethinkdb++.a build/include/rethinkdb.h build/librethinkdb++.so
+ install -m755 -d $(DESTDIR)$(prefix)/lib
+ install -m755 -d $(DESTDIR)$(prefix)/include
+ install -m644 build/librethinkdb++.a $(DESTDIR)$(prefix)/lib/librethinkdb++.a
+ install -m644 build/librethinkdb++.so $(DESTDIR)$(prefix)/lib/librethinkdb++.so
+ install -m644 build/include/rethinkdb.h $(DESTDIR)$(prefix)/include/rethinkdb.h
+
+%/.:
+ mkdir -p $*
+
+.PHONY: FORCE
+FORCE:
+
+-include $(d_files)
diff --git a/ext/librethinkdbxx/README.md b/ext/librethinkdbxx/README.md
new file mode 100644
index 00000000..92fa9136
--- /dev/null
+++ b/ext/librethinkdbxx/README.md
@@ -0,0 +1,72 @@
+# RethinkDB driver for C++
+
+This driver is compatible with RethinkDB 2.0. It is based on the
+official RethinkDB Python driver.
+
+* [RethinkDB server](http://rethinkdb.com/)
+* [RethinkDB API docs](http://rethinkdb.com/api/python/)
+
+## Example
+
+```
+#include <memory>
+#include <cstdio>
+#include <rethinkdb.h>
+
+namespace R = RethinkDB;
+
+int main() {
+ std::unique_ptr<R::Connection> conn = R::connect("localhost", 28015);
+ R::Cursor cursor = R::table("users").filter(R::row["age"] > 14).run(*conn);
+ for (R::Datum& user : cursor) {
+ printf("%s\n", user.as_json().c_str());
+ }
+}
+```
+
+## Build
+
+Requires a modern C++ compiler. to build and install, run:
+
+```
+make
+make install
+```
+
+Will build `include/rethinkdb.h`, `librethinkdb++.a` and
+`librethinkdb++.so` into the `build/` directory.
+
+To include documentation from the Python driver in the header file,
+pass the following argument to make.
+
+```
+make INCLUDE_PYTHON_DOCS=yes
+```
+
+To build in debug mode:
+
+```
+make DEBUG=yes
+```
+
+To install to a specific location:
+
+```
+make install prefix=/usr/local DESTDIR=
+```
+
+## Status
+
+Still in early stages of development.
+
+## Tests
+
+This driver is tested against the upstream ReQL tests from the
+RethinkDB repo, which are programmatically translated from Python to
+C++. As of 34dc13c, all tests pass:
+
+```
+$ make test
+...
+SUCCESS: 2053 tests passed
+```
diff --git a/ext/librethinkdbxx/reql/add_docs.py b/ext/librethinkdbxx/reql/add_docs.py
new file mode 100644
index 00000000..67f08df8
--- /dev/null
+++ b/ext/librethinkdbxx/reql/add_docs.py
@@ -0,0 +1,80 @@
+from sys import stdin, stderr, stdout, argv
+from re import match, sub
+
+docs = {}
+
+for line in open(argv[1]):
+ res = match('^\t\(([^,]*), (.*)\),$', line)
+ if res:
+ fullname = res.group(1)
+ docs[fullname.split('.')[-1]] = eval(res.group(2)).decode('utf-8')
+
+translate_name = {
+ 'name': None,
+ 'delete_': 'delete',
+ 'union_': 'union',
+ 'operator[]': '__getitem__',
+ 'operator+': '__add__',
+ 'operator-': '__sub__',
+ 'operator*': '__mul__',
+ 'operator/': '__div__',
+ 'operator%': '__mod__',
+ 'operator&&': 'and_',
+ 'operator||': 'or_',
+ 'operator==': '__eq__',
+ 'operator!=': '__ne__',
+ 'operator>': '__gt__',
+ 'operator>=': '__ge__',
+ 'operator<': '__lt__',
+ 'operator<=': '__le__',
+ 'operator!': 'not_',
+ 'default_': 'default',
+ 'array': None,
+ 'desc': None,
+ 'asc': None,
+ 'maxval': None,
+ 'minval': None,
+ 'january': None,
+ 'february': None,
+ 'march': None,
+ 'april': None,
+ 'may': None,
+ 'june': None,
+ 'july': None,
+ 'august': None,
+ 'september': None,
+ 'october': None,
+ 'november': None,
+ 'december': None,
+ 'monday': None,
+ 'tuesday': None,
+ 'wednesday': None,
+ 'thursday': None,
+ 'friday': None,
+ 'saturday': None,
+ 'sunday': None,
+}
+
+def print_docs(name, line):
+ py_name = translate_name.get(name, name)
+ if py_name in docs:
+ indent = match("^( *)", line).group(1)
+ stdout.write('\n')
+ # TODO: convert the examples to C++
+ for line in docs[py_name].split('\n'):
+ stdout.write(indent + "// " + line + '\n')
+ elif py_name:
+ stderr.write('Warning: no docs for ' + py_name + ': ' + line)
+
+stdout.write('// Contains documentation copied as-is from the Python driver')
+
+for line in stdin:
+ res = match("^ *CO?[0-9_]+\(([^,)]+)|extern Query (\w+)|^ *// *(\$)doc\((\w+)\) *$", line)
+ if res:
+ name = res.group(1) or res.group(2) or res.group(4)
+ print_docs(name, line)
+ if not res.group(3):
+ stdout.write(line)
+ else:
+ stdout.write(line)
+
diff --git a/ext/librethinkdbxx/reql/gen.py b/ext/librethinkdbxx/reql/gen.py
new file mode 100644
index 00000000..2b1fe9fc
--- /dev/null
+++ b/ext/librethinkdbxx/reql/gen.py
@@ -0,0 +1,33 @@
+from sys import argv
+from re import sub, finditer, VERBOSE
+
+def gen(defs):
+ indent = 0
+ enum = False
+ def p(s): print(" " * (indent * 4) + s)
+ for item in finditer("""
+ (?P<type> message|enum) \\s+ (?P<name> \\w+) \\s* \\{ |
+ (?P<var> \\w+) \\s* = \\s* (?P<val> \\w+) \\s* ; |
+ \\}
+ """, defs, flags=VERBOSE):
+ if item.group(0) == "}":
+ indent = indent - 1
+ p("};" if enum else "}")
+ enum = False;
+ elif item.group('type') == 'enum':
+ p("enum class %s {" % item.group('name'))
+ indent = indent + 1
+ enum = True
+ elif item.group('type') == 'message':
+ p("namespace %s {" % item.group('name'))
+ indent = indent + 1
+ enum = False
+ else:
+ if enum:
+ p("%s = %s," % (item.group('var'), item.group('val')))
+
+print("// Auto-generated by reql/gen.py")
+print("#pragma once")
+print("namespace RethinkDB { namespace Protocol {")
+gen(sub("//.*", "", open(argv[1]).read()))
+print("} }")
diff --git a/ext/librethinkdbxx/reql/python_docs.txt b/ext/librethinkdbxx/reql/python_docs.txt
new file mode 100644
index 00000000..41a5c84e
--- /dev/null
+++ b/ext/librethinkdbxx/reql/python_docs.txt
@@ -0,0 +1,189 @@
+# This file was generated by _scripts/gen_python.py from the rethinkdb documentation in http://github.com/rethinkdb/docs
+# hash: "3d13a937cfdacb7ffa3dab2ce3ebdf25bd3c192e"
+
+import rethinkdb
+
+docsSource = [
+
+ (rethinkdb.net.Connection.close, b'conn.close(noreply_wait=True)\n\nClose an open connection.\n\nClosing a connection normally waits until all outstanding requests have finished and then frees any open resources associated with the connection. By passing `False` to the `noreply_wait` optional argument, the connection will be closed immediately, possibly aborting any outstanding noreply writes.\n\nA noreply query is executed by passing the `noreply` option to the [run](http://rethinkdb.com/api/python/run/) command, indicating that `run()` should not wait for the query to complete before returning. You may also explicitly wait for a noreply query to complete by using the [noreply_wait](http://rethinkdb.com/api/python/noreply_wait) command.\n\n*Example* Close an open connection, waiting for noreply writes to finish.\n\n conn.close()\n\n*Example* Close an open connection immediately.\n\n conn.close(noreply_wait=False)\n'),
+ (rethinkdb.connect, b'r.connect(host="localhost", port=28015, db="test", auth_key="", timeout=20) -> connection\nr.connect(host) -> connection\n\nCreate a new connection to the database server. The keyword arguments are:\n\n- `host`: host of the RethinkDB instance. The default value is `localhost`.\n- `port`: the driver port, by default `28015`.\n- `db`: the database used if not explicitly specified in a query, by default `test`.\n- `auth_key`: the authentication key, by default the empty string.\n- `timeout`: timeout period in seconds for the connection to be opened (default `20`).\n\nIf the connection cannot be established, a `RqlDriverError` exception will be thrown.\n\nThe authentication key can be set from the RethinkDB command line tool. Once set, client connections must provide the key as an option to `run` in order to make the connection. For more information, read "Using the RethinkDB authentication system" in the documentation on [securing your cluster](http://rethinkdb.com/docs/security/).\n\n__Note:__ Currently, the Python driver is not thread-safe. Each thread or multiprocessing PID should be given its own connection object. (This is likely to change in a future release of RethinkDB; you can track issue [#2427](https://github.com/rethinkdb/rethinkdb/issues/2427) for progress.)\n\n*Example* Opens a connection using the default host and port but specifying the default database.\n\n conn = r.connect(db=\'marvel\')\n\n*Example* Opens a new connection to the database.\n\n conn = r.connect(host = \'localhost\',\n port = 28015,\n db = \'heroes\',\n auth_key = \'hunter2\')\n\n'),
+ (rethinkdb.net.Connection.noreply_wait, b'conn.noreply_wait()\n\n`noreply_wait` ensures that previous queries with the `noreply` flag have been processed\nby the server. Note that this guarantee only applies to queries run on the given connection.\n\n*Example* We have previously run queries with the `noreply` argument set to `True`. Now\nwait until the server has processed them.\n\n conn.noreply_wait()\n\n'),
+ (rethinkdb, b'r -> r\n\nThe top-level ReQL namespace.\n\n*Example* Setup your top-level namespace.\n\n import rethinkdb as r\n\n'),
+ (rethinkdb.net.Connection.reconnect, b'conn.reconnect(noreply_wait=True)\n\nClose and reopen a connection.\n\nClosing a connection normally waits until all outstanding requests have finished and then frees any open resources associated with the connection. By passing `False` to the `noreply_wait` optional argument, the connection will be closed immediately, possibly aborting any outstanding noreply writes.\n\nA noreply query is executed by passing the `noreply` option to the [run](http://rethinkdb.com/api/python/run/) command, indicating that `run()` should not wait for the query to complete before returning. You may also explicitly wait for a noreply query to complete by using the [noreply_wait](http://rethinkdb.com/api/python/noreply_wait) command.\n\n*Example* Cancel outstanding requests/queries that are no longer needed.\n\n conn.reconnect(noreply_wait=False)\n'),
+ (rethinkdb.net.Connection.repl, b"conn.repl()\n\nSet the default connection to make REPL use easier. Allows calling\n`.run()` on queries without specifying a connection.\n\n__Note:__ Avoid using `repl` in application code. RethinkDB connection objects are not thread-safe, and calls to `connect` from multiple threads may change the global connection object used by `repl`. Applications should specify connections explicitly.\n\n*Example* Set the default connection for the REPL, then call\n`run()` without specifying the connection.\n\n r.connect(db='marvel').repl()\n r.table('heroes').run()\n"),
+ (rethinkdb.ast.RqlQuery.run, b'query.run(conn, use_outdated=False, time_format=\'native\', profile=False, durability="hard") -> cursor\nquery.run(conn, use_outdated=False, time_format=\'native\', profile=False, durability="hard") -> object\n\nRun a query on a connection, returning either a single JSON result or\na cursor, depending on the query.\n\nThe optional arguments are:\n\n- `use_outdated`: whether or not outdated reads are OK (default: `False`).\n- `time_format`: what format to return times in (default: `\'native\'`).\n Set this to `\'raw\'` if you want times returned as JSON objects for exporting.\n- `profile`: whether or not to return a profile of the query\'s\n execution (default: `False`).\n- `durability`: possible values are `\'hard\'` and `\'soft\'`. In soft durability mode RethinkDB\nwill acknowledge the write immediately after receiving it, but before the write has\nbeen committed to disk.\n- `group_format`: what format to return `grouped_data` and `grouped_streams` in (default: `\'native\'`).\n Set this to `\'raw\'` if you want the raw pseudotype.\n- `noreply`: set to `True` to not receive the result object or cursor and return immediately.\n- `db`: the database to run this query against as a string. The default is the database specified in the `db` parameter to [connect](http://rethinkdb.com/api/python/connect/) (which defaults to `test`). The database may also be specified with the [db](http://rethinkdb.com/api/python/db/) command.\n- `array_limit`: the maximum numbers of array elements that can be returned by a query (default: 100,000). This affects all ReQL commands that return arrays. Note that it has no effect on the size of arrays being _written_ to the database; those always have an upper limit of 100,000 elements.\n- `binary_format`: what format to return binary data in (default: `\'native\'`). Set this to `\'raw\'` if you want the raw pseudotype.\n- `min_batch_rows`: minimum number of rows to wait for before batching a result set (default: 8). This is an integer.\n- `max_batch_rows`: maximum number of rows to wait for before batching a result set (default: unlimited). This is an integer.\n- `max_batch_bytes`: maximum number of bytes to wait for before batching a result set (default: 1024). This is an integer.\n- `max_batch_seconds`: maximum number of seconds to wait before batching a result set (default: 0.5). This is a float (not an integer) and may be specified to the microsecond.\n- `first_batch_scaledown_factor`: factor to scale the other parameters down by on the first batch (default: 4). For example, with this set to 8 and `max_batch_rows` set to 80, on the first batch `max_batch_rows` will be adjusted to 10 (80 / 8). This allows the first batch to return faster.\n\n*Example* Run a query on the connection `conn` and print out every\nrow in the result.\n\n for doc in r.table(\'marvel\').run(conn):\n print doc\n\n*Example* If you are OK with potentially out of date data from all\nthe tables involved in this query and want potentially faster reads,\npass a flag allowing out of date data in an options object. Settings\nfor individual tables will supercede this global setting for all\ntables in the query.\n\n r.table(\'marvel\').run(conn, use_outdated=True)\n\n*Example* If you just want to send a write and forget about it, you\ncan set `noreply` to true in the options. In this case `run` will\nreturn immediately.\n\n r.table(\'marvel\').run(conn, noreply=True)\n\n*Example* If you want to specify whether to wait for a write to be\nwritten to disk (overriding the table\'s default settings), you can set\n`durability` to `\'hard\'` or `\'soft\'` in the options.\n\n r.table(\'marvel\')\n .insert({ \'superhero\': \'Iron Man\', \'superpower\': \'Arc Reactor\' })\n .run(conn, noreply=True, durability=\'soft\')\n\n*Example* If you do not want a time object to be converted to a\nnative date object, you can pass a `time_format` flag to prevent it\n(valid flags are "raw" and "native"). This query returns an object\nwith two fields (`epoch_time` and `$reql_type$`) instead of a native date\nobject.\n\n r.now().run(conn, time_format="raw")\n\n*Example* Specify the database to use for the query.\n\n for doc in r.table(\'marvel\').run(conn, db=\'heroes\'):\n print doc\n\nThis is equivalent to using the `db` command to specify the database:\n\n r.db(\'heroes\').table(\'marvel\').run(conn) ...\n\n*Example* Change the batching parameters for this query.\n\n r.table(\'marvel\').run(conn, max_batch_rows=16, max_batch_bytes=2048)\n'),
+ (rethinkdb.set_loop_type, b'r.set_loop_type(string)\n\nSet an asynchronous event loop model. Currently, the only event loop model RethinkDB supports is `"tornado"`, for use with the [Tornado web framework](http://www.tornadoweb.org). After setting the event loop to `"tornado"`, the [connect](http://rethinkdb.com/api/python/connect) and [run](http://rethinkdb.com/api/python/run) commands will return Tornado `Future` objects.\n\n*Example* Read a table\'s data using Tornado.\n\n r.set_loop_type("tornado")\n conn = r.connect(host=\'localhost\', port=28015)\n \n @gen.coroutine\n def use_cursor(conn):\n # Print every row in the table.\n cursor = yield r.table(\'test\').order_by(index="id").run(yield conn)\n while (yield cursor.fetch_next()):\n item = yield cursor.next()\n print(item)\n\nFor a longer discussion with Tornado examples, see the documentation article on [Asynchronous connections][ac].\n\n[ac]: /docs/async-connections/\n'),
+ (rethinkdb.net.Connection.use, b"conn.use(db_name)\n\nChange the default database on this connection.\n\n*Example* Change the default database so that we don't need to\nspecify the database when referencing a table.\n\n conn.use('marvel')\n r.table('heroes').run(conn) # refers to r.db('marvel').table('heroes')\n"),
+ (rethinkdb.ast.Table.config, b'table.config() -> selection&lt;object&gt;\ndatabase.config() -> selection&lt;object&gt;\n\nQuery (read and/or update) the configurations for individual tables or databases.\n\nThe `config` command is a shorthand way to access the `table_config` or `db_config` [System tables](http://rethinkdb.com/docs/system-tables/). It will return the single row from the system that corresponds to the database or table configuration, as if [get](http://rethinkdb.com/api/python/get) had been called on the system table with the UUID of the database or table in question.\n\n*Example* Get the configuration for the `users` table.\n\n r.table(\'users\').config().run(conn)\n \n {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "users",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "a", "replicas": ["a", "b"]},\n {"primary_replica": "d", "replicas": ["c", "d"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n }\n\n*Example* Change the write acknowledgement requirement of the `users` table.\n\n r.table(\'users\').config().update({\'write_acks\': \'single\'}).run(conn)\n'),
+ (rethinkdb.ast.Table.rebalance, b'table.rebalance() -> object\ndatabase.rebalance() -> object\n\nRebalances the shards of a table. When called on a database, all the tables in that database will be rebalanced.\n\nThe `rebalance` command operates by measuring the distribution of primary keys within a table and picking split points that will give each shard approximately the same number of documents. It won\'t change the number of shards within a table, or change any other configuration aspect for the table or the database.\n\nA table will lose availability temporarily after `rebalance` is called; use the [wait](http://rethinkdb.com/api/python/wait) command to wait for the table to become available again, or [status](http://rethinkdb.com/api/python/status) to check if the table is available for writing.\n\nRethinkDB automatically rebalances tables when the number of shards are increased, and as long as your documents have evenly distributed primary keys&mdash;such as the default UUIDs&mdash;it is rarely necessary to call `rebalance` manually. Cases where `rebalance` may need to be called include:\n\n* Tables with unevenly distributed primary keys, such as incrementing integers\n* Changing a table\'s primary key type\n* Increasing the number of shards on an empty table, then using non-UUID primary keys in that table\n\nThe [web UI](http://rethinkdb.com/docs/administration-tools/) (and the [info](http://rethinkdb.com/api/python/info) command) can be used to tell you when a table\'s shards need to be rebalanced.\n\nThe return value of `rebalance` is an object with two fields:\n\n* `rebalanced`: the number of tables rebalanced.\n* `status_changes`: a list of new and old table status values. Each element of the list will be an object with two fields:\n * `old_val`: The table\'s [status](http://rethinkdb.com/api/python/status) value before `rebalance` was executed. \n * `new_val`: The table\'s `status` value after `rebalance` was executed. (This value will almost always indicate the table is unavailable.)\n\nSee the [status](http://rethinkdb.com/api/python/status) command for an explanation of the objects returned in the `old_val` and `new_val` fields.\n\n*Example* Rebalance a table.\n\n r.table(\'superheroes\').rebalance().run(conn)\n \n {\n "rebalanced": 1,\n "status_changes": [\n {\n "old_val": {\n "db": "database",\n "id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n "name": "superheroes",\n "shards": [\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n },\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n }\n ],\n "status": {\n "all_replicas_ready": True,\n "ready_for_outdated_reads": True,\n "ready_for_reads": True,\n "ready_for_writes": True\n }\n },\n "new_val": {\n "db": "database",\n "id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n "name": "superheroes",\n "shards": [\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "transitioning"\n }\n ]\n },\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "transitioning"\n }\n ]\n }\n ],\n "status": {\n "all_replicas_ready": False,\n "ready_for_outdated_reads": False,\n "ready_for_reads": False,\n "ready_for_writes": False\n }\n }\n \n }\n ]\n }\n'),
+ (rethinkdb.ast.Table.reconfigure, b'table.reconfigure(shards=<s>, replicas=<r>[, primary_replica_tag=<t>, dry_run=False]) -> object\ndatabase.reconfigure(shards=<s>, replicas=<r>[, primary_replica_tag=<t>, dry_run=False]) -> object\n\nReconfigure a table\'s sharding and replication.\n\n* `shards`: the number of shards, an integer from 1-32. Required.\n* `replicas`: either an integer or a mapping object. Required.\n * If `replicas` is an integer, it specifies the number of replicas per shard. Specifying more replicas than there are servers will return an error.\n * If `replicas` is an object, it specifies key-value pairs of server tags and the number of replicas to assign to those servers: `{"tag1": 2, "tag2": 4, "tag3": 2, ...}`. For more information about server tags, read [Administration tools](http://rethinkdb.com/docs/administration-tools/).\n* `primary_replica_tag`: the primary server specified by its server tag. Required if `replicas` is an object; the tag must be in the object. This must *not* be specified if `replicas` is an integer.\n* `dry_run`: if `True` the generated configuration will not be applied to the table, only returned.\n\nThe return value of `reconfigure` is an object with three fields:\n\n* `reconfigured`: the number of tables reconfigured. This will be `0` if `dry_run` is `True`.\n* `config_changes`: a list of new and old table configuration values. Each element of the list will be an object with two fields:\n * `old_val`: The table\'s [config](http://rethinkdb.com/api/python/config) value before `reconfigure` was executed. \n * `new_val`: The table\'s `config` value after `reconfigure` was executed.\n* `status_changes`: a list of new and old table status values. Each element of the list will be an object with two fields:\n * `old_val`: The table\'s [status](http://rethinkdb.com/api/python/status) value before `reconfigure` was executed. \n * `new_val`: The table\'s `status` value after `reconfigure` was executed.\n\nFor `config_changes` and `status_changes`, see the [config](http://rethinkdb.com/api/python/config) and [status](http://rethinkdb.com/api/python/status) commands for an explanation of the objects returned in the `old_val` and `new_val` fields.\n\nA table will lose availability temporarily after `reconfigure` is called; use the [table_status](http://rethinkdb.com/api/python/table_status) command to determine when the table is available again.\n\n**Note:** Whenever you call `reconfigure`, the write durability will be set to `hard` and the write acknowledgments will be set to `majority`; these can be changed by using the `config` command on the table.\n\nIf `reconfigure` is called on a database, all the tables in the database will have their configurations affected. The return value will be an array of the objects described above, one per table.\n\nRead [Sharding and replication](http://rethinkdb.com/docs/sharding-and-replication/) for a complete discussion of the subject, including advanced topics.\n\n*Example* Reconfigure a table.\n\n r.table(\'superheroes\').reconfigure(shards=2, replicas=1).run(conn)\n \n {\n "reconfigured": 1,\n "config_changes": [\n {\n "new_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "jeeves", "replicas": ["jeeves"]},\n {"primary_replica": "alfred", "replicas": ["alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n },\n "old_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "alfred", "replicas": ["alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n }\n }\n ],\n "status_changes": [\n {\n "new_val": (status object),\n "old_val": (status object)\n }\n ]\n }\n\n*Example* Reconfigure a table, specifying replicas by server tags.\n\n r.table(\'superheroes\').reconfigure(shards=2, replicas={\'wooster\': 1, \'wayne\': 1}, primary_replica_tag=\'wooster\').run(conn)\n \n {\n "reconfigured": 1,\n "config_changes": [\n {\n "new_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "jeeves", "replicas": ["jeeves", "alfred"]},\n {"primary_replica": "jeeves", "replicas": ["jeeves", "alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n },\n "old_val": {\n "id": "31c92680-f70c-4a4b-a49e-b238eb12c023",\n "name": "superheroes",\n "db": "superstuff",\n "primary_key": "id",\n "shards": [\n {"primary_replica": "alfred", "replicas": ["alfred"]}\n ],\n "write_acks": "majority",\n "durability": "hard"\n }\n }\n ],\n "status_changes": [\n {\n "new_val": (status object),\n "old_val": (status object)\n }\n ]\n }\n'),
+ (rethinkdb.ast.Table.status, b'table.status() -> selection&lt;object&gt;\n\nReturn the status of a table.\n\nThe return value is an object providing information about the table\'s shards, replicas and replica readiness states. For a more complete discussion of the object fields, read about the `table_status` table in [System tables](http://rethinkdb.com/docs/system-tables/).\n\n* `db`: database name.\n* `name`: table name.\n* `id`: table UUID.\n* `shards`: an array of objects, one for each shard, with the following keys per object:\n * `primary_replica`: name of the shard\'s primary server.\n * `replicas`: an array of objects showing the status of each replica, with the following keys:\n * `server`: name of the replica server.\n * `state`: one of `ready`, `disconnected`, `backfilling_data`, `offloading_data`, `erasing_data`, `looking_for_primary_replica` or `transitioning`.\n* `status`: an object with the following boolean keys:\n * `all_replicas_ready`: `True` if all backfills have finished.\n * `ready_for_outdated_reads`: `True` if the table is ready for read queries with the `use_outdated` flag set to `True`.\n * `ready_for_reads`: `True` if the table is ready for read queries with current data (with the `use_outdated` flag set to `False` or unspecified).\n * `ready_for_writes`: `True` if the table is ready for write queries.\n\n*Example* Get a table\'s status.\n\n r.table(\'superheroes\').status().run(conn)\n \n {\n "db": "database",\n "id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n "name": "superheroes",\n "shards": [\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n },\n {\n "primary_replica": "jeeves",\n "replicas": [\n {\n "server": "jeeves",\n "state": "ready"\n }\n ]\n }\n ],\n "status": {\n "all_replicas_ready": True,\n "ready_for_outdated_reads": True,\n "ready_for_reads": True,\n "ready_for_writes": True\n }\n }\n'),
+ (rethinkdb.ast.Table.wait, b'table.wait([wait_for=\'ready_for_writes\', timeout=<sec>]) -> object\ndatabase.wait([wait_for=\'ready_for_writes\', timeout=<sec>]) -> object\nr.wait([wait_for=\'ready_for_writes\', timeout=<sec>]) -> object\n\nWait for a table or all the tables in a database to be ready. A table may be temporarily unavailable after creation, rebalancing or reconfiguring. The `wait` command blocks until the given table (or database) is fully up to date.\n\nThe `wait` command takes two optional arguments:\n\n* `wait_for`: a string indicating a table [status](http://rethinkdb.com/api/python/status) to wait on before returning, one of `ready_for_outdated_reads`, `ready_for_reads`, `ready_for_writes`, or `all_replicas_ready`. The default is `ready_for_writes`. \n* `timeout`: a number indicating maximum time to wait for in seconds before returning. The default is no timeout.\n\nThe return value is an object consisting of two key/value pairs:\n\n* `ready`: an integer indicating the number of tables waited for. It will always be `1` when `wait` is called on a table, and the total number of tables when called on a database.\n* `status_changes`: a list with one entry for each of the tables. Each member of the list will be an object with two fields:\n * `old_val`: The table\'s [status](http://rethinkdb.com/api/python/status) value before `wait` was executed. \n * `new_val`: The table\'s `status` value after `wait` finished.\n\nSee [status](http://rethinkdb.com/api/python/status) and [System tables](http://rethinkdb.com/docs/system-tables/) for a description of the fields within `status_changes`.\n\nIf `wait` is called with no table or database specified (the `r.wait()` form), it will wait on all the tables in the default database (set with the [connect](http://rethinkdb.com/api/python/connect/) command\'s `db` parameter, which defaults to `test`).\n\n*Example* Wait on a table to be ready.\n\n r.table(\'superheroes\').wait().run(conn)\n \n {\n "ready": 1,\n "status_changes": [\n \t{\n \t "old_val": {\n \t\t"db": "database",\n \t\t"id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n \t\t"name": "superheroes",\n \t\t"shards": [\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t },\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t }\n \t\t],\n \t\t"status": {\n \t\t "all_replicas_ready": True,\n \t\t "ready_for_outdated_reads": True,\n \t\t "ready_for_reads": True,\n \t\t "ready_for_writes": True\n \t\t}\n \t },\n \t "new_val": {\n \t\t"db": "database",\n \t\t"id": "5cb35225-81b2-4cec-9eef-bfad15481265",\n \t\t"name": "superheroes",\n \t\t"shards": [\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t },\n \t\t {\n \t\t\t"primary_replica": None,\n \t\t\t"replicas": [\n \t\t\t {\n \t\t\t\t"server": "jeeves",\n \t\t\t\t"state": "ready"\n \t\t\t }\n \t\t\t]\n \t\t }\n \t\t],\n \t\t"status": {\n \t\t "all_replicas_ready": True,\n \t\t "ready_for_outdated_reads": True,\n \t\t "ready_for_reads": True,\n \t\t "ready_for_writes": True\n \t\t}\n \t }\n \t}\n ]\n }\n'),
+ (rethinkdb.ast.RqlQuery.avg, b"sequence.avg([field_or_function]) -> number\n\nAverages all the elements of a sequence. If called with a field name,\naverages all the values of that field in the sequence, skipping\nelements of the sequence that lack that field. If called with a\nfunction, calls that function on every element of the sequence and\naverages the results, skipping elements of the sequence where that\nfunction returns `None` or a non-existence error.\n\nProduces a non-existence error when called on an empty sequence. You\ncan handle this case with `default`.\n\n*Example* What's the average of 3, 5, and 7?\n\n r.expr([3, 5, 7]).avg().run(conn)\n\n*Example* What's the average number of points scored in a game?\n\n r.table('games').avg('points').run(conn)\n\n*Example* What's the average number of points scored in a game,\ncounting bonus points?\n\n r.table('games').avg(lambda game:\n game['points'] + game['bonus_points']\n ).run(conn)\n\n*Example* What's the average number of points scored in a game?\n(But return `None` instead of raising an error if there are no games where\npoints have been scored.)\n\n r.table('games').avg('points').default(None).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.contains, b"sequence.contains(value|predicate[, value|predicate, ...]) -> bool\n\nWhen called with values, returns `True` if a sequence contains all the\nspecified values. When called with predicate functions, returns `True`\nif for each predicate there exists at least one element of the stream\nwhere that predicate returns `True`.\n\nValues and predicates may be mixed freely in the argument list.\n\n*Example* Has Iron Man ever fought Superman?\n\n r.table('marvel').get('ironman')['opponents'].contains('superman').run(conn)\n\n*Example* Has Iron Man ever defeated Superman in battle?\n\n r.table('marvel').get('ironman')['battles'].contains(lambda battle:\n (battle['winner'] == 'ironman') & (battle['loser'] == 'superman')\n ).run(conn)\n\n*Example* Use `contains` with a predicate function to simulate an `or`. Return the Marvel superheroes who live in Detroit, Chicago or Hoboken.\n\n r.table('marvel').filter(\n lambda hero: r.expr(['Detroit', 'Chicago', 'Hoboken']).contains(hero['city'])\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.count, b"sequence.count([value_or_predicate]) -> number\nbinary.count() -> number\n\nCounts the number of elements in a sequence. If called with a value,\ncounts the number of times that value occurs in the sequence. If\ncalled with a predicate function, counts the number of elements in the\nsequence where that function returns `True`.\n\nIf `count` is called on a [binary](http://rethinkdb.com/api/python/binary) object, it will return the size of the object in bytes.\n\n*Example* Count the number of users.\n\n r.table('users').count().run(conn)\n\n*Example* Count the number of 18 year old users.\n\n r.table('users')['age'].count(18).run(conn)\n\n*Example* Count the number of users over 18.\n\n r.table('users')['age'].count(lambda age: age > 18).run(conn)\n\n r.table('users').count(lambda user: user['age'] > 18).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.distinct, b"sequence.distinct() -> array\ntable.distinct([index=<indexname>]) -> stream\n\nRemoves duplicate elements from a sequence.\n\nThe `distinct` command can be called on any sequence or table with an index.\n\n{% infobox %}\nWhile `distinct` can be called on a table without an index, the only effect will be to convert the table into a stream; the content of the stream will not be affected.\n{% endinfobox %}\n\n*Example* Which unique villains have been vanquished by Marvel heroes?\n\n r.table('marvel').concat_map(\n lambda hero: hero['villain_list']).distinct().run(conn)\n\n*Example* Topics in a table of messages have a secondary index on them, and more than one message can have the same topic. What are the unique topics in the table?\n\n r.table('messages').distinct(index='topics').run(conn)\n\nThe above structure is functionally identical to:\n\n r.table('messages')['topics'].distinct().run(conn)\n\nHowever, the first form (passing the index as an argument to `distinct`) is faster, and won't run into array limit issues since it's returning a stream.\n"),
+ (rethinkdb.ast.RqlQuery.group, b'sequence.group(field_or_function..., [index=\'index_name\', multi=False]) -> grouped_stream\n\nTakes a stream and partitions it into multiple groups based on the\nfields or functions provided.\n\nWith the `multi` flag single documents can be assigned to multiple groups, similar to the behavior of [multi-indexes](http://rethinkdb.com/docs/secondary-indexes/python). When `multi` is `True` and the grouping value is an array, documents will be placed in each group that corresponds to the elements of the array. If the array is empty the row will be ignored.\n\n*Example* Grouping games by player.\n\nSuppose that the table `games` has the following data:\n\n [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"},\n {"id": 12, "player": "Alice", "points": 2, "type": "free"}\n ]\n\nGrouping games by player can be done with:\n\n > r.table(\'games\').group(\'player\').run(conn)\n \n {\n "Alice": [\n {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n {"id": 12, "player": "Alice", "points": 2, "type": "free"}\n ],\n "Bob": [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"}\n ]\n }\n\nCommands chained after `group` will be called on each of these grouped\nsub-streams, producing grouped data.\n\n*Example* What is each player\'s best game?\n\n > r.table(\'games\').group(\'player\').max(\'points\').run(conn)\n \n {\n "Alice": {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n "Bob": {"id": 2, "player": "Bob", "points": 15, "type": "ranked"}\n }\n\nCommands chained onto grouped data will operate on each grouped datum,\nproducing more grouped data.\n\n*Example* What is the maximum number of points scored by each player?\n\n > r.table(\'games\').group(\'player\').max(\'points\')[\'points\'].run(conn)\n \n {\n "Alice": 7,\n "Bob": 15\n }\n\nYou can also group by more than one field.\n\n*Example* What is the maximum number of points scored by each\nplayer for each game type?\n\n > r.table(\'games\').group(\'player\', \'type\').max(\'points\')[\'points\'].run(conn)\n \n {\n ("Alice", "free"): 7,\n ("Bob", "free"): 10,\n ("Bob", "ranked"): 15\n }\n\nYou can also group by a function.\n\n*Example* What is the maximum number of points scored by each\nplayer for each game type?\n\n > r.table(\'games\')\n .group(lambda game:\n game.pluck(\'player\', \'type\')\n ).max(\'points\')[\'points\'].run(conn)\n \n {\n frozenset([(\'player\', \'Alice\'), (\'type\', \'free\')]): 7,\n frozenset([(\'player\', \'Bob\'), (\'type\', \'free\')]): 10,\n frozenset([(\'player\', \'Bob\'), (\'type\', \'ranked\')]): 15,\n }\n\nUsing a function, you can also group by date on a ReQL [date field](http://rethinkdb.com/docs/dates-and-times/javascript/).\n\n*Example* How many matches have been played this year by month?\n\n > r.table(\'matches\').group(\n lambda match: [match[\'date\'].year(), match[\'date\'].month()]\n ).count().run(conn)\n \n {\n (2014, 2): 2,\n (2014, 3): 2,\n (2014, 4): 1,\n (2014, 5): 3\n }\n\nYou can also group on an index (primary key or secondary).\n\n*Example* What is the maximum number of points scored by game type?\n\n > r.table(\'games\').group(index=\'type\').max(\'points\')[\'points\'].run(conn)\n \n {\n "free": 10,\n "ranked": 15\n }\n\nSuppose that the table `games2` has the following data:\n\n [\n { \'id\': 1, \'matches\': {\'a\': [1, 2, 3], \'b\': [4, 5, 6]} },\n { \'id\': 2, \'matches\': {\'b\': [100], \'c\': [7, 8, 9]} },\n { \'id\': 3, \'matches\': {\'a\': [10, 20], \'c\': [70, 80]} }\n ]\n\nUsing the `multi` option we can group data by match A, B or C.\n\n > r.table(\'games2\').group(r.row[\'matches\'].keys(), multi=True).run(conn)\n \n [\n {\n \'group\': \'a\',\n \'reduction\': [ <id 1>, <id 3> ]\n },\n {\n \'group\': \'b\',\n \'reduction\': [ <id 1>, <id 2> ]\n },\n {\n \'group\': \'c\',\n \'reduction\': [ <id 2>, <id 3> ]\n }\n ]\n\n(The full result set is abbreviated in the figure; `<id 1>, <id 2>` and `<id 3>` would be the entire documents matching those keys.)\n\n*Example* Use [map](http://rethinkdb.com/api/python/map) and [sum](http://rethinkdb.com/api/python/sum) to get the total points scored for each match.\n\n r.table(\'games2\').group(r.row[\'matches\'].keys(), multi=True).ungroup().map(\n lambda doc: { \'match\': doc[\'group\'], \'total\': doc[\'reduction\'].sum(\n lambda set: set[\'matches\'][doc[\'group\']].sum()\n )}).run(conn)\n \n [\n { \'match\': \'a\', \'total\': 36 },\n { \'match\': \'b\', \'total\': 115 },\n { \'match\': \'c\', \'total\': 174 }\n ]\n\nThe inner `sum` adds the scores by match within each document; the outer `sum` adds those results together for a total across all the documents.\n\nIf you want to operate on all the groups rather than operating on each\ngroup (e.g. if you want to order the groups by their reduction), you\ncan use [ungroup](http://rethinkdb.com/api/python/ungroup/) to turn a grouped stream or\ngrouped data into an array of objects representing the groups.\n\n*Example* Ungrouping grouped data.\n\n > r.table(\'games\').group(\'player\').max(\'points\')[\'points\'].ungroup().run(conn)\n \n [\n {\n "group": "Alice",\n "reduction": 7\n },\n {\n "group": "Bob",\n "reduction": 15\n }\n ]\n\nUngrouping is useful e.g. for ordering grouped data, or for inserting\ngrouped data into a table.\n\n*Example* What is the maximum number of points scored by each\nplayer, with the highest scorers first?\n\n > r.table(\'games\').group(\'player\').max(\'points\')[\'points\'].ungroup().order_by(\n r.desc(\'reduction\')).run(conn)\n \n [\n {\n "group": "Bob",\n "reduction": 15\n },\n {\n "group": "Alice",\n "reduction": 7\n }\n ]\n\nWhen grouped data are returned to the client, they are transformed\ninto a client-specific native type. (Something similar is done with\n[times](http://rethinkdb.com/docs/dates-and-times/).) In Python, grouped data are\ntransformed into a `dictionary`. If the group value is an `array`, the\nkey is converted to a `tuple`. If the group value is a `dictionary`,\nit will be converted to a `frozenset`.\n\nIf you instead want to receive the raw\npseudotype from the server (e.g. if you\'re planning to serialize the\nresult as JSON), you can specify `group_format: \'raw\'` as an optional\nargument to `run`:\n\n*Example* Get back the raw `GROUPED_DATA` pseudotype.\n\n > r.table(\'games\').group(\'player\').avg(\'points\').run(conn, group_format=\'raw\')\n \n {\n "$reql_type$": "GROUPED_DATA",\n "data": [\n ["Alice", 4.5],\n ["Bob", 12.5]\n ]\n }\n\nNot passing the `group_format` flag would return:\n\n {\n "Alice": 4.5,\n "Bob": 12.5\n }\n\nYou might also want to use the [ungroup](http://rethinkdb.com/api/python/ungroup/)\ncommand (see above), which will turn the grouped data into an array of\nobjects on the server.\n\nIf you run a query that returns a grouped stream, it will be\nautomatically converted to grouped data before being sent back to you\n(there is currently no efficient way to stream groups from RethinkDB).\nThis grouped data is subject to the array size limit (see [run](http://rethinkdb.com/api/python/run)).\n\nIn general, operations on grouped streams will be efficiently\ndistributed, and operations on grouped data won\'t be. You can figure\nout what you\'re working with by putting `type_of` on the end of your\nquery. Below are efficient and inefficient examples.\n\n*Example* Efficient operation.\n\n # r.table(\'games\').group(\'player\').type_of().run(conn)\n # Returns "GROUPED_STREAM"\n r.table(\'games\').group(\'player\').min(\'points\').run(conn) # EFFICIENT\n\n*Example* Inefficient operation.\n\n # r.table(\'games\').group(\'player\').order_by(\'score\').type_of().run(conn)\n # Returns "GROUPED_DATA"\n r.table(\'games\').group(\'player\').order_by(\'score\').nth(0).run(conn) # INEFFICIENT\n\nWhat does it mean to be inefficient here? When operating on grouped\ndata rather than a grouped stream, *all* of the data has to be\navailable on the node processing the query. This means that the\noperation will only use one server\'s resources, and will require\nmemory proportional to the size of the grouped data it\'s operating\non. (In the case of the `order_by` in the inefficient example, that\nmeans memory proportional **to the size of the table**.) The array\nlimit is also enforced for grouped data, so the `order_by` example\nwould fail for tables with more than 100,000 rows unless you used the `array_limit` option with `run`.\n\n*Example* What is the maximum number of points scored by each\nplayer in free games?\n\n > r.table(\'games\').filter(lambda game:\n game[\'type\'] = \'free\'\n ).group(\'player\').max(\'points\')[\'points\'].run(conn)\n \n {\n "Alice": 7,\n "Bob": 10\n }\n\n*Example* What is each player\'s highest even and odd score?\n\n > r.table(\'games\')\n .group(\'name\', lambda game:\n game[\'points\'] % 2\n ).max(\'points\')[\'points\'].run(conn)\n \n {\n ("Alice", 1): 7,\n ("Bob", 0): 10,\n ("Bob", 1): 15\n }\n'),
+ (rethinkdb.ast.RqlQuery.max, b"sequence.max(field_or_function) -> element\nsequence.max(index='index') -> element\n\nFinds the maximum element of a sequence. The `max` command can be called with:\n\n* a **field name**, to return the element of the sequence with the largest value in that field;\n* an **index** (the primary key or a secondary index), to return the element of the sequence with the largest value in that index;\n* a **function**, to apply the function to every element within the sequence and return the element which returns the largest value from the function, ignoring any elements where the function produces a non-existence error.\n\nFor more information on RethinkDB's sorting order, read the section in [ReQL data types](http://rethinkdb.com/docs/data-types/#sorting-order).\n\nCalling `max` on an empty sequence will throw a non-existence error; this can be handled using the [default](http://rethinkdb.com/api/python/default/) command.\n\n*Example* Return the maximum value in the list `[3, 5, 7]`.\n\n r.expr([3, 5, 7]).max().run(conn)\n\n*Example* Return the user who has scored the most points.\n\n r.table('users').max('points').run(conn)\n\n*Example* The same as above, but using a secondary index on the `points` field.\n\n r.table('users').max(index='points').run(conn)\n\n*Example* Return the user who has scored the most points, adding in bonus points from a separate field using a function.\n\n r.table('users').max(lambda user:\n user['points'] + user['bonus_points']\n ).run(conn)\n\n*Example* Return the highest number of points any user has ever scored. This returns the value of that `points` field, not a document.\n\n r.table('users').max('points')['points'].run(conn)\n\n*Example* Return the user who has scored the most points, but add a default `None` return value to prevent an error if no user has ever scored points.\n\n r.table('users').max('points').default(None).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.min, b"sequence.min(field_or_function) -> element\nsequence.min(index='index') -> element\n\nFinds the minimum element of a sequence. The `min` command can be called with:\n\n* a **field name**, to return the element of the sequence with the smallest value in that field;\n* an **index** (the primary key or a secondary index), to return the element of the sequence with the smallest value in that index;\n* a **function**, to apply the function to every element within the sequence and return the element which returns the smallest value from the function, ignoring any elements where the function produces a non-existence error.\n\nFor more information on RethinkDB's sorting order, read the section in [ReQL data types](http://rethinkdb.com/docs/data-types/#sorting-order).\n\nCalling `min` on an empty sequence will throw a non-existence error; this can be handled using the [default](http://rethinkdb.com/api/python/default/) command.\n\n*Example* Return the minimum value in the list `[3, 5, 7]`.\n\n r.expr([3, 5, 7]).min().run(conn)\n\n*Example* Return the user who has scored the fewest points.\n\n r.table('users').min('points').run(conn)\n\n*Example* The same as above, but using a secondary index on the `points` field.\n\n r.table('users').min(index='points').run(conn)\n\n*Example* Return the user who has scored the fewest points, adding in bonus points from a separate field using a function.\n\n r.table('users').min(lambda user:\n user['points'] + user['bonus_points']\n ).run(conn)\n\n*Example* Return the smallest number of points any user has ever scored. This returns the value of that `points` field, not a document.\n\n r.table('users').min('points')['points'].run(conn)\n\n*Example* Return the user who has scored the fewest points, but add a default `None` return value to prevent an error if no user has ever scored points.\n\n r.table('users').min('points').default(None).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.reduce, b'sequence.reduce(reduction_function) -> value\n\nProduce a single value from a sequence through repeated application of a reduction\nfunction. \nThe reduction function can be called on:\n\n- two elements of the sequence\n- one element of the sequence and one result of a previous reduction\n- two results of previous reductions\n\nThe reduction function can be called on the results of two previous reductions because the\n`reduce` command is distributed and parallelized across shards and CPU cores. A common\nmistaken when using the `reduce` command is to suppose that the reduction is executed\nfrom left to right. Read the [map-reduce in RethinkDB](http://rethinkdb.com/docs/map-reduce/) article to\nsee an example.\n\nIf the sequence is empty, the server will produce a `RqlRuntimeError` that can be\ncaught with `default`. \nIf the sequence has only one element, the first element will be returned.\n\n*Example* Return the number of documents in the table `posts`.\n\n r.table("posts").map(lambda doc: 1)\n .reduce(lambda left, right: left+right)\n .default(0).run(conn)\n\nA shorter way to execute this query is to use [count](http://rethinkdb.com/api/python/count).\n\n*Example* Suppose that each `post` has a field `comments` that is an array of\ncomments. \nReturn the number of comments for all posts.\n\n r.table("posts").map(lambda doc:\n doc["comments"].count()\n ).reduce(lambda left, right:\n left+right\n ).default(0).run(conn)\n\n*Example* Suppose that each `post` has a field `comments` that is an array of\ncomments. \nReturn the maximum number comments per post.\n\n r.table("posts").map(lambda doc:\n doc["comments"].count()\n ).reduce(lambda left, right:\n r.branch(\n left > right,\n left,\n right\n )\n ).default(0).run(conn)\n\nA shorter way to execute this query is to use [max](http://rethinkdb.com/api/python/max).\n'),
+ (rethinkdb.ast.RqlQuery.sum, b"sequence.sum([field_or_function]) -> number\n\nSums all the elements of a sequence. If called with a field name,\nsums all the values of that field in the sequence, skipping elements\nof the sequence that lack that field. If called with a function,\ncalls that function on every element of the sequence and sums the\nresults, skipping elements of the sequence where that function returns\n`None` or a non-existence error.\n\nReturns `0` when called on an empty sequence.\n\n*Example* What's 3 + 5 + 7?\n\n r.expr([3, 5, 7]).sum().run(conn)\n\n*Example* How many points have been scored across all games?\n\n r.table('games').sum('points').run(conn)\n\n*Example* How many points have been scored across all games,\ncounting bonus points?\n\n r.table('games').sum(lambda game:\n game['points'] + game['bonus_points']\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.ungroup, b'grouped_stream.ungroup() -> array\ngrouped_data.ungroup() -> array\n\nTakes a grouped stream or grouped data and turns it into an array of\nobjects representing the groups. Any commands chained after `ungroup`\nwill operate on this array, rather than operating on each group\nindividually. This is useful if you want to e.g. order the groups by\nthe value of their reduction.\n\nThe format of the array returned by `ungroup` is the same as the\ndefault native format of grouped data in the JavaScript driver and\ndata explorer.\n\n*Example* What is the maximum number of points scored by each\nplayer, with the highest scorers first?\n\nSuppose that the table `games` has the following data:\n\n [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 5, "player": "Alice", "points": 7, "type": "free"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"},\n {"id": 12, "player": "Alice", "points": 2, "type": "free"}\n ]\n\nWe can use this query:\n\n r.table(\'games\')\n .group(\'player\').max(\'points\')[\'points\']\n .ungroup().order_by(r.desc(\'reduction\')).run(conn)\n\nResult: \n\n [\n {\n "group": "Bob",\n "reduction": 15\n },\n {\n "group": "Alice",\n "reduction": 7\n }\n ]\n\n*Example* Select one random player and all their games.\n\n r.table(\'games\').group(\'player\').ungroup().sample(1).run(conn)\n\nResult:\n\n [\n {\n "group": "Bob",\n "reduction": [\n {"id": 2, "player": "Bob", "points": 15, "type": "ranked"},\n {"id": 11, "player": "Bob", "points": 10, "type": "free"}\n ]\n }\n ]\n\nNote that if you didn\'t call `ungroup`, you would instead select one\nrandom game from each player:\n\n r.table(\'games\').group(\'player\').sample(1).run(conn)\n\nResult:\n\n {\n "Alice": [\n {"id": 5, "player": "Alice", "points": 7, "type": "free"}\n ],\n "Bob": [\n {"id": 11, "player": "Bob", "points": 10, "type": "free"}\n ]\n }\n\n*Example* Types!\n\n r.table(\'games\').group(\'player\').type_of().run(conn) # Returns "GROUPED_STREAM"\n r.table(\'games\').group(\'player\').ungroup().type_of().run(conn) # Returns "ARRAY"\n r.table(\'games\').group(\'player\').avg(\'points\').run(conn) # Returns "GROUPED_DATA"\n r.table(\'games\').group(\'player\').avg(\'points\').ungroup().run(conn) #Returns "ARRAY"\n'),
+ (rethinkdb.args, b"r.args(array) -> special\n\n`r.args` is a special term that's used to splice an array of arguments\ninto another term. This is useful when you want to call a variadic\nterm such as `get_all` with a set of arguments produced at runtime.\n\nThis is analogous to unpacking argument lists in Python.\n\n*Example* Get Alice and Bob from the table `people`.\n\n r.table('people').get_all('Alice', 'Bob').run(conn)\n # or\n r.table('people').get_all(r.args(['Alice', 'Bob'])).run(conn)\n\n*Example* Get all of Alice's children from the table `people`.\n\n # r.table('people').get('Alice') returns {'id': 'Alice', 'children': ['Bob', 'Carol']}\n r.table('people').get_all(r.args(r.table('people').get('Alice')['children'])).run(conn)\n"),
+ (rethinkdb.binary, b'r.binary(data) -> binary\n\nEncapsulate binary data within a query.\n\nThe type of data `binary` accepts depends on the client language. In Python, it expects a parameter of `bytes` type. Using a `bytes` object within a query implies the use of `binary` and the ReQL driver will automatically perform the coercion (in Python 3 only).\n\nBinary objects returned to the client in JavaScript will also be of the `bytes` type. This can be changed with the `binary_format` option provided to [run](http://rethinkdb.com/api/python/run) to return "raw" objects.\n\nOnly a limited subset of ReQL commands may be chained after `binary`:\n\n* [coerce_to](http://rethinkdb.com/api/python/coerce_to/) can coerce `binary` objects to `string` types\n* [count](http://rethinkdb.com/api/python/count/) will return the number of bytes in the object\n* [slice](http://rethinkdb.com/api/python/slice/) will treat bytes like array indexes (i.e., `slice(10,20)` will return bytes 10&ndash;19)\n* [type_of](http://rethinkdb.com/api/python/type_of) returns `PTYPE<BINARY>`\n* [info](http://rethinkdb.com/api/python/info) will return information on a binary object.\n\n*Example* Save an avatar image to a existing user record.\n\n f = open(\'./default_avatar.png\', \'rb\')\n avatar_image = f.read()\n f.close()\n r.table(\'users\').get(100).update({\'avatar\': r.binary(avatar_image)}).run(conn)\n\n*Example* Get the size of an existing avatar image.\n\n r.table(\'users\').get(100)[\'avatar\'].count().run(conn)\n \n 14156\n\nRead more details about RethinkDB\'s binary object support: [Storing binary objects](http://rethinkdb.com/docs/storing-binary/).\n'),
+ (rethinkdb.branch, b'r.branch(test, true_branch, false_branch) -> any\n\nIf the `test` expression returns `False` or `None`, the `false_branch` will be evaluated.\nOtherwise, the `true_branch` will be evaluated.\n \nThe `branch` command is effectively an `if` renamed due to language constraints.\n\n*Example* Return heroes and superheroes.\n\n r.table(\'marvel\').map(\n r.branch(\n r.row[\'victories\'] > 100,\n r.row[\'name\'] + \' is a superhero\',\n r.row[\'name\'] + \' is a hero\'\n )\n ).run(conn)\n\nIf the documents in the table `marvel` are:\n\n [{\n "name": "Iron Man",\n "victories": 214\n },\n {\n "name": "Jubilee",\n "victories": 9\n }]\n\nThe results will be:\n\n [\n "Iron Man is a superhero",\n "Jubilee is a hero"\n ]\n'),
+ (rethinkdb.ast.RqlQuery.coerce_to, b"sequence.coerce_to('array') -> array\nvalue.coerce_to('string') -> string\nstring.coerce_to('number') -> number\narray.coerce_to('object') -> object\nobject.coerce_to('array') -> array\nbinary.coerce_to('string') -> string\nstring.coerce_to('binary') -> binary\n\nConvert a value of one type into another.\n\n* a sequence, selection or object can be coerced to an array\n* an array of key-value pairs can be coerced to an object\n* a string can be coerced to a number\n* any datum (single value) can be coerced to a string\n* a binary object can be coerced to a string and vice-versa\n\n*Example* Coerce a stream to an array to store its output in a field. (A stream cannot be stored in a field directly.)\n\n r.table('posts').map(lambda post: post.merge(\n { 'comments': r.table('comments').get_all(post['id'], index='post_id').coerce_to('array') }\n )).run(conn)\n\n*Example* Coerce an array of pairs into an object.\n\n r.expr([['name', 'Ironman'], ['victories', 2000]]).coerce_to('object').run(conn)\n\n__Note:__ To coerce a list of key-value pairs like `['name', 'Ironman', 'victories', 2000]` to an object, use the [object](http://rethinkdb.com/api/python/object) command.\n\n*Example* Coerce a number to a string.\n\n r.expr(1).coerce_to('string').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.default, b'value.default(default_value) -> any\nsequence.default(default_value) -> any\n\nHandle non-existence errors. Tries to evaluate and return its first argument. If an\nerror related to the absence of a value is thrown in the process, or if its first\nargument returns `None`, returns its second argument. (Alternatively, the second argument\nmay be a function which will be called with either the text of the non-existence error\nor `None`.)\n\n*Example* Suppose we want to retrieve the titles and authors of the table `posts`.\nIn the case where the author field is missing or `None`, we want to retrieve the string\n`Anonymous`.\n\n r.table("posts").map(lambda post:\n {\n "title": post["title"],\n "author": post["author"].default("Anonymous")\n }\n ).run(conn)\n\nWe can rewrite the previous query with `r.branch` too.\n\n r.table("posts").map(lambda post:\n r.branch(\n post.has_fields("author"),\n {\n "title": post["title"],\n "author": post["author"]\n },\n {\n "title": post["title"],\n "author": "Anonymous" \n }\n )\n ).run(conn)\n\n*Example* The `default` command can be useful to filter documents too. Suppose\nwe want to retrieve all our users who are not grown-ups or whose age is unknown\n(i.e the field `age` is missing or equals `None`). We can do it with this query:\n\n r.table("users").filter(lambda user:\n (user["age"] < 18).default(True)\n ).run(conn)\n\nOne more way to write the previous query is to set the age to be `-1` when the\nfield is missing.\n\n r.table("users").filter(lambda user:\n user["age"].default(-1) < 18\n ).run(conn)\n\nOne last way to do the same query is to use `has_fields`.\n\n r.table("users").filter(lambda user:\n user.has_fields("age").not_() | (user["age"] < 18)\n ).run(conn)\n\nThe body of every `filter` is wrapped in an implicit `.default(False)`. You can overwrite\nthe value `False` by passing an option in filter, so the previous query can also be\nwritten like this.\n\n r.table("users").filter(\n lambda user: (user["age"] < 18).default(True),\n default=True\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.do, b"any.do(function) -> any\nr.do([args]*, function) -> any\nany.do(expr) -> any\nr.do([args]*, expr) -> any\n\nCall an anonymous function using return values from other ReQL commands or queries as arguments.\n\nThe last argument to `do` (or, in some forms, the only argument) is an expression or an anonymous function which receives values from either the previous arguments or from prefixed commands chained before `do`. The `do` command is essentially a single-element [map](http://rethinkdb.com/api/python/map/), letting you map a function over just one document. This allows you to bind a query result to a local variable within the scope of `do`, letting you compute the result just once and reuse it in a complex expression or in a series of ReQL commands.\n\nArguments passed to the `do` function must be basic data types, and cannot be streams or selections. (Read about [ReQL data types](http://rethinkdb.com/docs/data-types/).) While the arguments will all be evaluated before the function is executed, they may be evaluated in any order, so their values should not be dependent on one another. The type of `do`'s result is the type of the value returned from the function or last expression.\n\n*Example* Compute a golfer's net score for a game.\n\n r.table('players').get('86be93eb-a112-48f5-a829-15b2cb49de1d').do(\n lambda player: player['gross_score'] - player['course_handicap']\n ).run(conn)\n\n*Example* Return the name of the best scoring player in a two-player golf match.\n\n r.do(r.table('players').get(id1), r.table('players').get(id2),\n (lambda player1, player2:\n r.branch(player1['gross_score'].lt(player2['gross_score']),\n player1, player2))\n ).run(conn)\n\nNote that `branch`, the ReQL conditional command, must be used instead of `if`. See the `branch` [documentation](http://rethinkdb.com/api/python/branch) for more.\n\n*Example* Take different actions based on the result of a ReQL [insert](http://rethinkdb.com/api/python/insert) command.\n\n new_data = {\n 'id': 100,\n 'name': 'Agatha',\n 'gross_score': 57,\n 'course_handicap': 4\n }\n r.table('players').insert(new_data).do(lambda doc:\n r.branch((doc['inserted'] != 0),\n r.table('log').insert({'time': r.now(), 'response': doc, 'result': 'ok'}),\n r.table('log').insert({'time': r.now(), 'response': doc, 'result': 'error'}))\n ).run(conn)\n"),
+ (rethinkdb.error, b"r.error(message) -> error\n\nThrow a runtime error. If called with no arguments inside the second argument to `default`, re-throw the current error.\n\n*Example* Iron Man can't possibly have lost a battle:\n\n r.table('marvel').get('IronMan').do(\n lambda ironman: r.branch(ironman['victories'] < ironman['battles'],\n r.error('impossible code path'),\n ironman)\n ).run(conn)\n\n"),
+ (rethinkdb.expr, b"r.expr(value) -> value\n\nConstruct a ReQL JSON object from a native object.\n\nIf the native object is of the `bytes` type, then `expr` will return a binary object. See [binary](http://rethinkdb.com/api/python/binary) for more information.\n\n*Example* Objects wrapped with expr can then be manipulated by ReQL API functions.\n\n r.expr({'a':'b'}).merge({'b':[1,2,3]}).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.for_each, b"sequence.for_each(write_query) -> object\n\nLoop over a sequence, evaluating the given write query for each element.\n\n*Example* Now that our heroes have defeated their villains, we can safely remove them from the villain table.\n\n r.table('marvel').for_each(\n lambda hero: r.table('villains').get(hero['villainDefeated']).delete()\n ).run(conn)\n\n"),
+ (rethinkdb.http, b'r.http(url[, options]) -> value\nr.http(url[, options]) -> stream\n\nRetrieve data from the specified URL over HTTP. The return type depends on the `result_format` option, which checks the `Content-Type` of the response by default.\n\n*Example* Perform an HTTP `GET` and store the result in a table.\n\n r.table(\'posts\').insert(r.http(\'http://httpbin.org/get\')).run(conn)\n\nSee [the tutorial](http://rethinkdb.com/docs/external-api-access/) on `r.http` for more examples on how to use this command.\n\n* `timeout`: Number of seconds to wait before timing out and aborting the operation. Default: 30.\n\n* `reattempts`: An integer giving the number of attempts to make in cast of connection errors or potentially-temporary HTTP errors. Default: 5.\n\n* `redirects`: An integer giving the number of redirects and location headers to follow. Default: 1.\n\n* `verify`: Verify the server\'s SSL certificate, specified as a boolean. Default: True.\n\n* `result_format`: The format the result should be returned in. The values can be `\'text\'` (always return as a string), `\'json\'` (parse the result as JSON, raising an error if the parsing fails), `\'jsonp\'` (parse the result as [padded JSON](http://www.json-p.org/)), `\'binary\'` (return a binary object), or `\'auto\'` . The default is `\'auto\'`.\n\n When `result_format` is `\'auto\'`, the response body will be parsed according to the `Content-Type` of the response:\n * `application/json`: parse as `\'json\'`\n * `application/json-p`, `text/json-p`, `text/javascript`: parse as `\'jsonp\'`\n * `audio/*`, `video/*`, `image/*`, `application/octet-stream`: return a binary object\n * Anything else: parse as `\'text\'`\n\n* `method`: HTTP method to use for the request. One of `GET`, `POST`, `PUT`, `PATCH`, `DELETE` or `HEAD`. Default: `GET`.\n\n* `auth`: Authentication information in the form of an object with key/value pairs indicating the authentication type (in the `type` key) and any required information. Types currently supported are `basic` and `digest` for HTTP Basic and HTTP Digest authentication respectively. If `type` is omitted, `basic` is assumed. Example:\n\n\t```py\n\tr.http(\'http://httpbin.org/basic-auth/fred/mxyzptlk\',\n auth={ \'type\': \'basic\', \'user\': \'fred\', \'pass\': \'mxyzptlk\' }).run(conn)\n\t```\n\n* `params`: URL parameters to append to the URL as encoded key/value pairs, specified as an object. For example, `{ \'query\': \'banana\', \'limit\': 2 }` will be appended as `?query=banana&limit=2`. Default: none.\n\n* `header`: Extra header lines to include. The value may be an array of strings or an object. Default: none.\n\n Unless specified otherwise, `r.http` will by default use the headers `Accept-Encoding: deflate=1;gzip=0.5` and `User-Agent: RethinkDB/VERSION`.\n\n* `data`: Data to send to the server on a `POST`, `PUT`, `PATCH`, or `DELETE` request.\n\n For `PUT`, `PATCH` and `DELETE` requests, the value will be serialized to JSON and placed in the request body, and the `Content-Type` will be set to `application/json`.\n\n\tFor `POST` requests, data may be either an object or a string. Objects will be written to the body as form-encoded key/value pairs (values must be numbers, strings, or `None`). Strings will be put directly into the body. If `data` is not a string or an object, an error will be thrown.\n\n If `data` is not specified, no data will be sent.\n\n`r.http` supports depagination, which will request multiple pages in a row and aggregate the results into a stream. The use of this feature is controlled by the optional arguments `page` and `page_limit`. Either none or both of these arguments must be provided.\n\n* `page`: This option may specify either a built-in pagination strategy (as a string), or a function to provide the next URL and/or `params` to request.\n\n At the moment, the only supported built-in is `\'link-next\'`, which is equivalent to `lambda info: info[\'header\'][\'link\'][\'rel="next"\'].default(None)`.\n\n *Example* Perform a GitHub search and collect up to 3 pages of results.\n\n ```py\n r.http("https://api.github.com/search/code?q=addClass+user:mozilla",\n page=\'link-next\', page_limit=3).run(conn)\n ```\n\n As a function, `page` takes one parameter, an object of the format:\n\n ```py\n {\n \'params\': object, # the URL parameters used in the last request\n \'header\': object, # the HTTP headers of the last response as key/value pairs\n \'body\': value # the body of the last response in the format specified by `result_format`\n }\n ```\n\n The `header` field will be a parsed version of the header with fields lowercased, like so:\n\n ```py\n {\n \'content-length\': \'1024\',\n \'content-type\': \'application/json\',\n \'date\': \'Thu, 1 Jan 1970 00:00:00 GMT\',\n \'link\': {\n \'rel="last"\': \'http://example.com/?page=34\',\n \'rel="next"\': \'http://example.com/?page=2\'\n }\n }\n ```\n\n The `page` function may return a string corresponding to the next URL to request, `None` indicating that there is no more to get, or an object of the format:\n\n ```py\n {\n \'url\': string, # the next URL to request, or None for no more pages\n \'params\': object # new URL parameters to use, will be merged with the previous request\'s params\n }\n ```\n\n* `page_limit`: An integer specifying the maximum number of requests to issue using the `page` functionality. This is to prevent overuse of API quotas, and must be specified with `page`.\n * `-1`: no limit\n * `0`: no requests will be made, an empty stream will be returned\n * `n`: `n` requests will be made\n\n# Examples\n\n*Example* Perform multiple requests with different parameters.\n\n r.expr([1, 2, 3]).map(lambda i: r.http(\'http://httpbin.org/get\',\n params={ \'user\': i })).run(conn)\n\n*Example* Perform a `PUT` request for each item in a table.\n\n r.table(\'data\').map(lambda row: r.http(\'http://httpbin.org/put\',\n method=\'PUT\', data=row)).run(conn)\n\n*Example* Perform a `POST` request with accompanying data.\n\nUsing form-encoded data:\n\n r.http(\'http://httpbin.org/post\',\n method=\'POST\',\n data={ \'player\': \'Bob\', \'game\': \'tic tac toe\' }).run(conn)\n\nUsing JSON data:\n\n r.http(\'http://httpbin.org/post\',\n method=\'POST\',\n data=r.expr(value).coerce_to(\'string\'),\n header={ \'Content-Type\': \'application/json\' }).run(conn)\n\n*Example* Perform depagination with a custom `page` function.\n\n r.http(\'example.com/pages\',\n page=lambda info: info[\'body\'][\'meta\'][\'next\'].default(None),\n page_limit=5).run(conn)\n\n# Learn more\n\nSee [the tutorial](http://rethinkdb.com/docs/external-api-access/) on `r.http` for more examples on how to use this command.\n'),
+ (rethinkdb.ast.RqlQuery.info, b"any.info() -> object\n\nGet information about a ReQL value.\n\n*Example* Get information about a table such as primary key, or cache size.\n\n r.table('marvel').info().run(conn)\n\n"),
+ (rethinkdb.js, b'r.js(js_string[, timeout=<number>]) -> value\n\nCreate a javascript expression.\n\n*Example* Concatenate two strings using JavaScript.\n\n`timeout` is the number of seconds before `r.js` times out. The default value is 5 seconds.\n\n{% infobox %}\nWhenever possible, you should use native ReQL commands rather than `r.js` for better performance.\n{% endinfobox %}\n\n r.js("\'str1\' + \'str2\'").run(conn)\n\n*Example* Select all documents where the \'magazines\' field is greater than 5 by running JavaScript on the server.\n\n r.table(\'marvel\').filter(\n r.js(\'(function (row) { return row.magazines.length > 5; })\')\n ).run(conn)\n\n*Example* You may also specify a timeout in seconds (defaults to 5).\n\n r.js(\'while(true) {}\', timeout=1.3).run(conn)\n\n'),
+ (rethinkdb.json, b'r.json(json_string) -> value\n\nParse a JSON string on the server.\n\n*Example* Send an array to the server\'\n\n r.json("[1,2,3]").run(conn)\n\n'),
+ (rethinkdb.range, b'r.range() -> stream\nr.range([start_value, ]end_value) -> stream\n\nGenerate a stream of sequential integers in a specified range. `range` takes 0, 1 or 2 arguments:\n\n* With no arguments, `range` returns an "infinite" stream from 0 up to and including the maximum integer value;\n* With one argument, `range` returns a stream from 0 up to but not including the end value;\n* With two arguments, `range` returns a stream from the start value up to but not including the end value.\n\nNote that the left bound (including the implied left bound of 0 in the 0- and 1-argument form) is always closed and the right bound is always open: the start value will always be included in the returned range and the end value will *not* be included in the returned range.\n\nAny specified arguments must be integers, or a `RqlRuntimeError` will be thrown. If the start value is equal or to higher than the end value, no error will be thrown but a zero-element stream will be returned.\n\n*Example* Return a four-element range of `[0, 1, 2, 3]`.\n\n > r.range(4).run(conn)\n \n [0, 1, 2, 3]\n\nYou can also use the [limit](http://rethinkdb.com/api/python/limit) command with the no-argument variant to achieve the same result in this case:\n\n > r.range().limit(4).run(conn)\n \n [0, 1, 2, 3]\n\n*Example* Return a range from -5 through 5.\n\n > r.range(-5, 6).run(conn)\n \n [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]\n'),
+ (rethinkdb.ast.RqlQuery.to_json_string, b'value.to_json_string() -> string\nvalue.to_json() -> string\n\nConvert a ReQL value or object to a JSON string. You may use either `to_json_string` or `to_json`.\n\n*Example* Get a ReQL document as a JSON string.\n\n > r.table(\'hero\').get(1).to_json()\n \n \'{"id": 1, "name": "Batman", "city": "Gotham", "powers": ["martial arts", "cinematic entrances"]}\'\n'),
+ (rethinkdb.ast.RqlQuery.to_json, b'value.to_json_string() -> string\nvalue.to_json() -> string\n\nConvert a ReQL value or object to a JSON string. You may use either `to_json_string` or `to_json`.\n\n*Example* Get a ReQL document as a JSON string.\n\n > r.table(\'hero\').get(1).to_json()\n \n \'{"id": 1, "name": "Batman", "city": "Gotham", "powers": ["martial arts", "cinematic entrances"]}\'\n'),
+ (rethinkdb.ast.RqlQuery.type_of, b'any.type_of() -> string\n\nGets the type of a value.\n\n*Example* Get the type of a string.\n\n r.expr("foo").type_of().run(conn)\n\n'),
+ (rethinkdb.uuid, b'r.uuid() -> string\n\nReturn a UUID (universally unique identifier), a string that can be used as a unique ID.\n\n*Example* Generate a UUID.\n\n > r.uuid().run(conn)\n \n 27961a0e-f4e8-4eb3-bf95-c5203e1d87b9\n'),
+ (rethinkdb.net.Cursor.close, b'cursor.close()\n\nClose a cursor. Closing a cursor cancels the corresponding query and frees the memory\nassociated with the open request.\n\n*Example* Close a cursor.\n\n cursor.close()\n'),
+ (rethinkdb.net.Cursor.next, b"cursor.next([wait=True])\n\nGet the next element in the cursor.\n\nThe optional `wait` argument specifies whether to wait for the next available element and how long to wait:\n\n* `True`: Wait indefinitely (the default).\n* `False`: Do not wait at all. If data is immediately available, it will be returned; if it is not available, a `RqlDriverError` will be raised.\n* number: Wait up the specified number of seconds for data to be available before raising `RqlDriverError`.\n\nThe behavior of `next` will be identical with `False`, `None` or the number `0`.\n\nCalling `next` the first time on a cursor provides the first element of the cursor. If the data set is exhausted (e.g., you have retrieved all the documents in a table), a `StopIteration` error will be raised when `next` is called.\n\n*Example* Retrieve the next element.\n\n cursor = r.table('superheroes').run(conn)\n doc = cursor.next()\n\n*Example* Retrieve the next element on a [changefeed](http://rethinkdb.com/docs/changefeeds/python), waiting up to five seconds.\n\n cursor = r.table('superheroes').changes().run(conn)\n doc = cursor.next(wait=5)\n\n__Note:__ RethinkDB sequences can be iterated through via the Python [Iterable][it] interface. The canonical way to retrieve all the results is to use a [for...in](../each/) loop or [list()](../to_array/).\n\n[it]: https://docs.python.org/3.4/library/stdtypes.html#iterator-types\n"),
+ (rethinkdb.ast.RqlQuery.date, b'time.date() -> time\n\nReturn a new time object only based on the day, month and year (ie. the same day at 00:00).\n\n*Example* Retrieve all the users whose birthday is today\n\n r.table("users").filter(lambda user:\n user["birthdate"].date() == r.now().date()\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.day, b'time.day() -> number\n\nReturn the day of a time object as a number between 1 and 31.\n\n*Example* Return the users born on the 24th of any month.\n\n r.table("users").filter(\n r.row["birthdate"].day() == 24\n )\n\n'),
+ (rethinkdb.ast.RqlQuery.day_of_week, b'time.day_of_week() -> number\n\nReturn the day of week of a time object as a number between 1 and 7 (following ISO 8601 standard). For your convenience, the terms r.monday, r.tuesday etc. are defined and map to the appropriate integer.\n\n*Example* Return today\'s day of week.\n\n r.now().day_of_week().run(conn)\n\n*Example* Retrieve all the users who were born on a Tuesday.\n\n r.table("users").filter( lambda user:\n user["birthdate"].day_of_week().eq(r.tuesday)\n )\n\n'),
+ (rethinkdb.ast.RqlQuery.day_of_year, b'time.day_of_year() -> number\n\nReturn the day of the year of a time object as a number between 1 and 366 (following ISO 8601 standard).\n\n*Example* Retrieve all the users who were born the first day of a year.\n\n r.table("users").filter(\n r.row["birthdate"].day_of_year() == 1\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.during, b'time.during(start_time, end_time[, left_bound="closed", right_bound="open"])\n -> bool\n\nReturn whether a time is between two other times. By default, this is inclusive of the start time and exclusive of the end time. Set `left_bound` and `right_bound` to explicitly include (`closed`) or exclude (`open`) that endpoint of the range.\n\n*Example* Retrieve all the posts that were posted between December 1st, 2013 (inclusive) and December 10th, 2013 (exclusive).\n\n r.table("posts").filter(\n r.row[\'date\'].during(r.time(2013, 12, 1, "Z"), r.time(2013, 12, 10, "Z"))\n ).run(conn)\n\n*Example* Retrieve all the posts that were posted between December 1st, 2013 (exclusive) and December 10th, 2013 (inclusive).\n\n r.table("posts").filter(\n r.row[\'date\'].during(r.time(2013, 12, 1, "Z"), r.time(2013, 12, 10, "Z"), left_bound="open", right_bound="closed")\n ).run(conn)\n\n'),
+ (rethinkdb.epoch_time, b'r.epoch_time(epoch_time) -> time\n\nCreate a time object based on seconds since epoch. The first argument is a double and\nwill be rounded to three decimal places (millisecond-precision).\n\n*Example* Update the birthdate of the user "John" to November 3rd, 1986.\n\n r.table("user").get("John").update({"birthdate": r.epoch_time(531360000)}).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.hours, b'time.hours() -> number\n\nReturn the hour in a time object as a number between 0 and 23.\n\n*Example* Return all the posts submitted after midnight and before 4am.\n\n r.table("posts").filter(lambda post:\n post["date"].hours() < 4\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.in_timezone, b"time.in_timezone(timezone) -> time\n\nReturn a new time object with a different timezone. While the time stays the same, the results returned by methods such as hours() will change since they take the timezone into account. The timezone argument has to be of the ISO 8601 format.\n\n*Example* Hour of the day in San Francisco (UTC/GMT -8, without daylight saving time).\n\n r.now().in_timezone('-08:00').hours().run(conn)\n"),
+ (rethinkdb.iso8601, b'r.iso8601(iso8601Date[, default_timezone=\'\']) -> time\n\nCreate a time object based on an ISO 8601 date-time string (e.g. \'2013-01-01T01:01:01+00:00\'). We support all valid ISO 8601 formats except for week dates. If you pass an ISO 8601 date-time without a time zone, you must specify the time zone with the `default_timezone` argument. Read more about the ISO 8601 format at [Wikipedia](http://en.wikipedia.org/wiki/ISO_8601).\n\n*Example* Update the time of John\'s birth.\n\n r.table("user").get("John").update({"birth": r.iso8601(\'1986-11-03T08:30:00-07:00\')}).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.minutes, b'time.minutes() -> number\n\nReturn the minute in a time object as a number between 0 and 59.\n\n*Example* Return all the posts submitted during the first 10 minutes of every hour.\n\n r.table("posts").filter(lambda post:\n post["date"].minutes() < 10\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.month, b'time.month() -> number\n\nReturn the month of a time object as a number between 1 and 12. For your convenience, the terms r.january, r.february etc. are defined and map to the appropriate integer.\n\n*Example* Retrieve all the users who were born in November.\n\n r.table("users").filter(\n r.row["birthdate"].month() == 11\n )\n\n*Example* Retrieve all the users who were born in November.\n\n r.table("users").filter(\n r.row["birthdate"].month() == r.november\n )\n\n'),
+ (rethinkdb.now, b'r.now() -> time\n\nReturn a time object representing the current time in UTC. The command now() is computed once when the server receives the query, so multiple instances of r.now() will always return the same time inside a query.\n\n*Example* Add a new user with the time at which he subscribed.\n\n r.table("users").insert({\n "name": "John",\n "subscription_date": r.now()\n }).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.seconds, b'time.seconds() -> number\n\nReturn the seconds in a time object as a number between 0 and 59.999 (double precision).\n\n*Example* Return the post submitted during the first 30 seconds of every minute.\n\n r.table("posts").filter(lambda post:\n post["date"].seconds() < 30\n ).run(conn)\n\n'),
+ (rethinkdb.time, b'r.time(year, month, day[, hour, minute, second], timezone)\n -> time\n\nCreate a time object for a specific time.\n\nA few restrictions exist on the arguments:\n\n- `year` is an integer between 1400 and 9,999.\n- `month` is an integer between 1 and 12.\n- `day` is an integer between 1 and 31.\n- `hour` is an integer.\n- `minutes` is an integer.\n- `seconds` is a double. Its value will be rounded to three decimal places\n(millisecond-precision).\n- `timezone` can be `\'Z\'` (for UTC) or a string with the format `\xc2\xb1[hh]:[mm]`.\n\n*Example* Update the birthdate of the user "John" to November 3rd, 1986 UTC.\n\n r.table("user").get("John").update({"birthdate": r.time(1986, 11, 3, \'Z\')}).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.time_of_day, b'time.time_of_day() -> number\n\nReturn the number of seconds elapsed since the beginning of the day stored in the time object.\n\n*Example* Retrieve posts that were submitted before noon.\n\n r.table("posts").filter(\n r.row["date"].time_of_day() <= 12*60*60\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.timezone, b'time.timezone() -> string\n\nReturn the timezone of the time object.\n\n*Example* Return all the users in the "-07:00" timezone.\n\n r.table("users").filter(lambda user:\n user["subscriptionDate"].timezone() == "-07:00"\n )\n\n'),
+ (rethinkdb.ast.RqlQuery.to_epoch_time, b'time.to_epoch_time() -> number\n\nConvert a time object to its epoch time.\n\n*Example* Return the current time in seconds since the Unix Epoch with millisecond-precision.\n\n r.now().to_epoch_time()\n\n'),
+ (rethinkdb.ast.RqlQuery.to_iso8601, b'time.to_iso8601() -> string\n\nConvert a time object to a string in ISO 8601 format.\n\n*Example* Return the current ISO 8601 time.\n\n > r.now().to_iso8601().run(conn)\n \n "2015-04-20T18:37:52.690+00:00"\n\n'),
+ (rethinkdb.ast.RqlQuery.year, b'time.year() -> number\n\nReturn the year of a time object.\n\n*Example* Retrieve all the users born in 1986.\n\n r.table("users").filter(lambda user:\n user["birthdate"].year() == 1986\n ).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.append, b"array.append(value) -> array\n\nAppend a value to an array.\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots.\n\n r.table('marvel').get('IronMan')['equipment'].append('newBoots').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.__getitem__, b"sequence[attr] -> sequence\nsingleSelection[attr] -> value\nobject[attr] -> value\narray[index] -> value\n\nGet a single field from an object. If called on a sequence, gets that field from every object in the sequence, skipping objects that lack it.\n\n*Example* What was Iron Man's first appearance in a comic?\n\n r.table('marvel').get('IronMan')['firstAppearance'].run(conn)\n\nThe `[]` command also accepts integer arguments as array offsets, like the [nth](http://rethinkdb.com/api/python/nth) command.\n\n*Example* Get the fourth element in a sequence. (The first element is position `0`, so the fourth element is position `3`.)\n\n r.expr([10, 20, 30, 40, 50])[3]\n \n 40\n"),
+ (rethinkdb.ast.RqlQuery.change_at, b'array.change_at(index, value) -> array\n\nChange a value in an array at a given index. Returns the modified array.\n\n*Example* Bruce Banner hulks out.\n\n r.expr(["Iron Man", "Bruce", "Spider-Man"]).change_at(1, "Hulk").run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.delete_at, b"array.delete_at(index [,endIndex]) -> array\n\nRemove one or more elements from an array at a given index. Returns the modified array. (Note: `delete_at` operates on arrays, not documents; to delete documents, see the [delete](http://rethinkdb.com/api/python/delete) command.)\n\nIf only `index` is specified, `delete_at` removes the element at that index. If both `index` and `end_index` are specified, `delete_at` removes the range of elements between `index` and `end_index`, inclusive of `index` but not inclusive of `end_index`.\n\nIf `end_index` is specified, it must not be less than `index`. Both `index` and `end_index` must be within the array's bounds (i.e., if the array has 10 elements, an `index` or `end_index` of 10 or higher is invalid).\n\nBy using a negative `index` you can delete from the end of the array. `-1` is the last element in the array, `-2` is the second-to-last element, and so on. You may specify a negative `end_index`, although just as with a positive value, this will not be inclusive. The range `(2,-1)` specifies the third element through the next-to-last element.\n\n*Example* Delete the second element of an array.\n\n > r.expr(['a','b','c','d','e','f']).delete_at(1).run(conn)\n \n ['a', 'c', 'd', 'e', 'f']\n\n*Example* Delete the second and third elements of an array.\n\n > r.expr(['a','b','c','d','e','f']).delete_at(1,3).run(conn)\n \n ['a', 'd', 'e', 'f']\n\n*Example* Delete the next-to-last element of an array.\n\n > r.expr(['a','b','c','d','e','f']).delete_at(-2).run(conn)\n \n ['a', 'b', 'c', 'd', 'f']\n\n*Example* Delete a comment on a post.\n\nGiven a post document such as:\n\n{\n id: '4cf47834-b6f9-438f-9dec-74087e84eb63',\n title: 'Post title',\n author: 'Bob',\n comments: [\n { author: 'Agatha', text: 'Comment 1' },\n { author: 'Fred', text: 'Comment 2' }\n ]\n}\n\nThe second comment can be deleted by using `update` and `delete_at` together.\n\n r.table('posts').get('4cf47834-b6f9-438f-9dec-74087e84eb63').update(\n lambda post: { 'comments': post['comments'].delete_at(1) }\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.difference, b"array.difference(array) -> array\n\nRemove the elements of one array from another array.\n\n*Example* Retrieve Iron Man's equipment list without boots.\n\n r.table('marvel').get('IronMan')['equipment'].difference(['Boots']).run(conn)\n\n*Example* Remove Iron Man's boots from his equipment.\n\n r.table('marvel').get('IronMan')[:equipment].update(lambda doc:\n {'equipment': doc['equipment'].difference(['Boots'])}\n ).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.get_field, b"sequence.get_field(attr) -> sequence\nsingleSelection.get_field(attr) -> value\nobject.get_field(attr) -> value\n\nGet a single field from an object. If called on a sequence, gets that field from every\nobject in the sequence, skipping objects that lack it.\n\n*Example* What was Iron Man's first appearance in a comic?\n\n r.table('marvel').get('IronMan').get_field('firstAppearance').run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.has_fields, b'sequence.has_fields([selector1, selector2...]) -> stream\narray.has_fields([selector1, selector2...]) -> array\nobject.has_fields([selector1, selector2...]) -> boolean\n\nTest if an object has one or more fields. An object has a field if it has that key and the key has a non-null value. For instance, the object `{\'a\': 1,\'b\': 2,\'c\': null}` has the fields `a` and `b`.\n\nWhen applied to a single object, `has_fields` returns `true` if the object has the fields and `false` if it does not. When applied to a sequence, it will return a new sequence (an array or stream) containing the elements that have the specified fields.\n\n*Example* Return the players who have won games.\n\n r.table(\'players\').has_fields(\'games_won\').run(conn)\n\n*Example* Return the players who have *not* won games. To do this, use `has_fields` with [not](http://rethinkdb.com/api/python/not), wrapped with [filter](http://rethinkdb.com/api/python/filter).\n\n r.table(\'players\').filter(~r.row.has_fields(\'games_won\')).run(conn)\n\n*Example* Test if a specific player has won any games.\n\n r.table(\'players\').get(\n \'b5ec9714-837e-400c-aa74-dbd35c9a7c4c\').has_fields(\'games_won\').run(conn)\n\n**Nested Fields**\n\n`has_fields` lets you test for nested fields in objects. If the value of a field is itself a set of key/value pairs, you can test for the presence of specific keys.\n\n*Example* In the `players` table, the `games_won` field contains one or more fields for kinds of games won:\n\n {\n \'games_won\': {\n \'playoffs\': 2,\n \'championships\': 1\n }\n }\n\nReturn players who have the "championships" field.\n\n r.table(\'players\').has_fields({\'games_won\': {\'championships\': true}}).run(conn)\n\nNote that `true` in the example above is testing for the existence of `championships` as a field, not testing to see if the value of the `championships` field is set to `true`. There\'s a more convenient shorthand form available. (See [pluck](http://rethinkdb.com/api/python/pluck) for more details on this.)\n\n r.table(\'players\').has_fields({\'games_won\': \'championships\'}).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.insert_at, b'array.insert_at(index, value) -> array\n\nInsert a value in to an array at a given index. Returns the modified array.\n\n*Example* Hulk decides to join the avengers.\n\n r.expr(["Iron Man", "Spider-Man"]).insert_at(1, "Hulk").run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.keys, b"singleSelection.keys() -> array\nobject.keys() -> array\n\nReturn an array containing all of the object's keys.\n\n*Example* Get all the keys of a row.\n\n r.table('marvel').get('ironman').keys().run(conn)\n\n"),
+ (rethinkdb.literal, b'r.literal(object) -> special\n\nReplace an object in a field instead of merging it with an existing object in a `merge` or `update` operation. = Using `literal` with no arguments in a `merge` or `update` operation will remove the corresponding field.\n\n*Example* Replace one nested document with another rather than merging the fields.\n\nAssume your users table has this structure:\n\n [\n {\n "id": 1,\n "name": "Alice",\n "data": {\n "age": 18,\n "city": "Dallas"\n }\n } \n ...\n ]\n\nUsing `update` to modify the `data` field will normally merge the nested documents:\n\n r.table(\'users\').get(1).update({ \'data\': { \'age\': 19, \'job\': \'Engineer\' } }).run(conn)\n \n {\n "id": 1,\n "name": "Alice",\n "data": {\n "age": 19,\n "city": "Dallas",\n "job": "Engineer"\n }\n } \n\nThat will preserve `city` and other existing fields. But to replace the entire `data` document with a new object, use `literal`:\n\n r.table(\'users\').get(1).update({ \'data\': r.literal({ \'age\': 19, \'job\': \'Engineer\' }) }).run(conn)\n \n {\n "id": 1,\n "name": "Alice",\n "data": {\n "age": 19,\n "job": "Engineer"\n }\n } \n\n*Example* Use `literal` to remove a field from a document.\n\n r.table(\'users\').get(1).merge({ "data": r.literal() }).run(conn)\n \n {\n "id": 1,\n "name": "Alice"\n }\n'),
+ (rethinkdb.ast.RqlQuery.merge, b'singleSelection.merge(object|function[, object|function, ...]) -> object\nobject.merge(object|function[, object|function, ...]) -> object\nsequence.merge(object|function[, object|function, ...]) -> stream\narray.merge(object|function[, object|function, ...]) -> array\n\nMerge two or more objects together to construct a new object with properties from all. When there is a conflict between field names, preference is given to fields in the rightmost object in the argument list `merge` also accepts a subquery function that returns an object, which will be used similarly to a [map](http://rethinkdb.com/api/python/map/) function.\n\n*Example* Equip Thor for battle.\n\n r.table(\'marvel\').get(\'thor\').merge(\n r.table(\'equipment\').get(\'hammer\'),\n r.table(\'equipment\').get(\'pimento_sandwich\')\n ).run(conn)\n\n*Example* Equip every hero for battle, using a subquery function to retrieve their weapons.\n\n r.table(\'marvel\').merge(lambda hero:\n { \'weapons\': r.table(\'weapons\').get(hero[\'weapon_id\']) }\n ).run(conn)\n\n*Example* Use `merge` to join each blog post with its comments.\n\nNote that the sequence being merged&mdash;in this example, the comments&mdash;must be coerced from a selection to an array. Without `coerce_to` the operation will throw an error ("Expected type DATUM but found SELECTION").\n\n r.table(\'posts\').merge(lambda post:\n { \'comments\': r.table(\'comments\').get_all(post[\'id\'],\n index=\'post_id\').coerce_to(\'array\') }\n ).run(conn)\n\n*Example* Merge can be used recursively to modify object within objects.\n\n r.expr({\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10, \'cooldown\' : 20}}}).merge(\n {\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10}}}\n ).run(conn)\n\n*Example* To replace a nested object with another object you can use the literal keyword.\n\n r.expr({\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10, \'cooldown\' : 20}}}).merge(\n {\'weapons\' : r.literal({\'repulsor rays\' : {\'dmg\' : 3, \'cooldown\' : 0}})}\n ).run(conn)\n\n*Example* Literal can be used to remove keys from an object as well.\n\n r.expr({\'weapons\' : {\'spectacular graviton beam\' : {\'dmg\' : 10, \'cooldown\' : 20}}}).merge(\n {\'weapons\' : {\'spectacular graviton beam\' : r.literal()}}\n ).run(conn)\n\n'),
+ (rethinkdb.object, b'r.object([key, value,]...) -> object\n\nCreates an object from a list of key-value pairs, where the keys must\nbe strings. `r.object(A, B, C, D)` is equivalent to\n`r.expr([[A, B], [C, D]]).coerce_to(\'OBJECT\')`.\n\n*Example* Create a simple object.\n\n > r.object(\'id\', 5, \'data\', [\'foo\', \'bar\']).run(conn)\n {\'data\': ["foo", "bar"], \'id\': 5}\n'),
+ (rethinkdb.ast.RqlQuery.pluck, b"sequence.pluck([selector1, selector2...]) -> stream\narray.pluck([selector1, selector2...]) -> array\nobject.pluck([selector1, selector2...]) -> object\nsingleSelection.pluck([selector1, selector2...]) -> object\n\nPlucks out one or more attributes from either an object or a sequence of objects\n(projection).\n\n*Example* We just need information about IronMan's reactor and not the rest of the\ndocument.\n\n r.table('marvel').get('IronMan').pluck('reactorState', 'reactorPower').run(conn)\n\n*Example* For the hero beauty contest we only care about certain qualities.\n\n r.table('marvel').pluck('beauty', 'muscleTone', 'charm').run(conn)\n\n*Example* Pluck can also be used on nested objects.\n\n r.table('marvel').pluck({'abilities' : {'damage' : True, 'mana_cost' : True}, 'weapons' : True}).run(conn)\n\n*Example* The nested syntax can quickly become overly verbose so there's a shorthand\nfor it.\n\n r.table('marvel').pluck({'abilities' : ['damage', 'mana_cost']}, 'weapons').run(conn)\n\nFor more information read the [nested field documentation](http://rethinkdb.com/docs/nested-fields/).\n"),
+ (rethinkdb.ast.RqlQuery.prepend, b"array.prepend(value) -> array\n\nPrepend a value to an array.\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots.\n\n r.table('marvel').get('IronMan')['equipment'].prepend('newBoots').run(conn)\n"),
+ (rethinkdb.row, b"r.row -> value\n\nReturns the currently visited document. Note that `row` does not work within subqueries to access nested documents; you should use anonymous functions to access those documents instead. (See the last example.)\n\n*Example* Get all users whose age is greater than 5.\n\n r.table('users').filter(r.row['age'] > 5).run(conn)\n\n*Example* Access the attribute 'child' of an embedded document.\n\n r.table('users').filter(r.row['embedded_doc']['child'] > 5).run(conn)\n\n*Example* Add 1 to every element of an array.\n\n r.expr([1, 2, 3]).map(r.row + 1).run(conn)\n\n*Example* For nested queries, use functions instead of `row`.\n\n r.table('users').filter(\n lambda doc: doc['name'] == r.table('prizes').get('winner')\n ).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.set_difference, b"array.set_difference(array) -> array\n\nRemove the elements of one array from another and return them as a set (an array with\ndistinct values).\n\n*Example* Check which pieces of equipment Iron Man has, excluding a fixed list.\n\n r.table('marvel').get('IronMan')['equipment'].set_difference(['newBoots', 'arc_reactor']).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.set_insert, b"array.set_insert(value) -> array\n\nAdd a value to an array and return it as a set (an array with distinct values).\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots.\n\n r.table('marvel').get('IronMan')['equipment'].set_insert('newBoots').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.set_intersection, b"array.set_intersection(array) -> array\n\nIntersect two arrays returning values that occur in both of them as a set (an array with\ndistinct values).\n\n*Example* Check which pieces of equipment Iron Man has from a fixed list.\n\n r.table('marvel').get('IronMan')['equipment'].set_intersection(['newBoots', 'arc_reactor']).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.set_union, b"array.set_union(array) -> array\n\nAdd a several values to an array and return it as a set (an array with distinct values).\n\n*Example* Retrieve Iron Man's equipment list with the addition of some new boots and an arc reactor.\n\n r.table('marvel').get('IronMan')['equipment'].set_union(['newBoots', 'arc_reactor']).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.splice_at, b'array.splice_at(index, array) -> array\n\nInsert several values in to an array at a given index. Returns the modified array.\n\n*Example* Hulk and Thor decide to join the avengers.\n\n r.expr(["Iron Man", "Spider-Man"]).splice_at(1, ["Hulk", "Thor"]).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.without, b"sequence.without([selector1, selector2...]) -> stream\narray.without([selector1, selector2...]) -> array\nsingleSelection.without([selector1, selector2...]) -> object\nobject.without([selector1, selector2...]) -> object\n\nThe opposite of pluck; takes an object or a sequence of objects, and returns them with\nthe specified paths removed.\n\n*Example* Since we don't need it for this computation we'll save bandwidth and leave\nout the list of IronMan's romantic conquests.\n\n r.table('marvel').get('IronMan').without('personalVictoriesList').run(conn)\n\n*Example* Without their prized weapons, our enemies will quickly be vanquished.\n\n r.table('enemies').without('weapons').run(conn)\n\n*Example* Nested objects can be used to remove the damage subfield from the weapons and abilities fields.\n\n r.table('marvel').without({'weapons' : {'damage' : True}, 'abilities' : {'damage' : True}}).run(conn)\n\n*Example* The nested syntax can quickly become overly verbose so there's a shorthand for it.\n\n r.table('marvel').without({'weapons' : 'damage', 'abilities' : 'damage'}).run(conn)\n\n"),
+ (rethinkdb.circle, b"r.circle([longitude, latitude], radius[, num_vertices=32, geo_system='WGS84', unit='m', fill=True]) -> geometry\nr.circle(point, radius[, {num_vertices=32, geo_system='WGS84', unit='m', fill=True]) -> geometry\n\nConstruct a circular line or polygon. A circle in RethinkDB is a polygon or line *approximating* a circle of a given radius around a given center, consisting of a specified number of vertices (default 32).\n\nThe center may be specified either by two floating point numbers, the latitude (&minus;90 to 90) and longitude (&minus;180 to 180) of the point on a perfect sphere (see [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system), or by a point object. The radius is a floating point number whose units are meters by default, although that may be changed with the `unit` argument.\n\nOptional arguments available with `circle` are:\n\n* `num_vertices`: the number of vertices in the polygon or line. Defaults to 32.\n* `geo_system`: the reference ellipsoid to use for geographic coordinates. Possible values are `WGS84` (the default), a common standard for Earth's geometry, or `unit_sphere`, a perfect sphere of 1 meter radius.\n* `unit`: Unit for the radius distance. Possible values are `m` (meter, the default), `km` (kilometer), `mi` (international mile), `nm` (nautical mile), `ft` (international foot).\n* `fill`: if `True` (the default) the circle is filled, creating a polygon; if `False` the circle is unfilled (creating a line).\n\n*Example* Define a circle.\n\n r.table('geo').insert({\n 'id': 300,\n 'name': 'Hayes Valley',\n 'neighborhood': r.circle([-122.423246,37.779388], 1000)\n }).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.distance, b"geometry.distance(geometry[, geo_system='WGS84', unit='m']) -> number\n\nCompute the distance between a point and another geometry object. At least one of the geometry objects specified must be a point.\n\nOptional arguments available with `distance` are:\n\n* `geo_system`: the reference ellipsoid to use for geographic coordinates. Possible values are `WGS84` (the default), a common standard for Earth's geometry, or `unit_sphere`, a perfect sphere of 1 meter radius.\n* `unit`: Unit to return the distance in. Possible values are `m` (meter, the default), `km` (kilometer), `mi` (international mile), `nm` (nautical mile), `ft` (international foot).\n\nIf one of the objects is a polygon or a line, the point will be projected onto the line or polygon assuming a perfect sphere model before the distance is computed (using the model specified with `geo_system`). As a consequence, if the polygon or line is extremely large compared to Earth's radius and the distance is being computed with the default WGS84 model, the results of `distance` should be considered approximate due to the deviation between the ellipsoid and spherical models.\n\n*Example* Compute the distance between two points on the Earth in kilometers.\n\n > point1 = r.point(-122.423246,37.779388)\n > point2 = r.point(-117.220406,32.719464)\n > r.distance(point1, point2, unit='km').run(conn)\n \n 734.1252496021841\n"),
+ (rethinkdb.ast.RqlQuery.fill, b"line.fill() -> polygon\n\nConvert a Line object into a Polygon object. If the last point does not specify the same coordinates as the first point, `polygon` will close the polygon by connecting them.\n\nLongitude (&minus;180 to 180) and latitude (&minus;90 to 90) of vertices are plotted on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\nIf the last point does not specify the same coordinates as the first point, `polygon` will close the polygon by connecting them. You cannot directly construct a polygon with holes in it using `polygon`, but you can use [polygon_sub](http://rethinkdb.com/api/python/polygon_sub) to use a second polygon within the interior of the first to define a hole.\n\n*Example* Create a line object and then convert it to a polygon.\n\n r.table('geo').insert({\n 'id': 201,\n 'rectangle': r.line(\n [-122.423246,37.779388],\n [-122.423246,37.329898],\n [-121.886420,37.329898],\n [-121.886420,37.779388]\n )\n }).run(conn)\n \n r.table('geo').get(201).update({\n 'rectangle': r.row('rectangle').fill()\n }).run(conn)\n"),
+ (rethinkdb.geojson, b"r.geojson(geojson) -> geometry\n\nConvert a [GeoJSON][] object to a ReQL geometry object.\n\n[GeoJSON]: http://geojson.org\n\nRethinkDB only allows conversion of GeoJSON objects which have ReQL equivalents: Point, LineString, and Polygon. MultiPoint, MultiLineString, and MultiPolygon are not supported. (You could, however, store multiple points, lines and polygons in an array and use a geospatial multi index with them.)\n\nOnly longitude/latitude coordinates are supported. GeoJSON objects that use Cartesian coordinates, specify an altitude, or specify their own coordinate reference system will be rejected.\n\n*Example* Convert a GeoJSON object to a ReQL geometry object.\n\n geo_json = {\n 'type': 'Point',\n 'coordinates': [ -122.423246, 37.779388 ]\n }\n r.table('geo').insert({\n 'id': 'sfo',\n 'name': 'San Francisco',\n 'location': r.geojson(geo_json)\n }).run(conn)\n"),
+ (rethinkdb.ast.Table.get_intersecting, b"table.get_intersecting(geometry, index='indexname') -> selection<stream>\n\nGet all documents where the given geometry object intersects the geometry object of the requested geospatial index.\n\nThe `index` argument is mandatory. This command returns the same results as `table.filter(r.row('index').intersects(geometry))`. The total number of results is limited to the array size limit which defaults to 100,000, but can be changed with the `array_limit` option to [run](http://rethinkdb.com/api/python/run).\n\n*Example* Which of the locations in a list of parks intersect `circle1`?\n\n circle1 = r.circle([-117.220406,32.719464], 10, unit='mi')\n r.table('parks').get_intersecting(circle1, index='area').run(conn)\n"),
+ (rethinkdb.ast.Table.get_nearest, b"table.get_nearest(point, index='indexname'[, max_results=100, max_dist=100000, unit='m', geo_system='WGS84']) -> array\n\nGet all documents where the specified geospatial index is within a certain distance of the specified point (default 100 kilometers).\n\nThe `index` argument is mandatory. Optional arguments are:\n\n* `max_results`: the maximum number of results to return (default 100).\n* `unit`: Unit for the distance. Possible values are `m` (meter, the default), `km` (kilometer), `mi` (international mile), `nm` (nautical mile), `ft` (international foot).\n* `max_dist`: the maximum distance from an object to the specified point (default 100 km).\n* `geo_system`: the reference ellipsoid to use for geographic coordinates. Possible values are `WGS84` (the default), a common standard for Earth's geometry, or `unit_sphere`, a perfect sphere of 1 meter radius.\n\nThe return value will be an array of two-item objects with the keys `dist` and `doc`, set to the distance between the specified point and the document (in the units specified with `unit`, defaulting to meters) and the document itself, respectively.\n\n*Example* Return a list of enemy hideouts within 5000 meters of the secret base.\n\n secret_base = r.point(-122.422876,37.777128)\n r.table('hideouts').get_nearest(secret_base, index='location',\n max_dist=5000).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.includes, b"sequence.includes(geometry) -> sequence\ngeometry.includes(geometry) -> bool\n\nTests whether a geometry object is completely contained within another. When applied to a sequence of geometry objects, `includes` acts as a [filter](http://rethinkdb.com/api/python/filter), returning a sequence of objects from the sequence that include the argument.\n\n*Example* Is `point2` included within a 2000-meter circle around `point1`?\n\n > point1 = r.point(-117.220406,32.719464)\n > point2 = r.point(-117.206201,32.725186)\n > r.circle(point1, 2000).includes(point2).run(conn)\n \n True\n\n*Example* Which of the locations in a list of parks include `circle1`?\n\n circle1 = r.circle([-117.220406,32.719464], 10, unit='mi')\n r.table('parks')['area'].includes(circle1).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.intersects, b"sequence.intersects(geometry) -> sequence\ngeometry.intersects(geometry) -> bool\n\nTests whether two geometry objects intersect with one another. When applied to a sequence of geometry objects, `intersects` acts as a [filter](http://rethinkdb.com/api/python/filter), returning a sequence of objects from the sequence that intersect with the argument.\n\n*Example* Is `point2` within a 2000-meter circle around `point1`?\n\n > point1 = r.point(-117.220406,32.719464)\n > point2 = r.point(-117.206201,32.725186)\n > r.circle(point1, 2000).intersects(point2).run(conn)\n \n True\n\n*Example* Which of the locations in a list of parks intersect `circle1`?\n\n circle1 = r.circle([-117.220406,32.719464], 10, unit='mi')\n r.table('parks')('area').intersects(circle1).run(conn)\n"),
+ (rethinkdb.line, b"r.line([lon1, lat1], [lon2, lat2], ...) -> line\nr.line(point1, point2, ...) -> line\n\nConstruct a geometry object of type Line. The line can be specified in one of two ways:\n\n* Two or more two-item arrays, specifying latitude and longitude numbers of the line's vertices;\n* Two or more [Point](http://rethinkdb.com/api/python/point) objects specifying the line's vertices.\n\nLongitude (&minus;180 to 180) and latitude (&minus;90 to 90) of vertices are plotted on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\n*Example* Define a line.\n\n r.table('geo').insert({\n 'id': 101,\n 'route': r.line([-122.423246,37.779388], [-121.886420,37.329898])\n }).run(conn)\n"),
+ (rethinkdb.point, b"r.point(longitude, latitude) -> point\n\nConstruct a geometry object of type Point. The point is specified by two floating point numbers, the longitude (&minus;180 to 180) and latitude (&minus;90 to 90) of the point on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\n*Example* Define a point.\n\n r.table('geo').insert({\n 'id': 1,\n 'name': 'San Francisco',\n 'location': r.point(-122.423246,37.779388)\n }).run(conn)\n"),
+ (rethinkdb.polygon, b"r.polygon([lon1, lat1], [lon2, lat2], ...) -> polygon\nr.polygon(point1, point2, ...) -> polygon\n\nConstruct a geometry object of type Polygon. The Polygon can be specified in one of two ways:\n\n* Three or more two-item arrays, specifying latitude and longitude numbers of the polygon's vertices;\n* Three or more [Point](http://rethinkdb.com/api/python/point) objects specifying the polygon's vertices.\n\nLongitude (&minus;180 to 180) and latitude (&minus;90 to 90) of vertices are plotted on a perfect sphere. See [Geospatial support](http://rethinkdb.com/docs/geo-support/) for more information on ReQL's coordinate system.\n\nIf the last point does not specify the same coordinates as the first point, `polygon` will close the polygon by connecting them. You cannot directly construct a polygon with holes in it using `polygon`, but you can use [polygon_sub](http://rethinkdb.com/api/python/polygon_sub) to use a second polygon within the interior of the first to define a hole.\n\n*Example* Define a polygon.\n\n r.table('geo').insert({\n 'id': 101,\n 'rectangle': r.polygon(\n [-122.423246,37.779388],\n [-122.423246,37.329898],\n [-121.886420,37.329898],\n [-121.886420,37.779388]\n )\n }).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.polygon_sub, b'polygon1.polygon_sub(polygon2) -> polygon\n\nUse `polygon2` to "punch out" a hole in `polygon1`. `polygon2` must be completely contained within `polygon1` and must have no holes itself (it must not be the output of `polygon_sub` itself).\n\n*Example* Define a polygon with a hole punched in it.\n\n outer_polygon = r.polygon(\n [-122.4,37.7],\n [-122.4,37.3],\n [-121.8,37.3],\n [-121.8,37.7]\n )\n inner_polygon = r.polygon(\n [-122.3,37.4],\n [-122.3,37.6],\n [-122.0,37.6],\n [-122.0,37.4]\n )\n outer_polygon.polygon_sub(inner_polygon).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.to_geojson, b"geometry.to_geojson() -> object\n\nConvert a ReQL geometry object to a [GeoJSON][] object.\n\n[GeoJSON]: http://geojson.org\n\n*Example* Convert a ReQL geometry object to a GeoJSON object.\n\n > r.table(geo).get('sfo')['location'].to_geojson().run(conn)\n \n {\n 'type': 'Point',\n 'coordinates': [ -122.423246, 37.779388 ]\n }\n"),
+ (rethinkdb.ast.RqlQuery.eq_join, b'sequence.eq_join(left_field, right_table[, index=\'id\']) -> sequence\n\nJoin tables using a field on the left-hand sequence matching primary keys or secondary indexes on the right-hand table. `eq_join` is more efficient than other Re_qL join types, and operates much faster. Documents in the result set consist of pairs of left-hand and right-hand documents, matched when the field on the left-hand side exists and is non-null and an entry with that field\'s value exists in the specified index on the right-hand side.\n\nThe result set of `eq_join` is a stream or array of objects. Each object in the returned set will be an object of the form `{ left: <left-document>, right: <right-document> }`, where the values of `left` and `right` will be the joined documents. Use the <code><a href="/api/python/zip/">zip</a></code> command to merge the `left` and `right` fields together.\n\n**Example:** Match players with the games they\'ve played against one another.\n\nThe players table contains these documents:\n\n [\n { \'id\': 1, \'player\': \'George\', \'gameId\': 1 },\n { \'id\': 2, \'player\': \'Agatha\', \'gameId\': 3 },\n { \'id\': 3, \'player\': \'Fred\', \'gameId\': 2 },\n { \'id\': 4, \'player\': \'Marie\', \'gameId\': 2 },\n { \'id\': 5, \'player\': \'Earnest\', \'gameId\': 1 },\n { \'id\': 6, \'player\': \'Beth\', \'gameId\': 3 }\n ]\n\nThe games table contains these documents:\n\n [\n { \'id\': 1, \'field\': \'Little Delving\' },\n { \'id\': 2, \'field\': \'Rushock Bog\' },\n { \'id\': 3, \'field\': \'Bucklebury\' }\n ]\n\nJoin these tables using `game_id` on the player table and `id` on the games table:\n\n r.table(\'players\').eq_join(\'game_id\', r.table(\'games\')).run(conn)\n\nThis will return a result set such as the following:\n\n [\n {\n "left" : { "gameId" : 3, "id" : 2, "player" : "Agatha" },\n "right" : { "id" : 3, "field" : "Bucklebury" }\n },\n {\n "left" : { "gameId" : 2, "id" : 3, "player" : "Fred" },\n "right" : { "id" : 2, "field" : "Rushock Bog" }\n },\n ...\n ]\n\nWhat you likely want is the result of using `zip` with that. For clarity, we\'ll use `without` to drop the `id` field from the games table (it conflicts with the `id` field for the players and it\'s redundant anyway), and we\'ll order it by the games.\n\n r.table(\'players\').eq_join(\'game_id\', r.table(\'games\')).without({\'right\': "id"}).zip().order_by(\'game_id\').run(conn)\n \n [\n { "field": "Little Delving", "gameId": 1, "id": 5, "player": "Earnest" },\n { "field": "Little Delving", "gameId": 1, "id": 1, "player": "George" },\n { "field": "Rushock Bog", "gameId": 2, "id": 3, "player": "Fred" },\n { "field": "Rushock Bog", "gameId": 2, "id": 4, "player": "Marie" },\n { "field": "Bucklebury", "gameId": 3, "id": 6, "player": "Beth" },\n { "field": "Bucklebury", "gameId": 3, "id": 2, "player": "Agatha" }\n ]\n\nFor more information, see [Table joins in Rethink_dB](http://rethinkdb.com/docs/table-joins/).\n\n**Example:** Use a secondary index on the right table rather than the primary key. If players have a secondary index on their cities, we can get a list of arenas with players in the same area.\n\n r.table(\'arenas\').eq_join(\'city_id\', r.table(\'arenas\'), index=\'city_id\').run(conn)\n\n**Example:** Use a nested key as the join field. Suppose the documents in the players table were structured like this:\n\n { \'id\': 1, \'player\': \'George\', \'game\': {\'id\': 1} },\n { \'id\': 2, \'player\': \'Agatha\', \'game\': {\'id\': 3} },\n ...\n\nSimply specify the field using the `row` command instead of a string.\n\n r.table(\'players\').eq_join(r.row[\'game\'][\'id\'], r.table(\'games\')).without({\'right\': \'id\'}).zip().run(conn)\n \n [\n { "field": "Little Delving", "game": { "id": 1 }, "id": 5, "player": "Earnest" },\n { "field": "Little Delving", "game": { "id": 1 }, "id": 1, "player": "George" },\n ...\n ]\n\n**Example:** Use a function instead of a field to join on a more complicated expression. Suppose the players have lists of favorite games ranked in order in a field such as `"favorites": [3, 2, 1]`. Get a list of players and their top favorite:\n\n r.table(\'players3\').eq_join(\n lambda player: player[\'favorites\'].nth(0),\n r.table(\'games\')\n ).without([{\'left\': [\'favorites\', \'game_id\', \'id\']}, {\'right\': \'id\'}]).zip()\n\nResult:\n\n [\n \t{ "field": "Rushock Bog", "name": "Fred" },\n \t{ "field": "Little Delving", "name": "George" },\n \t...\n ]\n'),
+ (rethinkdb.ast.RqlQuery.inner_join, b"sequence.inner_join(other_sequence, predicate) -> stream\narray.inner_join(other_sequence, predicate) -> array\n\nReturns an inner join of two sequences. The returned sequence represents an intersection of the left-hand sequence and the right-hand sequence: each row of the left-hand sequence will be compared with each row of the right-hand sequence to find all pairs of rows which satisfy the predicate. Each matched pair of rows of both sequences are combined into a result row. In most cases, you will want to follow the join with [zip](http://rethinkdb.com/api/python/zip) to combine the left and right results.\n\nNote that `inner_join` is slower and much less efficient than using [eq_join](http://rethinkdb.com/api/python/eq_join/) or [concat_map](http://rethinkdb.com/api/python/concat_map/) with [get_all](http://rethinkdb.com/api/python/get_all/). You should avoid using `inner_join` in commands when possible.\n\n*Example* Return a list of all matchups between Marvel and DC heroes in which the DC hero could beat the Marvel hero in a fight.\n\n r.table('marvel').inner_join(r.table('dc'),\n lambda marvel_row, dc_row: marvel_row['strength'] < dc_row['strength']\n ).zip().run(conn)\n\n(Compare this to an [outer_join](http://rethinkdb.com/api/python/outer_join) with the same inputs and predicate, which would return a list of *all* Marvel heroes along with any DC heroes with a higher strength.)"),
+ (rethinkdb.ast.RqlQuery.outer_join, b"sequence.outer_join(other_sequence, predicate) -> stream\narray.outer_join(other_sequence, predicate) -> array\n\nReturns a left outer join of two sequences. The returned sequence represents a union of the left-hand sequence and the right-hand sequence: all documents in the left-hand sequence will be returned, each matched with a document in the right-hand sequence if one satisfies the predicate condition. In most cases, you will want to follow the join with [zip](http://rethinkdb.com/api/python/zip) to combine the left and right results.\n\nNote that `outer_join` is slower and much less efficient than using [concat_map](http://rethinkdb.com/api/python/concat_map/) with [get_all](http://rethinkdb.com/api/python/get_all). You should avoid using `outer_join` in commands when possible.\n\n*Example* Return a list of all Marvel heroes, paired with any DC heroes who could beat them in a fight.\n\n r.table('marvel').outer_join(r.table('dc'),\n lambda marvel_row, dc_row: marvel_row['strength'] < dc_row['strength']\n ).zip().run(conn)\n\n(Compare this to an [inner_join](http://rethinkdb.com/api/python/inner_join) with the same inputs and predicate, which would return a list only of the matchups in which the DC hero has the higher strength.)\n"),
+ (rethinkdb.ast.RqlQuery.zip, b"stream.zip() -> stream\narray.zip() -> array\n\nUsed to 'zip' up the result of a join by merging the 'right' fields into 'left' fields of each member of the sequence.\n\n*Example* 'zips up' the sequence by merging the left and right fields produced by a join.\n\n r.table('marvel').eq_join('main_dc_collaborator', r.table('dc')).zip().run(conn)\n"),
+ (rethinkdb.db_create, b'r.db_create(db_name) -> object\n\nCreate a database. A RethinkDB database is a collection of tables, similar to\nrelational databases.\n\nIf successful, the command returns an object with two fields:\n\n* `dbs_created`: always `1`.\n* `config_changes`: a list containing one object with two fields, `old_val` and `new_val`:\n * `old_val`: always `None`.\n * `new_val`: the database\'s new [config](http://rethinkdb.com/api/python/config) value.\n\nIf a database with the same name already exists, the command throws `RqlRuntimeError`.\n\nNote: Only alphanumeric characters and underscores are valid for the database name.\n\n*Example* Create a database named \'superheroes\'.\n\n r.db_create(\'superheroes\').run(conn)\n \n {\n "config_changes": [\n {\n "new_val": {\n "id": "e4689cfc-e903-4532-a0e6-2d6797a43f07",\n "name": "superheroes"\n },\n "old_val": None\n }\n ],\n "dbs_created": 1\n }\n\n'),
+ (rethinkdb.db_drop, b'r.db_drop(db_name) -> object\n\nDrop a database. The database, all its tables, and corresponding data will be deleted.\n\nIf successful, the command returns an object with two fields:\n\n* `dbs_dropped`: always `1`.\n* `tables_dropped`: the number of tables in the dropped database.\n* `config_changes`: a list containing one two-field object, `old_val` and `new_val`:\n * `old_val`: the database\'s original [config](http://rethinkdb.com/api/python/config) value.\n * `new_val`: always `None`.\n\nIf the given database does not exist, the command throws `RqlRuntimeError`.\n\n*Example* Drop a database named \'superheroes\'.\n\n r.db_drop(\'superheroes\').run(conn)\n \n {\n "config_changes": [\n {\n "old_val": {\n "id": "e4689cfc-e903-4532-a0e6-2d6797a43f07",\n "name": "superheroes"\n },\n "new_val": None\n }\n ],\n "tables_dropped": 3,\n "dbs_dropped": 1\n }\n\n'),
+ (rethinkdb.db_list, b'r.db_list() -> array\n\nList all database names in the system. The result is a list of strings.\n\n*Example* List all databases.\n\n r.db_list().run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.changes, b'stream.changes(squash=True, include_states=False) -> stream\nsingleSelection.changes(squash=True, include_states=False) -> stream\n\nReturn an infinite stream of objects representing changes to a query.\n\nThe `squash` optional argument controls how `changes` batches change notifications:\n\n* `True`: When multiple changes to the same document occur before a batch of notifications is sent, the changes are "squashed" into one change. The client receives a notification that will bring it fully up to date with the server. This is the default.\n* `False`: All changes will be sent to the client verbatim.\n* `n`: A numeric value (floating point). Similar to `True`, but the server will wait `n` seconds to respond in order to squash as many changes together as possible, reducing network traffic.\n\nIf the `include_states` optional argument is `True`, the changefeed stream will include special status documents consisting of the field `state` and a string indicating a change in the feed\'s state. These documents can occur at any point in the feed between the notification documents described below. There are currently two states:\n\n* `{"state": "initializing"}` indicates the following documents represent initial values on the feed rather than changes. This will be the first document of a feed that returns initial values.\n* `{"state": "ready"}` indicates the following documents represent changes. This will be the first document of a feed that does *not* return initial values; otherwise, it will indicate the initial values have all been sent.\n\nPoint changefeeds will always return initial values and have an `initializing` state; feeds that return changes on unfiltered tables will never return initial values. Feeds that return changes on more complex queries may or may not return return initial values, depending on the kind of aggregation. Read the article on [Changefeeds in RethinkDB][cfr] for a more detailed discussion. If `include_states` is `True` on a changefeed that does not return initial values, the first document on the feed will be `{"state": "ready"}`.\n\n[cfr]: /docs/changefeeds/python/\n\nIf `include_states` is `False` (the default), the status documents will not be sent on the feed.\n\nIf the table becomes unavailable, the changefeed will be disconnected, and a runtime exception will be thrown by the driver.\n\nChangefeed notifications take the form of a two-field object:\n\n {\n "old_val": <document before change>,\n "new_val": <document after change>\n }\n\nThe first notification object in the changefeed stream will contain the query\'s initial value in `new_val` and have no `old_val` field. When a document is deleted, `new_val` will be `None`; when a document is inserted, `old_val` will be `None`.\n\nCertain document transformation commands can be chained before changefeeds. For more information, read the [discussion of changefeeds][cfr] in the "Query language" documentation.\n\nThe server will buffer up to 100,000 elements. If the buffer limit is hit, early changes will be discarded, and the client will receive an object of the form `{"error": "Changefeed cache over array size limit, skipped X elements."}` where `X` is the number of elements skipped.\n\nCommands that operate on streams (such as `filter` or `map`) can usually be chained after `changes`. However, since the stream produced by `changes` has no ending, commands that need to consume the entire stream before returning (such as `reduce` or `count`) cannot.\n\nIt\'s a good idea to open changefeeds on their own connection. If you don\'t, other queries run on the same connection will experience unpredictable latency spikes while the connection blocks on more changes.\n\n*Example* Subscribe to the changes on a table.\n\nStart monitoring the changefeed in one client:\n\n for change in r.table(\'games\').changes().run(conn):\n print change\n\nAs these queries are performed in a second client, the first client would receive and print the following objects:\n\n > r.table(\'games\').insert({\'id\': 1}).run(conn)\n {\'old_val\': None, \'new_val\': {\'id\': 1}}\n \n > r.table(\'games\').get(1).update({\'player1\': \'Bob\'}).run(conn)\n {\'old_val\': {\'id\': 1}, \'new_val\': {\'id\': 1, \'player1\': \'Bob\'}}\n \n > r.table(\'games\').get(1).replace({\'id\': 1, \'player1\': \'Bob\', \'player2\': \'Alice\'}).run(conn)\n {\'old_val\': {\'id\': 1, \'player1\': \'Bob\'},\n \'new_val\': {\'id\': 1, \'player1\': \'Bob\', \'player2\': \'Alice\'}}\n \n > r.table(\'games\').get(1).delete().run(conn)\n {\'old_val\': {\'id\': 1, \'player1\': \'Bob\', \'player2\': \'Alice\'}, \'new_val\': None}\n \n > r.table_drop(\'games\').run(conn)\n RqlRuntimeError: Changefeed aborted (table unavailable)\n\n*Example* Return all the changes that increase a player\'s score.\n\n r.table(\'test\').changes().filter(\n r.row[\'new_val\'][\'score\'] > r.row[\'old_val\'][\'score\']\n ).run(conn)\n\n*Example* Return all the changes to Bob\'s score.\n\n # Note that this will have to look at and discard all the changes to\n # rows besides Bob\'s. This is currently no way to filter with an index\n # on changefeeds.\n r.table(\'test\').changes().filter(r.row[\'new_val\'][\'name\'].eq(\'Bob\')).run(conn)\n\n*Example* Return all the inserts on a table.\n\n r.table(\'test\').changes().filter(r.row[\'old_val\'].eq(None)).run(conn)\n\n*Example* Return all the changes to game 1, with state notifications.\n\n r.table(\'games\').get(1).changes(include_states=True).run(conn)\n \n # result returned on changefeed\n {"state": "initializing"}\n {"new_val": {"id": 1, "score": 12, "arena": "Hobbiton Field"}}\n {"state": "ready"}\n {\n \t"old_val": {"id": 1, "score": 12, "arena": "Hobbiton Field"},\n \t"new_val": {"id": 1, "score": 14, "arena": "Hobbiton Field"}\n }\n {\n \t"old_val": {"id": 1, "score": 14, "arena": "Hobbiton Field"},\n \t"new_val": {"id": 1, "score": 17, "arena": "Hobbiton Field", "winner": "Frodo"}\n }\n\n*Example* Return all the changes to the top 10 games. This assumes the presence of a `score` secondary index on the `games` table.\n\n r.table(\'games\').order_by(index=r.desc(\'score\')).limit(10).run(conn)\n'),
+ (rethinkdb.ast.Table.index_create, b'table.index_create(index_name[, index_function][, multi=False, geo=False]) -> object\n\nCreate a new secondary index on a table. Secondary indexes improve the speed of many read queries at the slight cost of increased storage space and decreased write performance. For more information about secondary indexes, read the article "[Using secondary indexes in RethinkDB](http://rethinkdb.com/docs/secondary-indexes/)."\n\nRethinkDB supports different types of secondary indexes:\n\n- *Simple indexes* based on the value of a single field.\n- *Compound indexes* based on multiple fields.\n- *Multi indexes* based on arrays of values.\n- *Geospatial indexes* based on indexes of geometry objects, created when the `geo` optional argument is true.\n- Indexes based on *arbitrary expressions*.\n\nThe `index_function` can be an anonymous function or a binary representation obtained from the `function` field of [index_status](http://rethinkdb.com/api/python/index_status).\n\nIf successful, `create_index` will return an object of the form `{"created": 1}`. If an index by that name already exists on the table, a `RqlRuntimeError` will be thrown.\n\n*Example* Create a simple index based on the field `post_id`.\n\n r.table(\'comments\').index_create(\'post_id\').run(conn)\n*Example* Create a simple index based on the nested field `author > name`.\n\n r.table(\'comments\').index_create(\'author_name\', r.row["author"]["name"]).run(conn)\n\n*Example* Create a geospatial index based on the field `location`.\n\n r.table(\'places\').index_create(\'location\', geo=True).run(conn)\n\nA geospatial index field should contain only geometry objects. It will work with geometry ReQL terms ([get_intersecting](http://rethinkdb.com/api/python/get_intersecting/) and [get_nearest](http://rethinkdb.com/api/python/get_nearest/)) as well as index-specific terms ([index_status](http://rethinkdb.com/api/python/index_status), [index_wait](http://rethinkdb.com/api/python/index_wait), [index_drop](http://rethinkdb.com/api/python/index_drop) and [index_list](http://rethinkdb.com/api/python/index_list)). Using terms that rely on non-geometric ordering such as [get_all](http://rethinkdb.com/api/python/get_all/), [order_by](http://rethinkdb.com/api/python/order_by/) and [between](http://rethinkdb.com/api/python/order_by/) will result in an error.\n\n*Example* Create a compound index based on the fields `post_id` and `date`.\n\n r.table(\'comments\').index_create(\'post_and_date\', [r.row["post_id"], r.row["date"]]).run(conn)\n\n*Example* Create a multi index based on the field `authors`.\n\n r.table(\'posts\').index_create(\'authors\', multi=True).run(conn)\n\n*Example* Create a geospatial multi index based on the field `towers`.\n\n r.table(\'networks\').index_create(\'towers\', geo=True, multi=True).run(conn)\n\n*Example* Create an index based on an arbitrary expression.\n\n r.table(\'posts\').index_create(\'authors\', lambda doc:\n r.branch(\n doc.has_fields("updated_at"),\n doc["updated_at"],\n doc["created_at"]\n )\n ).run(conn)\n\n*Example* Create a new secondary index based on an existing one.\n\n index = r.table(\'posts\').index_status(\'authors\').nth(0)[\'function\'].run(conn)\n r.table(\'new_posts\').index_create(\'authors\', index).run(conn)\n\n*Example* Rebuild an outdated secondary index on a table.\n\n old_index = r.table(\'posts\').index_status(\'old_index\').nth(0)[\'function\'].run(conn)\n r.table(\'posts\').index_create(\'new_index\', old_index).run(conn)\n r.table(\'posts\').index_wait(\'new_index\').run(conn)\n r.table(\'posts\').index_rename(\'new_index\', \'old_index\', overwrite=True).run(conn)\n'),
+ (rethinkdb.ast.Table.index_drop, b"table.index_drop(index_name) -> object\n\nDelete a previously created secondary index of this table.\n\n*Example* Drop a secondary index named 'code_name'.\n\n r.table('dc').index_drop('code_name').run(conn)\n\n"),
+ (rethinkdb.ast.Table.index_list, b"table.index_list() -> array\n\nList all the secondary indexes of this table.\n\n*Example* List the available secondary indexes for this table.\n\n r.table('marvel').index_list().run(conn)\n"),
+ (rethinkdb.ast.Table.index_rename, b"table.index_rename(old_index_name, new_index_name[, overwrite=False]) -> object\n\nRename an existing secondary index on a table. If the optional argument `overwrite` is specified as `True`, a previously existing index with the new name will be deleted and the index will be renamed. If `overwrite` is `False` (the default) an error will be raised if the new index name already exists.\n\nThe return value on success will be an object of the format `{'renamed': 1}`, or `{'renamed': 0}` if the old and new names are the same.\n\nAn error will be raised if the old index name does not exist, if the new index name is already in use and `overwrite` is `False`, or if either the old or new index name are the same as the primary key field name.\n\n*Example* Rename an index on the comments table.\n\n r.table('comments').index_rename('post_id', 'message_id').run(conn)\n"),
+ (rethinkdb.ast.Table.index_status, b'table.index_status([, index...]) -> array\n\nGet the status of the specified indexes on this table, or the status\nof all indexes on this table if no indexes are specified.\n\nThe result is an array where for each index, there will be an object like this one:\n\n {\n "index": <index_name>,\n "ready": True,\n "function": <binary>,\n "multi": <bool>,\n "outdated": <bool>\n }\n\nor this one:\n\n {\n "index": <index_name>,\n "ready": False,\n "blocks_processed": <int>,\n "blocks_total": <int>,\n "function": <binary>,\n "multi": <bool>,\n "outdated": <bool>\n }\n\nThe `multi` field will be `true` or `false` depending on whether this index was created as a multi index (see [index_create](http://rethinkdb.com/api/python/index_create/) for details). The `outdated` field will be true if the index is outdated in the current version of RethinkDB and needs to be rebuilt.\n\nThe `function` field is a binary object containing an opaque representation of the secondary index (including the `multi` argument if specified). It can be passed as the second argument to [index_create](http://rethinkdb.com/api/python/index_create/) to create a new index with the same function; see `index_create` for more information.\n\n*Example* Get the status of all the indexes on `test`:\n\n r.table(\'test\').index_status().run(conn)\n\n*Example* Get the status of the `timestamp` index:\n\n r.table(\'test\').index_status(\'timestamp\').run(conn)\n\n*Example* Save the binary representation of the index:\n\n func = r.table(\'test\').index_status(\'timestamp\').nth(0)[\'function\'].run(conn)\n'),
+ (rethinkdb.ast.Table.index_wait, b'table.index_wait([, index...]) -> array\n\nWait for the specified indexes on this table to be ready, or for all\nindexes on this table to be ready if no indexes are specified.\n\nThe result is an array containing one object for each table index:\n\n {\n "index": <index_name>,\n "ready": True,\n "function": <binary>,\n "multi": <bool>,\n "geo": <bool>,\n "outdated": <bool>\n }\n\nSee the [index_status](http://rethinkdb.com/api/python/index_status) documentation for a description of the field values.\n\n*Example* Wait for all indexes on the table `test` to be ready:\n\n r.table(\'test\').index_wait().run(conn)\n\n*Example* Wait for the index `timestamp` to be ready:\n\n r.table(\'test\').index_wait(\'timestamp\').run(conn)\n'),
+ (rethinkdb.ast.DB.table_create, b'db.table_create(table_name[, options]) -> object\n\nCreate a table. A RethinkDB table is a collection of JSON documents.\n\nIf successful, the command returns an object with two fields:\n\n* `tables_created`: always `1`.\n* `config_changes`: a list containing one two-field object, `old_val` and `new_val`:\n * `old_val`: always `None`.\n * `new_val`: the table\'s new [config](http://rethinkdb.com/api/python/config) value.\n\nIf a table with the same name already exists, the command throws `RqlRuntimeError`.\n\nNote: Only alphanumeric characters and underscores are valid for the table name.\n\nWhen creating a table you can specify the following options:\n\n* `primary_key`: the name of the primary key. The default primary key is `id`.\n* `durability`: if set to `soft`, writes will be acknowledged by the server immediately and flushed to disk in the background. The default is `hard`: acknowledgment of writes happens after data has been written to disk.\n* `shards`: the number of shards, an integer from 1-32. Defaults to `1`.\n* `replicas`: either an integer or a mapping object. Defaults to `1`.\n * If `replicas` is an integer, it specifies the number of replicas per shard. Specifying more replicas than there are servers will return an error.\n * If `replicas` is an object, it specifies key-value pairs of server tags and the number of replicas to assign to those servers: `{\'tag1\': 2, \'tag2\': 4, \'tag3\': 2, ...}`.\n* `primary_replica_tag`: the primary server specified by its server tag. Required if `replicas` is an object; the tag must be in the object. This must *not* be specified if `replicas` is an integer.\n\nThe [data type](http://rethinkdb.com/docs/data-types/) of a primary key is usually a string (like a UUID) or a number, but it can also be a time, binary object, boolean or an array. It cannot be an object.\n\n*Example* Create a table named \'dc_universe\' with the default settings.\n\n r.db(\'test\').table_create(\'dc_universe\').run(conn)\n \n {\n "config_changes": [\n {\n "new_val": {\n "db": "test",\n "durability": "hard",\n "id": "20ea60d4-3b76-4817-8828-98a236df0297",\n "name": "dc_universe",\n "primary_key": "id",\n "shards": [\n {\n "primary_replica": "rethinkdb_srv1",\n "replicas": [\n "rethinkdb_srv1",\n "rethinkdb_srv2"\n ]\n }\n ],\n "write_acks": "majority"\n },\n "old_val": None\n }\n ],\n "tables_created": 1\n }\n\n*Example* Create a table named \'dc_universe\' using the field \'name\' as primary key.\n\n r.db(\'test\').table_create(\'dc_universe\', primary_key=\'name\').run(conn)\n\n*Example* Create a table set up for two shards and three replicas per shard. This requires three available servers.\n\n r.db(\'test\').table_create(\'dc_universe\', shards=2, replicas=3).run(conn)\n\nRead [Sharding and replication](http://rethinkdb.com/docs/sharding-and-replication/) for a complete discussion of the subject, including advanced topics.\n'),
+ (rethinkdb.ast.DB.table_drop, b'db.table_drop(table_name) -> object\n\nDrop a table. The table and all its data will be deleted.\n\nIf successful, the command returns an object with two fields:\n\n* `tables_dropped`: always `1`.\n* `config_changes`: a list containing one two-field object, `old_val` and `new_val`:\n * `old_val`: the dropped table\'s [config](http://rethinkdb.com/api/python/config) value.\n * `new_val`: always `None`.\n\nIf the given table does not exist in the database, the command throws `RqlRuntimeError`.\n\n*Example* Drop a table named \'dc_universe\'.\n\n r.db(\'test\').table_drop(\'dc_universe\').run(conn)\n \n {\n "config_changes": [\n {\n "old_val": {\n "db": "test",\n "durability": "hard",\n "id": "20ea60d4-3b76-4817-8828-98a236df0297",\n "name": "dc_universe",\n "primary_key": "id",\n "shards": [\n {\n "primary_replica": "rethinkdb_srv1",\n "replicas": [\n "rethinkdb_srv1",\n "rethinkdb_srv2"\n ]\n }\n ],\n "write_acks": "majority"\n },\n "new_val": None\n }\n ],\n "tables_dropped": 1\n }\n'),
+ (rethinkdb.ast.DB.table_list, b"db.table_list() -> array\n\nList all table names in a database. The result is a list of strings.\n\n*Example* List all tables of the 'test' database.\n\n r.db('test').table_list().run(conn)\n \n"),
+ (rethinkdb.ast.RqlQuery.__add__, b'number + number -> number\nstring + string -> string\narray + array -> array\ntime + number -> time\n\nSum two numbers, concatenate two strings, or concatenate 2 arrays.\n\n*Example* It\'s as easy as 2 + 2 = 4.\n\n > (r.expr(2) + 2).run(conn)\n \n 4\n\n*Example* Strings can be concatenated too.\n\n > (r.expr("foo") + "bar").run(conn)\n \n "foobar"\n\n*Example* Arrays can be concatenated too.\n\n > (r.expr(["foo", "bar"]) + ["buzz"]).run(conn)\n \n [\'foo\', \'bar\', \'buzz\']\n\n*Example* Create a date one year from now.\n\n r.now() + 365*24*60*60\n\n*Example* Use [args](http://rethinkdb.com/api/python/args) with `add` to sum multiple values.\n\n > r.add(r.args([10, 20, 30])).run(conn)\n \n 60\n\n*Example* Concatenate an array of strings with `args`.\n\n > r.add(r.args([\'foo\', \'bar\', \'buzz\'])).run(conn)\n \n "foobarbuzz"\n'),
+ (rethinkdb.add, b'number + number -> number\nstring + string -> string\narray + array -> array\ntime + number -> time\n\nSum two numbers, concatenate two strings, or concatenate 2 arrays.\n\n*Example* It\'s as easy as 2 + 2 = 4.\n\n > (r.expr(2) + 2).run(conn)\n \n 4\n\n*Example* Strings can be concatenated too.\n\n > (r.expr("foo") + "bar").run(conn)\n \n "foobar"\n\n*Example* Arrays can be concatenated too.\n\n > (r.expr(["foo", "bar"]) + ["buzz"]).run(conn)\n \n [\'foo\', \'bar\', \'buzz\']\n\n*Example* Create a date one year from now.\n\n r.now() + 365*24*60*60\n\n*Example* Use [args](http://rethinkdb.com/api/python/args) with `add` to sum multiple values.\n\n > r.add(r.args([10, 20, 30])).run(conn)\n \n 60\n\n*Example* Concatenate an array of strings with `args`.\n\n > r.add(r.args([\'foo\', \'bar\', \'buzz\'])).run(conn)\n \n "foobarbuzz"\n'),
+ (rethinkdb.ast.RqlQuery.__and__, b'bool & bool -> bool\nr.and_(bool, bool) -> bool\nbool.and_(bool) -> bool\n\nCompute the logical "and" of two or more values. The `and_` command can be used as an infix operator after its first argument (`r.expr(True).and_(False)`) or given all of its arguments as parameters (`r.and_(True, False)`). The standard Python and operator, `&`, may also be used with ReQL.\n\n*Example* Return whether both `a` and `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) & b).run(conn)\n \n False\n*Example* Return whether all of `x`, `y` and `z` evaluate to true.\n\n > x = True\n > y = True\n > z = True\n > r.and_(x, y, z).run(conn)\n \n True\n'),
+ (rethinkdb.and_, b'bool & bool -> bool\nr.and_(bool, bool) -> bool\nbool.and_(bool) -> bool\n\nCompute the logical "and" of two or more values. The `and_` command can be used as an infix operator after its first argument (`r.expr(True).and_(False)`) or given all of its arguments as parameters (`r.and_(True, False)`). The standard Python and operator, `&`, may also be used with ReQL.\n\n*Example* Return whether both `a` and `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) & b).run(conn)\n \n False\n*Example* Return whether all of `x`, `y` and `z` evaluate to true.\n\n > x = True\n > y = True\n > z = True\n > r.and_(x, y, z).run(conn)\n \n True\n'),
+ (rethinkdb.ast.RqlQuery.__div__, b"number / number -> number\n\nDivide two numbers.\n\n*Example* It's as easy as 2 / 2 = 1.\n\n (r.expr(2) / 2).run(conn)\n"),
+ (rethinkdb.div, b"number / number -> number\n\nDivide two numbers.\n\n*Example* It's as easy as 2 / 2 = 1.\n\n (r.expr(2) / 2).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.__eq__, b'value == value -> bool\nvalue.eq(value) -> bool\n\nTest if two values are equal.\n\n*Example* Does 2 equal 2?\n\n (r.expr(2) == 2).run(conn)\n r.expr(2).eq(2).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.eq, b'value == value -> bool\nvalue.eq(value) -> bool\n\nTest if two values are equal.\n\n*Example* Does 2 equal 2?\n\n (r.expr(2) == 2).run(conn)\n r.expr(2).eq(2).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.__ge__, b'value >= value -> bool\nvalue.ge(value) -> bool\n\nTest if the first value is greater than or equal to other.\n\n*Example* Is 2 greater than or equal to 2?\n\n (r.expr(2) >= 2).run(conn)\n r.expr(2).ge(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.ge, b'value >= value -> bool\nvalue.ge(value) -> bool\n\nTest if the first value is greater than or equal to other.\n\n*Example* Is 2 greater than or equal to 2?\n\n (r.expr(2) >= 2).run(conn)\n r.expr(2).ge(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__gt__, b'value > value -> bool\nvalue.gt(value) -> bool\n\nTest if the first value is greater than other.\n\n*Example* Is 2 greater than 2?\n\n (r.expr(2) > 2).run(conn)\n r.expr(2).gt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.gt, b'value > value -> bool\nvalue.gt(value) -> bool\n\nTest if the first value is greater than other.\n\n*Example* Is 2 greater than 2?\n\n (r.expr(2) > 2).run(conn)\n r.expr(2).gt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__le__, b'value <= value -> bool\nvalue.le(value) -> bool\n\nTest if the first value is less than or equal to other.\n\n*Example* Is 2 less than or equal to 2?\n\n (r.expr(2) <= 2).run(conn)\n r.expr(2).le(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.le, b'value <= value -> bool\nvalue.le(value) -> bool\n\nTest if the first value is less than or equal to other.\n\n*Example* Is 2 less than or equal to 2?\n\n (r.expr(2) <= 2).run(conn)\n r.expr(2).le(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__lt__, b'value < value -> bool\nvalue.lt(value) -> bool\n\nTest if the first value is less than other.\n\n*Example* Is 2 less than 2?\n\n (r.expr(2) < 2).run(conn)\n r.expr(2).lt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.lt, b'value < value -> bool\nvalue.lt(value) -> bool\n\nTest if the first value is less than other.\n\n*Example* Is 2 less than 2?\n\n (r.expr(2) < 2).run(conn)\n r.expr(2).lt(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__mod__, b"number % number -> number\n\nFind the remainder when dividing two numbers.\n\n*Example* It's as easy as 2 % 2 = 0.\n\n (r.expr(2) % 2).run(conn)\n\n`\n"),
+ (rethinkdb.mod, b"number % number -> number\n\nFind the remainder when dividing two numbers.\n\n*Example* It's as easy as 2 % 2 = 0.\n\n (r.expr(2) % 2).run(conn)\n\n`\n"),
+ (rethinkdb.ast.RqlQuery.__mul__, b'number * number -> number\narray * number -> array\n\nMultiply two numbers, or make a periodic array.\n\n*Example* It\'s as easy as 2 * 2 = 4.\n\n (r.expr(2) * 2).run(conn)\n\n*Example* Arrays can be multiplied by numbers as well.\n\n (r.expr(["This", "is", "the", "song", "that", "never", "ends."]) * 100).run(conn)\n\n'),
+ (rethinkdb.mul, b'number * number -> number\narray * number -> array\n\nMultiply two numbers, or make a periodic array.\n\n*Example* It\'s as easy as 2 * 2 = 4.\n\n (r.expr(2) * 2).run(conn)\n\n*Example* Arrays can be multiplied by numbers as well.\n\n (r.expr(["This", "is", "the", "song", "that", "never", "ends."]) * 100).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__ne__, b'value != value -> bool\nvalue.ne(value) -> bool\n\nTest if two values are not equal.\n\n*Example* Does 2 not equal 2?\n\n (r.expr(2) != 2).run(conn)\n r.expr(2).ne(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.ne, b'value != value -> bool\nvalue.ne(value) -> bool\n\nTest if two values are not equal.\n\n*Example* Does 2 not equal 2?\n\n (r.expr(2) != 2).run(conn)\n r.expr(2).ne(2).run(conn)\n\n'),
+ (rethinkdb.ast.RqlQuery.__invert__, b'bool.not_() -> bool\nnot_(bool) -> bool\n(~bool) -> bool\n\nCompute the logical inverse (not) of an expression.\n\n`not_` can be called either via method chaining, immediately after an expression that evaluates as a boolean value, or by passing the expression as a parameter to `not_`. All values that are not `False` or `None` will be converted to `True`.\n\nYou may also use `~` as a shorthand operator.\n\n*Example* Not true is false.\n\n r.not_(True).run(conn)\n r.expr(True).not_().run(conn)\n (~r.expr(True)).run(conn)\n\nThese evaluate to `false`.\n\nNote that when using `~` the expression is wrapped in parentheses. Without this, Python will evaluate `r.expr(True)` *first* rather than using the ReQL operator and return an incorrect value. (`~True` evaluates to &minus;2 in Python.)\n\n*Example* Return all the users that do not have a "flag" field.\n\n r.table(\'users\').filter(\n lambda users: (~users.has_fields(\'flag\'))\n ).run(conn)\n\n*Example* As above, but prefix-style.\n\n r.table(\'users\').filter(\n lambda users: r.not_(users.has_fields(\'flag\'))\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.not_, b'bool.not_() -> bool\nnot_(bool) -> bool\n(~bool) -> bool\n\nCompute the logical inverse (not) of an expression.\n\n`not_` can be called either via method chaining, immediately after an expression that evaluates as a boolean value, or by passing the expression as a parameter to `not_`. All values that are not `False` or `None` will be converted to `True`.\n\nYou may also use `~` as a shorthand operator.\n\n*Example* Not true is false.\n\n r.not_(True).run(conn)\n r.expr(True).not_().run(conn)\n (~r.expr(True)).run(conn)\n\nThese evaluate to `false`.\n\nNote that when using `~` the expression is wrapped in parentheses. Without this, Python will evaluate `r.expr(True)` *first* rather than using the ReQL operator and return an incorrect value. (`~True` evaluates to &minus;2 in Python.)\n\n*Example* Return all the users that do not have a "flag" field.\n\n r.table(\'users\').filter(\n lambda users: (~users.has_fields(\'flag\'))\n ).run(conn)\n\n*Example* As above, but prefix-style.\n\n r.table(\'users\').filter(\n lambda users: r.not_(users.has_fields(\'flag\'))\n ).run(conn)\n'),
+ (rethinkdb.not_, b'bool.not_() -> bool\nnot_(bool) -> bool\n(~bool) -> bool\n\nCompute the logical inverse (not) of an expression.\n\n`not_` can be called either via method chaining, immediately after an expression that evaluates as a boolean value, or by passing the expression as a parameter to `not_`. All values that are not `False` or `None` will be converted to `True`.\n\nYou may also use `~` as a shorthand operator.\n\n*Example* Not true is false.\n\n r.not_(True).run(conn)\n r.expr(True).not_().run(conn)\n (~r.expr(True)).run(conn)\n\nThese evaluate to `false`.\n\nNote that when using `~` the expression is wrapped in parentheses. Without this, Python will evaluate `r.expr(True)` *first* rather than using the ReQL operator and return an incorrect value. (`~True` evaluates to &minus;2 in Python.)\n\n*Example* Return all the users that do not have a "flag" field.\n\n r.table(\'users\').filter(\n lambda users: (~users.has_fields(\'flag\'))\n ).run(conn)\n\n*Example* As above, but prefix-style.\n\n r.table(\'users\').filter(\n lambda users: r.not_(users.has_fields(\'flag\'))\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.__or__, b'bool | bool -> bool\nbool.or_(bool[, bool, ...]) -> bool\nr.or_(bool, bool) -> bool\n\nCompute the logical "or" of two or more values. The `or_` command can be used as an infix operator after its first argument (`r.expr(True).or_(False)`) or given all of its arguments as parameters (`r.or_(True, False)`). The standard Python or operator, `|`, may also be used with ReQL.\n\n*Example* Return whether either `a` or `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) | b).run(conn)\n \n True\n\n*Example* Return whether any of `x`, `y` or `z` evaluate to true.\n\n > x = False\n > y = False\n > z = False\n > r.or_(x, y, z).run(conn)\n \n False\n\n__Note:__ When using `or` inside a `filter` predicate to test the values of fields that may not exist on the documents being tested, you should use the `default` command with those fields so they explicitly return `False`.\n\n r.table(\'posts\').filter(lambda post:\n post[\'category\'].default(\'foo\').eq(\'article\').or(\n post[\'genre\'].default(\'foo\').eq(\'mystery\'))\n ).run(conn)\n'),
+ (rethinkdb.or_, b'bool | bool -> bool\nbool.or_(bool[, bool, ...]) -> bool\nr.or_(bool, bool) -> bool\n\nCompute the logical "or" of two or more values. The `or_` command can be used as an infix operator after its first argument (`r.expr(True).or_(False)`) or given all of its arguments as parameters (`r.or_(True, False)`). The standard Python or operator, `|`, may also be used with ReQL.\n\n*Example* Return whether either `a` or `b` evaluate to true.\n\n > a = True\n > b = False\n > (r.expr(a) | b).run(conn)\n \n True\n\n*Example* Return whether any of `x`, `y` or `z` evaluate to true.\n\n > x = False\n > y = False\n > z = False\n > r.or_(x, y, z).run(conn)\n \n False\n\n__Note:__ When using `or` inside a `filter` predicate to test the values of fields that may not exist on the documents being tested, you should use the `default` command with those fields so they explicitly return `False`.\n\n r.table(\'posts\').filter(lambda post:\n post[\'category\'].default(\'foo\').eq(\'article\').or(\n post[\'genre\'].default(\'foo\').eq(\'mystery\'))\n ).run(conn)\n'),
+ (rethinkdb.random, b"r.random() -> number\nr.random(number[, number], float=True) -> number\nr.random(integer[, integer]) -> integer\n\nGenerate a random number between given (or implied) bounds. `random` takes zero, one or two arguments.\n\n- With __zero__ arguments, the result will be a floating-point number in the range `[0,1)` (from 0 up to but not including 1).\n- With __one__ argument _x,_ the result will be in the range `[0,x)`, and will be integer unless `float=True` is given as an option. Specifying a floating point number without the `float` option will raise an error.\n- With __two__ arguments _x_ and _y,_ the result will be in the range `[x,y)`, and will be integer unless `float=True` is given as an option. If _x_ and _y_ are equal an error will occur, unless the floating-point option has been specified, in which case _x_ will be returned. Specifying a floating point number without the `float` option will raise an error.\n\nNote: The last argument given will always be the 'open' side of the range, but when\ngenerating a floating-point number, the 'open' side may be less than the 'closed' side.\n\n*Example* Generate a random number in the range `[0,1)`\n\n r.random().run(conn)\n\n*Example* Generate a random integer in the range `[0,100)`\n\n r.random(100).run(conn)\n r.random(0, 100).run(conn)\n\n*Example* Generate a random number in the range `(-2.24,1.59]`\n\n r.random(1.59, -2.24, float=True).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.__sub__, b"number - number -> number\ntime - time -> number\ntime - number -> time\n\nSubtract two numbers.\n\n*Example* It's as easy as 2 - 2 = 0.\n\n (r.expr(2) - 2).run(conn)\n\n*Example* Create a date one year ago today.\n\n r.now() - 365*24*60*60\n\n*Example* Retrieve how many seconds elapsed between today and date\n\n r.now() - date\n\n"),
+ (rethinkdb.sub, b"number - number -> number\ntime - time -> number\ntime - number -> time\n\nSubtract two numbers.\n\n*Example* It's as easy as 2 - 2 = 0.\n\n (r.expr(2) - 2).run(conn)\n\n*Example* Create a date one year ago today.\n\n r.now() - 365*24*60*60\n\n*Example* Retrieve how many seconds elapsed between today and date\n\n r.now() - date\n\n"),
+ (rethinkdb.ast.Table.between, b'table.between(lower_key, upper_key[, index=\'id\', left_bound=\'closed\', right_bound=\'open\'])\n -> selection\n\nGet all documents between two keys. Accepts three optional arguments: `index`, `left_bound`, and `right_bound`. If `index` is set to the name of a secondary index, `between` will return all documents where that index\'s value is in the specified range (it uses the primary key by default). `left_bound` or `right_bound` may be set to `open` or `closed` to indicate whether or not to include that endpoint of the range (by default, `left_bound` is closed and `right_bound` is open).\n\nYou may also use the special constants `r.minval` and `r.maxval` for boundaries, which represent "less than any index key" and "more than any index key" respectively. For instance, if you use `r.minval` as the lower key, then `between` will return all documents whose primary keys (or indexes) are less than the specified upper key.\n\nNote that compound indexes are sorted using [lexicographical order][lo]. Take the following range as an example:\n\n\t[[1, "c"] ... [5, "e"]]\n\nThis range includes all compound keys:\n\n* whose first item is 1 and second item is equal or greater than "c";\n* whose first item is between 1 and 5, *regardless of the value of the second item*;\n* whose first item is 5 and second item is less than or equal to "e".\n\n[lo]: https://en.wikipedia.org/wiki/Lexicographical_order\n\n*Example* Find all users with primary key >= 10 and < 20 (a normal half-open interval).\n\n r.table(\'marvel\').between(10, 20).run(conn)\n\n*Example* Find all users with primary key >= 10 and <= 20 (an interval closed on both sides).\n\n r.table(\'marvel\').between(10, 20, right_bound=\'closed\').run(conn)\n\n*Example* Find all users with primary key < 20.\n\n r.table(\'marvel\').between(r.minval, 20).run(conn)\n\n*Example* Find all users with primary key > 10.\n\n r.table(\'marvel\').between(10, r.maxval, left_bound=\'open\').run(conn)\n\n*Example* Between can be used on secondary indexes too. Just pass an optional index argument giving the secondary index to query.\n\n r.table(\'dc\').between(\'dark_knight\', \'man_of_steel\', index=\'code_name\').run(conn)\n\n*Example* Get all users whose full name is between "John Smith" and "Wade Welles."\n\n r.table("users").between(["Smith", "John"], ["Welles", "Wade"],\n index="full_name").run(conn)\n\n*Example* Subscribe to a [changefeed](http://rethinkdb.com/docs/changefeeds/javascript) of teams ranked in the top 10.\n\n changes = r.table("teams").between(1, 11, index="rank").changes().run(conn)\n\n__Note:__ Between works with secondary indexes on date fields, but will not work with unindexed date fields. To test whether a date value is between two other dates, use the [during](http://rethinkdb.com/api/python/during) command, not `between`.\n\nSecondary indexes can be used in extremely powerful ways with `between` and other commands; read the full article on [secondary indexes](http://rethinkdb.com/docs/secondary-indexes) for examples using boolean operations, `contains` and more.\n\n__Note:__ RethinkDB uses byte-wise ordering for `between` and does not support Unicode collations; non-ASCII characters will be sorted by UTF-8 codepoint.\n\n__Note:__ If you chain `between` after [order_by](http://rethinkdb.com/api/python/order_by), the `between` command must use the index specified in `order_by`, and will default to that index. Trying to specify another index will result in a `RqlRuntimeError`.\n'),
+ (rethinkdb.db, b"r.db(db_name) -> db\n\nReference a database.\n\n*Example* Before we can query a table we have to select the correct database.\n\n r.db('heroes').table('marvel').run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.filter, b'selection.filter(predicate[, default=False]) -> selection\nstream.filter(predicate[, default=False]) -> stream\narray.filter(predicate[, default=False]) -> array\n\nReturn all the elements in a sequence for which the given predicate is true. The return value of `filter` will be the same as the input (sequence, stream, or array). Documents can be filtered in a variety of ways&mdash;ranges, nested values, boolean conditions, and the results of anonymous functions.\n\nBy default, `filter` will silently skip documents with missing fields: if the predicate tries to access a field that doesn\'t exist (for instance, the predicate `{\'age\': 30}` applied to a document with no `age` field), that document will not be returned in the result set, and no error will be generated. This behavior can be changed with the `default` optional argument.\n\n* If `default` is set to `True`, documents with missing fields will be returned rather than skipped.\n* If `default` is set to `r.error()`, an `RqlRuntimeError` will be thrown when a document with a missing field is tested.\n* If `default` is set to `False` (the default), documents with missing fields will be skipped.\n\n*Example* Get all users who are 30 years old.\n\n r.table(\'users\').filter({\'age\': 30}).run(conn)\n\nThe predicate `{\'age\': 30}` selects documents in the `users` table with an `age` field whose value is `30`. Documents with an `age` field set to any other value *or* with no `age` field present are skipped.\n\nWhile the `{\'field\': value}` style of predicate is useful for exact matches, a more general way to write a predicate is to use the [row](http://rethinkdb.com/api/python/row) command with a comparison operator such as [eq](http://rethinkdb.com/api/python/eq) (`==`) or [gt](http://rethinkdb.com/api/python/gt) (`>`), or to use a lambda function that returns `True` or `False`.\n\n r.table(\'users\').filter(r.row["age"] == 30).run(conn)\n\nIn this case, the predicate `r.row["age"] == 30` returns `True` if the field `age` is equal to 30. You can write this predicate as a lambda function instead:\n\n r.table(\'users\').filter(lambda user:\n user["age"] == 30\n ).run(conn)\n\nPredicates to `filter` are evaluated on the server, and must use ReQL expressions. Some Python comparison operators are overloaded by the RethinkDB driver and will be translated to ReQL, such as `==`, `<`/`>` and `|`/`&` (note the single character form, rather than `||`/`&&`).\n\nAlso, predicates must evaluate document fields. They cannot evaluate [secondary indexes](http://rethinkdb.com/docs/secondary-indexes/).\n\n*Example* Get all users who are more than 18 years old.\n\n r.table("users").filter(r.row["age"] > 18).run(conn)\n\n*Example* Get all users who are less than 18 years old and more than 13 years old.\n\n r.table("users").filter((r.row["age"] < 18) & (r.row["age"] > 13)).run(conn)\n\n*Example* Get all users who are more than 18 years old or have their parental consent.\n\n r.table("users").filter(\n (r.row["age"] >= 18) | (r.row["hasParentalConsent"])).run(conn)\n\n*Example* Retrieve all users who subscribed between January 1st, 2012\n(included) and January 1st, 2013 (excluded).\n\n r.table("users").filter(\n lambda user: user["subscription_date"].during(\n r.time(2012, 1, 1, \'Z\'), r.time(2013, 1, 1, \'Z\'))\n ).run(conn)\n\n*Example* Retrieve all users who have a gmail account (whose field `email` ends with `@gmail.com`).\n\n r.table("users").filter(\n lambda user: user["email"].match("@gmail.com$")\n ).run(conn)\n\n*Example* Filter based on the presence of a value in an array.\n\nGiven this schema for the `users` table:\n\n {\n "name": <type \'str\'>\n "places_visited": [<type \'str\'>]\n }\n\nRetrieve all users whose field `places_visited` contains `France`.\n\n r.table("users").filter(lambda user:\n user["places_visited"].contains("France")\n ).run(conn)\n\n*Example* Filter based on nested fields.\n\nGiven this schema for the `users` table:\n\n {\n "id": <type \'str\'>\n "name": {\n "first": <type \'str\'>,\n "middle": <type \'str\'>,\n "last": <type \'str\'>\n }\n }\n\nRetrieve all users named "William Adama" (first name "William", last name\n"Adama"), with any middle name.\n\n r.table("users").filter({\n "name": {\n "first": "William",\n "last": "Adama"\n }\n }).run(conn)\n\nIf you want an exact match for a field that is an object, you will have to use `r.literal`.\n\nRetrieve all users named "William Adama" (first name "William", last name\n"Adama"), and who do not have a middle name.\n\n r.table("users").filter(r.literal({\n "name": {\n "first": "William",\n "last": "Adama"\n }\n })).run(conn)\n\nYou may rewrite these with lambda functions.\n\n r.table("users").filter(\n lambda user:\n (user["name"]["first"] == "William")\n & (user["name"]["last"] == "Adama")\n ).run(conn)\n\n r.table("users").filter(lambda user:\n user["name"] == {\n "first": "William",\n "last": "Adama"\n }\n ).run(conn)\n\nBy default, documents missing fields tested by the `filter` predicate are skipped. In the previous examples, users without an `age` field are not returned. By passing the optional `default` argument to `filter`, you can change this behavior.\n\n*Example* Get all users less than 18 years old or whose `age` field is missing.\n\n r.table("users").filter(r.row["age"] < 18, default=True).run(conn)\n\n*Example* Get all users more than 18 years old. Throw an error if a\ndocument is missing the field `age`.\n\n r.table("users").filter(r.row["age"] > 18, default=r.error()).run(conn)\n\n*Example* Get all users who have given their phone number (all the documents whose field `phone_number` exists and is not `None`).\n\n r.table(\'users\').filter(\n lambda user: user.has_fields(\'phone_number\')\n ).run(conn)\n\n*Example* Get all users with an "editor" role or an "admin" privilege.\n\n r.table(\'users\').filter(\n lambda user: (user[\'role\'] == \'editor\').default(False) |\n (user[\'privilege\'] == \'admin\').default(False)\n ).run(conn)\n\nInstead of using the `default` optional argument to `filter`, we have to use default values on the fields within the `or` clause. Why? If the field on the left side of the `or` clause is missing from a document&mdash;in this case, if the user doesn\'t have a `role` field&mdash;the predicate will generate an error, and will return `False` (or the value the `default` argument is set to) without evaluating the right side of the `or`. By using `.default(False)` on the fields, each side of the `or` will evaluate to either the field\'s value or `False` if the field doesn\'t exist.\n'),
+ (rethinkdb.ast.Table.get, b"table.get(key) -> singleRowSelection\n\nGet a document by primary key.\n\nIf no document exists with that primary key, `get` will return `None`.\n\n*Example* Find a document by UUID.\n\n r.table('posts').get('a9849eef-7176-4411-935b-79a6e3c56a74').run(conn)\n\n*Example* Find a document and merge another document with it.\n\n r.table('heroes').get(3).merge(\n { 'powers': ['invisibility', 'speed'] }\n ).run(conn)\n\n_*Example* Subscribe to a document's [changefeed](http://rethinkdb.com/docs/changefeeds/python).\n\n changes = r.table('heroes').get(3).changes().run(conn)\n"),
+ (rethinkdb.ast.Table.get_all, b"table.get_all(key1[, key2...], [, index='id']) -> selection\n\nGet all documents where the given value matches the value of the requested index.\n\n*Example* Secondary index keys are not guaranteed to be unique so we cannot query via [get](http://rethinkdb.com/api/python/get/) when using a secondary index.\n\n r.table('marvel').get_all('man_of_steel', index='code_name').run(conn)\n\n*Example* Without an index argument, we default to the primary index. While `get` will either return the document or `None` when no document with such a primary key value exists, this will return either a one or zero length stream.\n\n r.table('dc').get_all('superman').run(conn)\n\n*Example* You can get multiple documents in a single call to `get_all`.\n\n r.table('dc').get_all('superman', 'ant man').run(conn)\n\n*Example* You can use [args](http://rethinkdb.com/api/python/args/) with `get_all` to retrieve multiple documents whose keys are in a list. This uses `get_all` to get a list of female superheroes, coerces that to an array, and then gets a list of villains who have those superheroes as enemies.\n\n r.do(\n r.table('heroes').get_all('f', {'index': 'gender'})['id'].coerce_to('array'), \n lamdba heroines: r.table('villains').get_all(r.args(heroines))\n ).run(conn)\n\nSecondary indexes can be used in extremely powerful ways with `get_all` and other commands; read the full article on [secondary indexes](http://rethinkdb.com/docs/secondary-indexes) for examples using boolean operations, `contains` and more.\n"),
+ (rethinkdb.ast.DB.table, b"db.table(name[, use_outdated=False, identifier_format='name']) -> table\n\nReturn all documents in a table. Other commands may be chained after `table` to return a subset of documents (such as `get` and `filter`) or perform further processing.\n\n*Example* Return all documents in the table 'marvel' of the default database.\n\n r.table('marvel').run(conn)\n\n*Example* Return all documents in the table 'marvel' of the database 'heroes'.\n\n r.db('heroes').table('marvel').run(conn)\n\nThere are two optional arguments.\n\n* `use_outdated`: if `True`, this allows potentially out-of-date data to be returned, with potentially faster reads. It also allows you to perform reads from a secondary replica if a primary has failed. Default `False`.\n* `identifier_format`: possible values are `name` and `uuid`, with a default of `name`. If set to `uuid`, then [system tables](http://rethinkdb.com/docs/system-tables/) will refer to servers, databases and tables by UUID rather than name. (This only has an effect when used with system tables.)\n\n*Example* Allow potentially out-of-date data in exchange for faster reads.\n\n r.db('heroes').table('marvel', use_outdated=True).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.downcase, b'string.downcase() -> string\n\nLowercases a string.\n\n*Example*\n\n > r.expr("Sentence about LaTeX.").downcase().run(conn)\n "sentence about latex."\n\n__Note:__ `upcase` and `downcase` only affect ASCII characters.\n'),
+ (rethinkdb.ast.RqlQuery.match, b'string.match(regexp) -> None/object\n\nMatches against a regular expression. If there is a match, returns an object with the fields:\n\n- `str`: The matched string\n- `start`: The matched string\'s start\n- `end`: The matched string\'s end\n- `groups`: The capture groups defined with parentheses\n\nIf no match is found, returns `None`.\n\nAccepts RE2 syntax\n([https://code.google.com/p/re2/wiki/Syntax](https://code.google.com/p/re2/wiki/Syntax)).\nYou can enable case-insensitive matching by prefixing the regular expression with\n`(?i)`. See the linked RE2 documentation for more flags.\n\nThe `match` command does not support backreferences.\n\n*Example* Get all users whose name starts with "A". Because `None` evaluates to `false` in\n`filter`, you can just use the result of `match` for the predicate.\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("^A")\n ).run(conn)\n\n*Example* Get all users whose name ends with "n".\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("n$")\n ).run(conn)\n\n*Example* Get all users whose name has "li" in it\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("li")\n ).run(conn)\n\n*Example* Get all users whose name is "John" with a case-insensitive search.\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("(?i)^john$")\n ).run(conn)\n\n*Example* Get all users whose name is composed of only characters between "a" and "z".\n\n r.table(\'users\').filter(lambda doc:\n doc[\'name\'].match("(?i)^[a-z]+$")\n ).run(conn)\n\n*Example* Get all users where the zipcode is a string of 5 digits.\n\n r.table(\'users\').filter(lambda doc:\n doc[\'zipcode\'].match("\\d{5}")\n ).run(conn)\n\n*Example* Retrieve the domain of a basic email\n\n r.expr("name@domain.com").match(".*@(.*)").run(conn)\n\nResult:\n\n {\n "start": 0,\n "end": 20,\n "str": "name@domain.com",\n "groups":[\n {\n "end": 17,\n "start": 7,\n "str": "domain.com"\n }\n ]\n }\n\nYou can then retrieve only the domain with the [\\[\\]](http://rethinkdb.com/api/python/get_field) selector.\n\n r.expr("name@domain.com").match(".*@(.*)")["groups"][0]["str"].run(conn)\n\nReturns `\'domain.com\'`\n\n*Example* Fail to parse out the domain and returns `None`.\n\n r.expr("name[at]domain.com").match(".*@(.*)").run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.split, b'string.split([separator, [max_splits]]) -> array\n\nSplits a string into substrings. Splits on whitespace when called\nwith no arguments. When called with a separator, splits on that\nseparator. When called with a separator and a maximum number of\nsplits, splits on that separator at most `max_splits` times. (Can be\ncalled with `None` as the separator if you want to split on whitespace\nwhile still specifying `max_splits`.)\n\nMimics the behavior of Python\'s `string.split` in edge cases, except\nfor splitting on the empty string, which instead produces an array of\nsingle-character strings.\n\n*Example* Split on whitespace.\n\n > r.expr("foo bar bax").split().run(conn)\n ["foo", "bar", "bax"]\n\n*Example* Split the entries in a CSV file.\n\n > r.expr("12,37,,22,").split(",").run(conn)\n ["12", "37", "", "22", ""]\n\n*Example* Split a string into characters.\n\n > r.expr("mlucy").split("").run(conn)\n ["m", "l", "u", "c", "y"]\n\n*Example* Split the entries in a CSV file, but only at most 3\ntimes.\n\n > r.expr("12,37,,22,").split(",", 3).run(conn)\n ["12", "37", "", "22,"]\n\n*Example* Split on whitespace at most once (i.e. get the first word).\n\n > r.expr("foo bar bax").split(None, 1).run(conn)\n ["foo", "bar bax"]\n'),
+ (rethinkdb.ast.RqlQuery.upcase, b'string.upcase() -> string\n\nUppercases a string.\n\n*Example*\n\n > r.expr("Sentence about LaTeX.").upcase().run(conn)\n "SENTENCE ABOUT LATEX."\n\n__Note:__ `upcase` and `downcase` only affect ASCII characters.\n'),
+ (rethinkdb.ast.RqlQuery.concat_map, b'stream.concat_map(mapping_function) -> stream\narray.concat_map(mapping_function) -> array\n\nConcatenate one or more elements into a single sequence using a mapping function.\n\n`concat_map` works in a similar fashion to `map`, applying the given function to each element in a sequence, but it will always return a single sequence. If the mapping function returns a sequence, `map` would produce a sequence of sequences:\n\n r.expr([1, 2, 3]).map(lambda x: [x, x.mul(2)]).run(conn)\n\nResult:\n\n [[1, 2], [2, 4], [3, 6]]\n\nWhereas `concat_map` with the same mapping function would merge those sequences into one:\n\n r.expr([1, 2, 3]).concat_map(lambda x: [x, x.mul(2)]).run(conn)\n\nResult:\n\n [1, 2, 2, 4, 3, 6]\n\nThe return value, array or stream, will be the same type as the input.\n\n*Example* Construct a sequence of all monsters defeated by Marvel heroes. The field "defeatedMonsters" is an array of one or more monster names.\n\n r.table(\'marvel\').concat_map(lambda hero: hero[\'defeatedMonsters\']).run(conn)\n\n*Example* Simulate an [eq_join](http://rethinkdb.com/api/python/eq_join/) using `concat_map`. (This is how ReQL joins are implemented internally.)\n\n r.table(\'posts\').concat_map(\n lambda post: r.table(\'comments\').get_all(\n post[\'id\'], index=\'post_id\'\n ).map(\n lambda comment: { \'left\': post, \'right\': comment}\n )\n ).run(conn)\n'),
+ (rethinkdb.ast.RqlQuery.is_empty, b"sequence.is_empty() -> bool\n\nTest if a sequence is empty.\n\n*Example* Are there any documents in the marvel table?\n\n r.table('marvel').is_empty().run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.limit, b"sequence.limit(n) -> stream\narray.limit(n) -> array\n\nEnd the sequence after the given number of elements.\n\n*Example* Only so many can fit in our Pantheon of heroes.\n\n r.table('marvel').order_by('belovedness').limit(10).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.map, b"sequence1.map([sequence2, ...], mapping_function) -> stream\narray1.map([sequence2, ...], mapping_function) -> array\nr.map(sequence1[, sequence2, ...], mapping_function) -> stream\nr.map(array1[, array2, ...], mapping_function) -> array\n\nTransform each element of one or more sequences by applying a mapping function to them. If `map` is run with two or more sequences, it will iterate for as many items as there are in the shortest sequence.\n\nNote that `map` can only be applied to sequences, not single values. If you wish to apply a function to a single value/selection (including an array), use the [do](http://rethinkdb.com/api/python/do) command.\n\n*Example* Return the first five squares.\n\n > r.expr([1, 2, 3, 4, 5]).map(lambda val: (val * val)).run(conn)\n \n [1, 4, 9, 16, 25]\n\n*Example* Sum the elements of three sequences.\n\n > sequence1 = [100, 200, 300, 400]\n > sequence2 = [10, 20, 30, 40]\n > sequence3 = [1, 2, 3, 4]\n > r.map(sequence1, sequence2, sequence3,\n lambda val1, val2, val3: (val1 + val2 + val3)).run(conn)\n \n [111, 222, 333, 444]\n\n*Example* Rename a field when retrieving documents using `map` and `merge`.\n\nThis example renames the field `id` to `user_id` when retrieving documents from the table `users`.\n\n r.table('users').map(\n lambda doc: doc.merge({'user_id': doc['id']}).without('id')).run(conn)\n\nNote that in this case, [row](http://rethinkdb.com/api/python/row) may be used as an alternative to writing an anonymous function, as it returns the same value as the function parameter receives:\n\n r.table('users').map(\n r.row.merge({'user_id': r.row['id']}).without('id')).run(conn)\n\n*Example* Assign every superhero an archenemy.\n\n r.table('heroes').map(r.table('villains'),\n lambda hero, villain: hero.merge({'villain': villain})).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.nth, b"sequence.nth(index) -> object\nselection.nth(index) -> selection&lt;object&gt;\n\nGet the *nth* element of a sequence, counting from zero. If the argument is negative, count from the last element.\n\nIn Python, you can use `[]` with an integer as a shorthand for `nth`.\n\n*Example* Select the second element in the array.\n\n r.expr([1,2,3]).nth(1).run(conn)\n r.expr([1,2,3])[1].run(conn)\n\n*Example* Select the bronze medalist from the competitors.\n\n r.table('players').order_by(index=r.desc('score')).nth(3).run(conn)\n\n*Example* Select the last place competitor.\n\n r.table('players').order_by(index=r.desc('score')).nth(-1).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.offsets_of, b"sequence.offsets_of(datum | predicate) -> array\n\nGet the indexes of an element in a sequence. If the argument is a predicate, get the indexes of all elements matching it.\n\n*Example* Find the position of the letter 'c'.\n\n r.expr(['a','b','c']).offsets_of('c').run(conn)\n\n*Example* Find the popularity ranking of invisible heroes.\n\n r.table('marvel').union(r.table('dc')).order_by('popularity').offsets_of(\n r.row['superpowers'].contains('invisibility')\n ).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.order_by, b'table.order_by([key1...], index=index_name) -> selection<stream>\nselection.order_by(key1, [key2...]) -> selection<array>\nsequence.order_by(key1, [key2...]) -> array\n\nSort the sequence by document values of the given key(s). To specify\nthe ordering, wrap the attribute with either `r.asc` or `r.desc`\n(defaults to ascending).\n\n__Note:__ RethinkDB uses byte-wise ordering for `orderBy` and does not support Unicode collations; non-ASCII characters will be sorted by UTF-8 codepoint. For more information on RethinkDB\'s sorting order, read the section in [ReQL data types](http://rethinkdb.com/docs/data-types/#sorting-order).\n\nSorting without an index requires the server to hold the sequence in\nmemory, and is limited to 100,000 documents (or the setting of the `arrayLimit` option for [run](http://rethinkdb.com/api/python/run)). Sorting with an index can\nbe done on arbitrarily large tables, or after a `between` command\nusing the same index.\n\n*Example* Order all the posts using the index `date`. \n\n r.table(\'posts\').order_by(index=\'date\').run(conn)\n\nThe index must either be the primary key or have been previously created with [index_create](http://rethinkdb.com/api/python/index_create/).\n\n r.table(\'posts\').index_create(\'date\').run(conn)\n\nYou can also select a descending ordering:\n\n r.table(\'posts\').order_by(index=r.desc(\'date\')).run(conn, callback)\n\n*Example* Order a sequence without an index.\n\n r.table(\'posts\').get(1)[\'comments\'].order_by(\'date\')\n\nYou can also select a descending ordering:\n\n r.table(\'posts\').get(1)[\'comments\'].order_by(r.desc(\'date\'))\n\nIf you\'re doing ad-hoc analysis and know your table won\'t have more then 100,000\nelements (or you\'ve changed the setting of the `arrayLimit` option for [run](http://rethinkdb.com/api/python/run)) you can run `order_by` without an index:\n\n r.table(\'small_table\').order_by(\'date\')\n\n*Example* You can efficiently order using multiple fields by using a\n[compound index](http://www.rethinkdb.com/docs/secondary-indexes/python/).\n\nOrder by date and title.\n\n r.table(\'posts\').order_by(index=\'date_and_title\').run(conn)\n\nThe index must have been previously created with [index_create](http://rethinkdb.com/api/python/index_create/).\n\n r.table(\'posts\').index_create(\'date_and_title\', lambda post:\n [post["date"], post["title"]]).run(conn)\n\n_Note_: You cannot specify multiple orders in a compound index. See [issue #2306](https://github.com/rethinkdb/rethinkdb/issues/2306)\nto track progress.\n\n*Example* If you have a sequence with fewer documents than the `array_limit`, you can order it\nby multiple fields without an index.\n\n r.table(\'small_table\').order_by(\'date\', r.desc(\'title\'))\n\n*Example* Notice that an index ordering always has highest\nprecedence. The following query orders posts by date, and if multiple\nposts were published on the same date, they will be ordered by title.\n\n r.table(\'post\').order_by(\'title\', index=\'date\').run(conn)\n*Example* You can use [nested field](http://rethinkdb.com/docs/cookbook/python/#filtering-based-on-nested-fields) syntax to sort on fields from subdocuments. (You can also create indexes on nested fields using this syntax with `index_create`.)\n\n r.table(\'user\').order_by(lambda user: user[\'group\'][\'id\']).run(conn)\n\n*Example* You can efficiently order data on arbitrary expressions using indexes.\n\n r.table(\'posts\').order_by(index=\'votes\').run(conn)\n\nThe index must have been previously created with [index_create](http://rethinkdb.com/api/ruby/index_create/).\n\n r.table(\'posts\').index_create(\'votes\', lambda post:\n post["upvotes"]-post["downvotes"]\n ).run(conn)\n\n*Example* If you have a sequence with fewer documents than the `array_limit`, you can order it with an arbitrary function directly.\n\n r.table(\'small_table\').order_by(lambda doc:\n doc[\'upvotes\']-doc[\'downvotes\']\n );\n\nYou can also select a descending ordering:\n\n r.table(\'small_table\').order_by(r.desc(lambda doc:\n doc[\'upvotes\']-doc[\'downvotes\']\n ));\n\n*Example* Ordering after a `between` command can be done as long as the same index is being used.\n\n r.table("posts").between(r.time(2013, 1, 1, \'+00:00\'), r.time(2013, 1, 1, \'+00:00\'), index=\'date\')\n .order_by(index=\'date\').run(conn);\n\n'),
+ (rethinkdb.ast.RqlQuery.sample, b"sequence.sample(number) -> selection\nstream.sample(number) -> array\narray.sample(number) -> array\n\nSelect a given number of elements from a sequence with uniform random distribution. Selection is done without replacement.\n\nIf the sequence has less than the requested number of elements (i.e., calling `sample(10)` on a sequence with only five elements), `sample` will return the entire sequence in a random order.\n\n*Example* Select 3 random heroes.\n\n r.table('marvel').sample(3).run(conn)\n"),
+ (rethinkdb.ast.RqlQuery.skip, b"sequence.skip(n) -> stream\narray.skip(n) -> array\n\nSkip a number of elements from the head of the sequence.\n\n*Example* Here in conjunction with `order_by` we choose to ignore the most successful heroes.\n\n r.table('marvel').order_by('successMetric').skip(10).run(conn)\n\n"),
+ (rethinkdb.ast.RqlQuery.slice, b"selection.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> selection\nstream.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> stream\narray.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> array\nbinary.slice(start_index[, end_index, left_bound='closed', right_bound='open']) -> binary\n\nReturn the elements of a sequence within the specified range.\n\n`slice` returns the range between `start_index` and `end_index`. If only `start_index` is specified, `slice` returns the range from that index to the end of the sequence. Specify `left_bound` or `right_bound` as `open` or `closed` to indicate whether to include that endpoint of the range by default: `closed` returns that endpoint, while `open` does not. By default, `left_bound` is closed and `right_bound` is open, so the range `(10,13)` will return the tenth, eleventh and twelfth elements in the sequence.\n\nIf `end_index` is past the end of the sequence, all elements from `start_index` to the end of the sequence will be returned. If `start_index` is past the end of the sequence or `end_index` is less than `start_index`, a zero-element sequence will be returned (although see below for negative `end_index` values). An error will be raised on a negative `start_index`.\n\nA negative `end_index` is allowed with arrays; in that case, the returned range counts backward from the array's end. That is, the range of `(2,-1)` returns the second element through the next-to-last element of the range. A negative `end_index` is not allowed with a stream. (An `end_index` of &minus;1 *is* allowed with a stream if `right_bound` is closed; this behaves as if no `end_index` was specified.)\n\nIf `slice` is used with a [binary](http://rethinkdb.com/api/python/binary) object, the indexes refer to byte positions within the object. That is, the range `(10,20)` will refer to the 10th byte through the 19th byte.\n\nIf you are only specifying the indexes and not the bounding options, you may use Python's slice operator as a shorthand: `[start_index:end_index]`.\n\n**Example:** Return the fourth, fifth and sixth youngest players. (The youngest player is at index 0, so those are elements 3&ndash;5.)\n\n r.table('players').order_by(index='age').slice(3,6).run(conn)\n\nOr, using Python's slice operator:\n\n r.table('players').filter({'class': 'amateur'})[10:20].run(conn)\n\n**Example:** Return all but the top three players who have a red flag.\n\n r.table('players').filter({'flag': 'red'}).order_by(index=r.desc('score')).slice(3).run(conn)\n\n**Example:** Return holders of tickets `X` through `Y`, assuming tickets are numbered sequentially. We want to include ticket `Y`.\n\n r.table('users').order_by(index='ticket').slice(x, y, right_bound='closed').run(conn)\n\n**Example:** Return the elements of an array from the second through two from the end (that is, not including the last two).\n\n r.expr([0,1,2,3,4,5]).slice(2,-2).run(conn)\n\nResult:\n\n [2,3]\n"),
+ (rethinkdb.ast.RqlQuery.union, b"stream.union(sequence[, sequence, ...]) -> stream\narray.union(sequence[, sequence, ...]) -> array\n\nConcatenate two or more sequences.\n\n*Example* Construct a stream of all heroes.\n\n r.table('marvel').union(r.table('dc')).run(conn)\n\n*Example* Combine four arrays into one.\n\n r.expr([1, 2]).union([3, 4], [5, 6], [7, 8, 9]).run(conn)\n \n [1, 2, 3, 4, 5, 6, 7, 8, 9]\n"),
+ (rethinkdb.ast.RqlQuery.with_fields, b"sequence.with_fields([selector1, selector2...]) -> stream\narray.with_fields([selector1, selector2...]) -> array\n\nPlucks one or more attributes from a sequence of objects, filtering out any objects in the sequence that do not have the specified fields. Functionally, this is identical to `has_fields` followed by `pluck` on a sequence.\n\n*Example* Get a list of users and their posts, excluding any users who have not made any posts.\n\nExisting table structure:\n\n [\n { 'id': 1, 'user': 'bob', 'email': 'bob@foo.com', 'posts': [ 1, 4, 5 ] },\n { 'id': 2, 'user': 'george', 'email': 'george@foo.com' },\n { 'id': 3, 'user': 'jane', 'email': 'jane@foo.com', 'posts': [ 2, 3, 6 ] }\n ]\n\nCommand and output:\n\n r.table('users').with_fields('id', 'user', 'posts').run(conn)\n \n [\n { 'id': 1, 'user': 'bob', 'posts': [ 1, 4, 5 ] },\n { 'id': 3, 'user': 'jane', 'posts': [ 2, 3, 6 ] }\n ]\n\n*Example* Use the [nested field syntax](http://rethinkdb.com/docs/nested-fields/) to get a list of users with cell phone numbers in their contacts.\n\n r.table('users').with_fields('id', 'user', {contact: {'phone': 'work'}).run(conn)\n"),
+ (rethinkdb.ast.Table.delete, b'table.delete([durability="hard", return_changes=False])\n -> object\nselection.delete([durability="hard", return_changes=False])\n -> object\nsingleSelection.delete([durability="hard", return_changes=False])\n -> object\n\nDelete one or more documents from a table.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the\ntable or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). \nIn soft durability mode RethinkDB will acknowledge the write immediately after\nreceiving it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n\nDelete returns an object that contains the following attributes:\n\n- `deleted`: the number of documents that were deleted.\n- `skipped`: the number of documents that were skipped. \nFor example, if you attempt to delete a batch of documents, and another concurrent query\ndeletes some of those documents first, they will be counted as skipped.\n- `errors`: the number of errors encountered while performing the delete.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `inserted`, `replaced`, and `unchanged`: all 0 for a delete operation.\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `delete` operation. Each object will have two keys: `{"new_val": None, "old_val": <old value>}`.\n\n*Example* Delete a single document from the table `comments`.\n\n r.table("comments").get("7eab9e63-73f1-4f33-8ce4-95cbea626f59").delete().run(conn)\n\n*Example* Delete all documents from the table `comments`.\n\n r.table("comments").delete().run(conn)\n\n*Example* Delete all comments where the field `id_post` is `3`.\n\n r.table("comments").filter({"id_post": 3}).delete().run(conn)\n\n*Example* Delete a single document from the table `comments` and return its value.\n\n r.table("comments").get("7eab9e63-73f1-4f33-8ce4-95cbea626f59").delete(return_changes=True).run(conn)\n\nThe result will look like:\n\n {\n "deleted": 1,\n "errors": 0,\n "inserted": 0,\n "changes": [\n {\n "new_val": None,\n "old_val": {\n "id": "7eab9e63-73f1-4f33-8ce4-95cbea626f59",\n "author": "William",\n "comment": "Great post",\n "id_post": 3\n }\n }\n ],\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\n*Example* Delete all documents from the table `comments` without waiting for the\noperation to be flushed to disk.\n\n r.table("comments").delete(durability="soft"}).run(conn)\n'),
+ (rethinkdb.ast.Table.insert, b'table.insert(object | [object1, object2, ...][, durability="hard", return_changes=False, conflict="error"])\n -> object\n\nInsert documents into a table. Accepts a single document or an array of\ndocuments.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the table or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). In soft durability mode RethinkDB will acknowledge the write immediately after receiving and caching it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n- `conflict`: Determine handling of inserting documents with the same primary key as existing entries. Possible values are `"error"`, `"replace"` or `"update"`.\n - `"error"`: Do not insert the new document and record the conflict as an error. This is the default.\n - `"replace"`: [Replace](http://rethinkdb.com/api/python/replace/) the old document in its entirety with the new one.\n - `"update"`: [Update](http://rethinkdb.com/api/python/update/) fields of the old document with fields from the new one.\n\nInsert returns an object that contains the following attributes:\n\n- `inserted`: the number of documents successfully inserted.\n- `replaced`: the number of documents updated when `conflict` is set to `"replace"` or `"update"`.\n- `unchanged`: the number of documents whose fields are identical to existing documents with the same primary key when `conflict` is set to `"replace"` or `"update"`.\n- `errors`: the number of errors encountered while performing the insert.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `deleted` and `skipped`: 0 for an insert operation.\n- `generated_keys`: a list of generated primary keys for inserted documents whose primary keys were not specified (capped to 100,000).\n- `warnings`: if the field `generated_keys` is truncated, you will get the warning _"Too many generated keys (&lt;X&gt;), array truncated to 100000."_.\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `insert` operation. Each object will have two keys: `{"new_val": <new value>, "old_val": None}`.\n\n*Example* Insert a document into the table `posts`.\n\n r.table("posts").insert({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Dolor sit amet"\n }).run(conn)\n\nThe result will be:\n\n {\n "deleted": 0,\n "errors": 0,\n "inserted": 1,\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\n*Example* Insert a document without a defined primary key into the table `posts` where the\nprimary key is `id`.\n\n r.table("posts").insert({\n "title": "Lorem ipsum",\n "content": "Dolor sit amet"\n }).run(conn)\n\nRethinkDB will generate a primary key and return it in `generated_keys`.\n\n {\n "deleted": 0,\n "errors": 0,\n "generated_keys": [\n "dd782b64-70a7-43e4-b65e-dd14ae61d947"\n ],\n "inserted": 1,\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\nRetrieve the document you just inserted with:\n\n r.table("posts").get("dd782b64-70a7-43e4-b65e-dd14ae61d947").run(conn)\n\nAnd you will get back:\n\n {\n "id": "dd782b64-70a7-43e4-b65e-dd14ae61d947",\n "title": "Lorem ipsum",\n "content": "Dolor sit amet",\n }\n\n*Example* Insert multiple documents into the table `users`.\n\n r.table("users").insert([\n {"id": "william", "email": "william@rethinkdb.com"},\n {"id": "lara", "email": "lara@rethinkdb.com"}\n ]).run(conn)\n\n*Example* Insert a document into the table `users`, replacing the document if the document\nalready exists. \n\n r.table("users").insert(\n {"id": "william", "email": "william@rethinkdb.com"},\n conflict="error"\n ).run(conn)\n\n*Example* Copy the documents from `posts` to `posts_backup`.\n\n r.table("posts_backup").insert( r.table("posts") ).run(conn)\n\n*Example* Get back a copy of the inserted document (with its generated primary key).\n\n r.table("posts").insert(\n {"title": "Lorem ipsum", "content": "Dolor sit amet"},\n return_changes=True\n ).run(conn)\n\nThe result will be\n\n {\n "deleted": 0,\n "errors": 0,\n "generated_keys": [\n "dd782b64-70a7-43e4-b65e-dd14ae61d947"\n ],\n "inserted": 1,\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0,\n "changes": [\n {\n "old_val": None,\n "new_val": {\n "id": "dd782b64-70a7-43e4-b65e-dd14ae61d947",\n "title": "Lorem ipsum",\n "content": "Dolor sit amet"\n }\n }\n ]\n }\n'),
+ (rethinkdb.ast.Table.replace, b'table.replace(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nselection.replace(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nsingleSelection.replace(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\n\nReplace documents in a table. Accepts a JSON document or a ReQL expression, and replaces\nthe original document with the new one. The new document must have the same primary key\nas the original document.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the\ntable or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). \nIn soft durability mode RethinkDB will acknowledge the write immediately after\nreceiving it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n- `non_atomic`: if set to `True`, executes the replacement and distributes the result to replicas in a non-atomic fashion. This flag is required to perform non-deterministic updates, such as those that require reading data from another table.\n\nReplace returns an object that contains the following attributes:\n\n- `replaced`: the number of documents that were replaced\n- `unchanged`: the number of documents that would have been modified, except that the\nnew value was the same as the old value\n- `inserted`: the number of new documents added. You can have new documents inserted if\nyou do a point-replace on a key that isn\'t in the table or you do a replace on a\nselection and one of the documents you are replacing has been deleted\n- `deleted`: the number of deleted documents when doing a replace with `None`\n- `errors`: the number of errors encountered while performing the replace.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `skipped`: 0 for a replace operation\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `replace` operation. Each object will have two keys: `{"new_val": <new value>, "old_val": <old value>}`.\n\n*Example* Replace the document with the primary key `1`.\n\n r.table("posts").get(1).replace({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Aleas jacta est",\n "status": "draft"\n }).run(conn)\n\n*Example* Remove the field `status` from all posts.\n\n r.table("posts").replace(lambda post:\n post.without("status")\n ).run(conn)\n\n*Example* Remove all the fields that are not `id`, `title` or `content`.\n\n r.table("posts").replace(lambda post:\n post.pluck("id", "title", "content")\n ).run(conn)\n\n*Example* Replace the document with the primary key `1` using soft durability.\n\n r.table("posts").get(1).replace({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Aleas jacta est",\n "status": "draft"\n }, durability="soft").run(conn)\n\n*Example* Replace the document with the primary key `1` and return the values of the document before\nand after the replace operation.\n\n r.table("posts").get(1).replace({\n "id": 1,\n "title": "Lorem ipsum",\n "content": "Aleas jacta est",\n "status": "published"\n }, return_changes=True).run(conn)\n\nThe result will have a `changes` field:\n\n {\n "deleted": 0,\n "errors": 0,\n "inserted": 0,\n "changes": [\n {\n "new_val": {\n "id":1,\n "title": "Lorem ipsum"\n "content": "Aleas jacta est",\n "status": "published",\n },\n "old_val": {\n "id":1,\n "title": "Lorem ipsum"\n "content": "TODO",\n "status": "draft",\n "author": "William",\n }\n }\n ], \n "replaced": 1,\n "skipped": 0,\n "unchanged": 0\n }\n'),
+ (rethinkdb.ast.Table.sync, b'table.sync() -> object\n\n`sync` ensures that writes on a given table are written to permanent storage. Queries\nthat specify soft durability (`durability=\'soft\'`) do not give such guarantees, so\n`sync` can be used to ensure the state of these queries. A call to `sync` does not return\nuntil all previous writes to the table are persisted.\n\nIf successful, the operation returns an object: `{"synced": 1}`.\n\n*Example* After having updated multiple heroes with soft durability, we now want to wait\nuntil these changes are persisted.\n\n r.table(\'marvel\').sync().run(conn)\n\n'),
+ (rethinkdb.ast.Table.update, b'table.update(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nselection.update(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\nsingleSelection.update(object | expr[, durability="hard", return_changes=False, non_atomic=False])\n -> object\n\nUpdate JSON documents in a table. Accepts a JSON document, a ReQL expression, or a combination of the two.\n\nThe optional arguments are:\n\n- `durability`: possible values are `hard` and `soft`. This option will override the table or query\'s durability setting (set in [run](http://rethinkdb.com/api/python/run/)). In soft durability mode RethinkDB will acknowledge the write immediately after receiving it, but before the write has been committed to disk.\n- `return_changes`:\n - `True`: return a `changes` array consisting of `old_val`/`new_val` objects describing the changes made, only including the documents actually updated.\n - `False`: do not return a `changes` array (the default).\n - `"always"`: behave as `True`, but include all documents the command tried to update whether or not the update was successful. (This was the behavior of `True` pre-2.0.)\n- `non_atomic`: if set to `True`, executes the update and distributes the result to replicas in a non-atomic fashion. This flag is required to perform non-deterministic updates, such as those that require reading data from another table.\n\nUpdate returns an object that contains the following attributes:\n\n- `replaced`: the number of documents that were updated.\n- `unchanged`: the number of documents that would have been modified except the new value was the same as the old value.\n- `skipped`: the number of documents that were skipped because the document didn\'t exist.\n- `errors`: the number of errors encountered while performing the update.\n- `first_error`: If errors were encountered, contains the text of the first error.\n- `deleted` and `inserted`: 0 for an update operation.\n- `changes`: if `return_changes` is set to `True`, this will be an array of objects, one for each objected affected by the `update` operation. Each object will have two keys: `{"new_val": <new value>, "old_val": <old value>}`.\n\n*Example* Update the status of the post with `id` of `1` to `published`.\n\n r.table("posts").get(1).update({"status": "published"}).run(conn)\n\n*Example* Update the status of all posts to `published`.\n\n r.table("posts").update({"status": "published"}).run(conn)\n\n*Example* Update the status of all the posts written by William.\n\n r.table("posts").filter({"author": "William"}).update({"status": "published"}).run(conn)\n\n*Example* Increment the field `view` with `id` of `1`.\nThis query will throw an error if the field `views` doesn\'t exist.\n\n r.table("posts").get(1).update({\n "views": r.row["views"]+1\n }).run(conn)\n\n*Example* Increment the field `view` of the post with `id` of `1`.\nIf the field `views` does not exist, it will be set to `0`.\n\n r.table("posts").update({\n "views": (r.row["views"]+1).default(0)\n }).run(conn)\n\n*Example* Perform a conditional update. \nIf the post has more than 100 views, set the `type` of a post to `hot`, else set it to `normal`.\n\n r.table("posts").get(1).update(lambda post:\n r.branch(\n post["views"] > 100,\n {"type": "hot"},\n {"type": "normal"}\n )\n ).run(conn)\n\n*Example* Update the field `num_comments` with the result of a sub-query. Because this update is not atomic, you must pass the `non_atomic` flag.\n\n r.table("posts").get(1).update({\n "num_comments": r.table("comments").filter({"id_post": 1}).count()\n }, non_atomic=True).run(conn)\n\nIf you forget to specify the `non_atomic` flag, you will get a `RqlRuntimeError`:\n\nRqlRuntimeError: Could not prove function deterministic. Maybe you want to use the non_atomic flag? \n\n*Example* Update the field `num_comments` with a random value between 0 and 100. This update cannot be proven deterministic because of `r.js` (and in fact is not), so you must pass the `non_atomic` flag.\n\n r.table("posts").get(1).update({\n "num_comments": r.js("Math.floor(Math.random()*100)")\n }, non_atomic=True).run(conn)\n\n*Example* Update the status of the post with `id` of `1` using soft durability.\n\n r.table("posts").get(1).update({status: "published"}, durability="soft").run(conn)\n\n*Example* Increment the field `views` and return the values of the document before and after the update operation.\n\n r.table("posts").get(1).update({\n "views": r.row["views"]+1\n }, return_changes=True).run(conn)\n\nThe result will now include a `changes` field:\n\n {\n "deleted": 1,\n "errors": 0,\n "inserted": 0,\n "changes": [\n {\n "new_val": {\n "id": 1,\n "author": "Julius_Caesar",\n "title": "Commentarii de Bello Gallico",\n "content": "Aleas jacta est",\n "views": 207\n },\n "old_val": {\n "id": 1,\n "author": "Julius_Caesar",\n "title": "Commentarii de Bello Gallico",\n "content": "Aleas jacta est",\n "views": 206\n }\n }\n ],\n "replaced": 0,\n "skipped": 0,\n "unchanged": 0\n }\n\nThe `update` command supports RethinkDB\'s [nested field][nf] syntax to update subdocuments. Consider a user table with contact information in this format:\n\n[nf]: /docs/nested-fields/python\n\n {\n "id": 10001,\n "name": "Bob Smith",\n "contact": {\n "phone": {\n "work": "408-555-1212",\n "home": "408-555-1213",\n "cell": "408-555-1214"\n },\n "email": {\n "work": "bob@smith.com",\n "home": "bobsmith@example.com",\n "other": "bobbys@moosecall.net"\n },\n "im": {\n "skype": "Bob Smith",\n "aim": "bobmoose",\n "icq": "nobodyremembersicqnumbers"\n }\n },\n "notes": [\n {\n "date": r.time(2014,1,1,\'Z\'),\n "from": "John Doe",\n "subject": "My name is even more boring than Bob\'s"\n },\n {\n "date": r.time(2014,2,2,\'Z\'),\n "from": "Bob Smith Sr",\n "subject": "Happy Second of February"\n }\n ]\n }\n\n*Example* Update Bob Smith\'s cell phone number.\n\n r.table("users").get(10001).update(\n {"contact": {"phone": {"cell": "408-555-4242"}}}\n ).run(conn)\n\n*Example* Add another note to Bob Smith\'s record.\n\n new_note = {\n "date": r.now(),\n "from": "Inigo Montoya",\n "subject": "You killed my father"\n }\n r.table("users").get(10001).update(\n {"notes": r.row["notes"].append(new_note)}\n ).run(conn)\n\n*Example* Send a note to every user with an ICQ number.\n\n icq_note = {\n "date": r.now(),\n "from": "Admin",\n "subject": "Welcome to the future"\n }\n r.table("users").filter(\n r.row.has_fields({"contact": {"im": "icq"}})\n ).update(\n {"notes": r.row["notes"].append(icq_note)}\n ).run(conn)\n\n*Example* Replace all of Bob\'s IM records. Normally, `update` will merge nested documents together; to replace the entire `"im"` document, use the [literal][] command.\n\n[literal]: /api/python/literal/\n\n r.table(\'users\').get(10001).update(\n {"contact": {"im": r.literal({"aim": "themoosemeister"})}}\n ).run(conn)\n'),
+]
+
+for function, text in docsSource:
+ try:
+ text = str(text.decode('utf-8'))
+ except UnicodeEncodeError:
+ pass
+ if hasattr(function, "__func__"):
+ function.__func__.__doc__ = text
+ else:
+ function.__doc__ = text
diff --git a/ext/librethinkdbxx/reql/ql2.proto b/ext/librethinkdbxx/reql/ql2.proto
new file mode 100644
index 00000000..e40c5be5
--- /dev/null
+++ b/ext/librethinkdbxx/reql/ql2.proto
@@ -0,0 +1,843 @@
+////////////////////////////////////////////////////////////////////////////////
+// THE HIGH-LEVEL VIEW //
+////////////////////////////////////////////////////////////////////////////////
+
+// Process: When you first open a connection, send the magic number
+// for the version of the protobuf you're targeting (in the [Version]
+// enum). This should **NOT** be sent as a protobuf; just send the
+// little-endian 32-bit integer over the wire raw. This number should
+// only be sent once per connection.
+
+// The magic number shall be followed by an authorization key. The
+// first 4 bytes are the length of the key to be sent as a little-endian
+// 32-bit integer, followed by the key string. Even if there is no key,
+// an empty string should be sent (length 0 and no data).
+
+// Following the authorization key, the client shall send a magic number
+// for the communication protocol they want to use (in the [Protocol]
+// enum). This shall be a little-endian 32-bit integer.
+
+// The server will then respond with a NULL-terminated string response.
+// "SUCCESS" indicates that the connection has been accepted. Any other
+// response indicates an error, and the response string should describe
+// the error.
+
+// Next, for each query you want to send, construct a [Query] protobuf
+// and serialize it to a binary blob. Send the blob's size to the
+// server encoded as a little-endian 32-bit integer, followed by the
+// blob itself. You will recieve a [Response] protobuf back preceded
+// by its own size, once again encoded as a little-endian 32-bit
+// integer. You can see an example exchange below in **EXAMPLE**.
+
+// A query consists of a [Term] to evaluate and a unique-per-connection
+// [token].
+
+// Tokens are used for two things:
+// * Keeping track of which responses correspond to which queries.
+// * Batched queries. Some queries return lots of results, so we send back
+// batches of <1000, and you need to send a [CONTINUE] query with the same
+// token to get more results from the original query.
+////////////////////////////////////////////////////////////////////////////////
+
+message VersionDummy { // We need to wrap it like this for some
+ // non-conforming protobuf libraries
+ // This enum contains the magic numbers for your version. See **THE HIGH-LEVEL
+ // VIEW** for what to do with it.
+ enum Version {
+ V0_1 = 0x3f61ba36;
+ V0_2 = 0x723081e1; // Authorization key during handshake
+ V0_3 = 0x5f75e83e; // Authorization key and protocol during handshake
+ V0_4 = 0x400c2d20; // Queries execute in parallel
+ V1_0 = 0x34c2bdc3; // Users and permissions
+ }
+
+ // The protocol to use after the handshake, specified in V0_3
+ enum Protocol {
+ PROTOBUF = 0x271ffc41;
+ JSON = 0x7e6970c7;
+ }
+}
+
+// You send one of:
+// * A [START] query with a [Term] to evaluate and a unique-per-connection token.
+// * A [CONTINUE] query with the same token as a [START] query that returned
+// [SUCCESS_PARTIAL] in its [Response].
+// * A [STOP] query with the same token as a [START] query that you want to stop.
+// * A [NOREPLY_WAIT] query with a unique per-connection token. The server answers
+// with a [WAIT_COMPLETE] [Response].
+// * A [SERVER_INFO] query. The server answers with a [SERVER_INFO] [Response].
+message Query {
+ enum QueryType {
+ START = 1; // Start a new query.
+ CONTINUE = 2; // Continue a query that returned [SUCCESS_PARTIAL]
+ // (see [Response]).
+ STOP = 3; // Stop a query partway through executing.
+ NOREPLY_WAIT = 4; // Wait for noreply operations to finish.
+ SERVER_INFO = 5; // Get server information.
+ }
+ optional QueryType type = 1;
+ // A [Term] is how we represent the operations we want a query to perform.
+ optional Term query = 2; // only present when [type] = [START]
+ optional int64 token = 3;
+ // This flag is ignored on the server. `noreply` should be added
+ // to `global_optargs` instead (the key "noreply" should map to
+ // either true or false).
+ optional bool OBSOLETE_noreply = 4 [default = false];
+
+ // If this is set to [true], then [Datum] values will sometimes be
+ // of [DatumType] [R_JSON] (see below). This can provide enormous
+ // speedups in languages with poor protobuf libraries.
+ optional bool accepts_r_json = 5 [default = false];
+
+ message AssocPair {
+ optional string key = 1;
+ optional Term val = 2;
+ }
+ repeated AssocPair global_optargs = 6;
+}
+
+// A backtrace frame (see `backtrace` in Response below)
+message Frame {
+ enum FrameType {
+ POS = 1; // Error occurred in a positional argument.
+ OPT = 2; // Error occurred in an optional argument.
+ }
+ optional FrameType type = 1;
+ optional int64 pos = 2; // The index of the positional argument.
+ optional string opt = 3; // The name of the optional argument.
+}
+message Backtrace {
+ repeated Frame frames = 1;
+}
+
+// You get back a response with the same [token] as your query.
+message Response {
+ enum ResponseType {
+ // These response types indicate success.
+ SUCCESS_ATOM = 1; // Query returned a single RQL datatype.
+ SUCCESS_SEQUENCE = 2; // Query returned a sequence of RQL datatypes.
+ SUCCESS_PARTIAL = 3; // Query returned a partial sequence of RQL
+ // datatypes. If you send a [CONTINUE] query with
+ // the same token as this response, you will get
+ // more of the sequence. Keep sending [CONTINUE]
+ // queries until you get back [SUCCESS_SEQUENCE].
+ WAIT_COMPLETE = 4; // A [NOREPLY_WAIT] query completed.
+ SERVER_INFO = 5; // The data for a [SERVER_INFO] request. This is
+ // the same as `SUCCESS_ATOM` except that there will
+ // never be profiling data.
+
+ // These response types indicate failure.
+ CLIENT_ERROR = 16; // Means the client is buggy. An example is if the
+ // client sends a malformed protobuf, or tries to
+ // send [CONTINUE] for an unknown token.
+ COMPILE_ERROR = 17; // Means the query failed during parsing or type
+ // checking. For example, if you pass too many
+ // arguments to a function.
+ RUNTIME_ERROR = 18; // Means the query failed at runtime. An example is
+ // if you add together two values from a table, but
+ // they turn out at runtime to be booleans rather
+ // than numbers.
+ }
+ optional ResponseType type = 1;
+
+ // If `ResponseType` is `RUNTIME_ERROR`, this may be filled in with more
+ // information about the error.
+ enum ErrorType {
+ INTERNAL = 1000000;
+ RESOURCE_LIMIT = 2000000;
+ QUERY_LOGIC = 3000000;
+ NON_EXISTENCE = 3100000;
+ OP_FAILED = 4100000;
+ OP_INDETERMINATE = 4200000;
+ USER = 5000000;
+ PERMISSION_ERROR = 6000000;
+ }
+ optional ErrorType error_type = 7;
+
+ // ResponseNotes are used to provide information about the query
+ // response that may be useful for people writing drivers or ORMs.
+ // Currently all the notes we send indicate that a stream has certain
+ // special properties.
+ enum ResponseNote {
+ // The stream is a changefeed stream (e.g. `r.table('test').changes()`).
+ SEQUENCE_FEED = 1;
+ // The stream is a point changefeed stream
+ // (e.g. `r.table('test').get(0).changes()`).
+ ATOM_FEED = 2;
+ // The stream is an order_by_limit changefeed stream
+ // (e.g. `r.table('test').order_by(index: 'id').limit(5).changes()`).
+ ORDER_BY_LIMIT_FEED = 3;
+ // The stream is a union of multiple changefeed types that can't be
+ // collapsed to a single type
+ // (e.g. `r.table('test').changes().union(r.table('test').get(0).changes())`).
+ UNIONED_FEED = 4;
+ // The stream is a changefeed stream and includes notes on what state
+ // the changefeed stream is in (e.g. objects of the form `{state:
+ // 'initializing'}`).
+ INCLUDES_STATES = 5;
+ }
+ repeated ResponseNote notes = 6;
+
+ optional int64 token = 2; // Indicates what [Query] this response corresponds to.
+
+ // [response] contains 1 RQL datum if [type] is [SUCCESS_ATOM] or
+ // [SERVER_INFO]. [response] contains many RQL data if [type] is
+ // [SUCCESS_SEQUENCE] or [SUCCESS_PARTIAL]. [response] contains 1
+ // error message (of type [R_STR]) in all other cases.
+ repeated Datum response = 3;
+
+ // If [type] is [CLIENT_ERROR], [TYPE_ERROR], or [RUNTIME_ERROR], then a
+ // backtrace will be provided. The backtrace says where in the query the
+ // error occurred. Ideally this information will be presented to the user as
+ // a pretty-printed version of their query with the erroneous section
+ // underlined. A backtrace is a series of 0 or more [Frame]s, each of which
+ // specifies either the index of a positional argument or the name of an
+ // optional argument. (Those words will make more sense if you look at the
+ // [Term] message below.)
+ optional Backtrace backtrace = 4; // Contains n [Frame]s when you get back an error.
+
+ // If the [global_optargs] in the [Query] that this [Response] is a
+ // response to contains a key "profile" which maps to a static value of
+ // true then [profile] will contain a [Datum] which provides profiling
+ // information about the execution of the query. This field should be
+ // returned to the user along with the result that would normally be
+ // returned (a datum or a cursor). In official drivers this is accomplished
+ // by putting them inside of an object with "value" mapping to the return
+ // value and "profile" mapping to the profile object.
+ optional Datum profile = 5;
+}
+
+// A [Datum] is a chunk of data that can be serialized to disk or returned to
+// the user in a Response. Currently we only support JSON types, but we may
+// support other types in the future (e.g., a date type or an integer type).
+message Datum {
+ enum DatumType {
+ R_NULL = 1;
+ R_BOOL = 2;
+ R_NUM = 3; // a double
+ R_STR = 4;
+ R_ARRAY = 5;
+ R_OBJECT = 6;
+ // This [DatumType] will only be used if [accepts_r_json] is
+ // set to [true] in [Query]. [r_str] will be filled with a
+ // JSON encoding of the [Datum].
+ R_JSON = 7; // uses r_str
+ }
+ optional DatumType type = 1;
+ optional bool r_bool = 2;
+ optional double r_num = 3;
+ optional string r_str = 4;
+
+ repeated Datum r_array = 5;
+ message AssocPair {
+ optional string key = 1;
+ optional Datum val = 2;
+ }
+ repeated AssocPair r_object = 6;
+}
+
+// A [Term] is either a piece of data (see **Datum** above), or an operator and
+// its operands. If you have a [Datum], it's stored in the member [datum]. If
+// you have an operator, its positional arguments are stored in [args] and its
+// optional arguments are stored in [optargs].
+//
+// A note about type signatures:
+// We use the following notation to denote types:
+// arg1_type, arg2_type, argrest_type... -> result_type
+// So, for example, if we have a function `avg` that takes any number of
+// arguments and averages them, we might write:
+// NUMBER... -> NUMBER
+// Or if we had a function that took one number modulo another:
+// NUMBER, NUMBER -> NUMBER
+// Or a function that takes a table and a primary key of any Datum type, then
+// retrieves the entry with that primary key:
+// Table, DATUM -> OBJECT
+// Some arguments must be provided as literal values (and not the results of sub
+// terms). These are marked with a `!`.
+// Optional arguments are specified within curly braces as argname `:` value
+// type (e.x `{noreply:BOOL}`)
+// Many RQL operations are polymorphic. For these, alterantive type signatures
+// are separated by `|`.
+//
+// The RQL type hierarchy is as follows:
+// Top
+// DATUM
+// NULL
+// BOOL
+// NUMBER
+// STRING
+// OBJECT
+// SingleSelection
+// ARRAY
+// Sequence
+// ARRAY
+// Stream
+// StreamSelection
+// Table
+// Database
+// Function
+// Ordering - used only by ORDER_BY
+// Pathspec -- an object, string, or array that specifies a path
+// Error
+message Term {
+ enum TermType {
+ // A RQL datum, stored in `datum` below.
+ DATUM = 1;
+
+ MAKE_ARRAY = 2; // DATUM... -> ARRAY
+ // Evaluate the terms in [optargs] and make an object
+ MAKE_OBJ = 3; // {...} -> OBJECT
+
+ // * Compound types
+
+ // Takes an integer representing a variable and returns the value stored
+ // in that variable. It's the responsibility of the client to translate
+ // from their local representation of a variable to a unique _non-negative_
+ // integer for that variable. (We do it this way instead of letting
+ // clients provide variable names as strings to discourage
+ // variable-capturing client libraries, and because it's more efficient
+ // on the wire.)
+ VAR = 10; // !NUMBER -> DATUM
+ // Takes some javascript code and executes it.
+ JAVASCRIPT = 11; // STRING {timeout: !NUMBER} -> DATUM |
+ // STRING {timeout: !NUMBER} -> Function(*)
+ UUID = 169; // () -> DATUM
+
+ // Takes an HTTP URL and gets it. If the get succeeds and
+ // returns valid JSON, it is converted into a DATUM
+ HTTP = 153; // STRING {data: OBJECT | STRING,
+ // timeout: !NUMBER,
+ // method: STRING,
+ // params: OBJECT,
+ // header: OBJECT | ARRAY,
+ // attempts: NUMBER,
+ // redirects: NUMBER,
+ // verify: BOOL,
+ // page: FUNC | STRING,
+ // page_limit: NUMBER,
+ // auth: OBJECT,
+ // result_format: STRING,
+ // } -> STRING | STREAM
+
+ // Takes a string and throws an error with that message.
+ // Inside of a `default` block, you can omit the first
+ // argument to rethrow whatever error you catch (this is most
+ // useful as an argument to the `default` filter optarg).
+ ERROR = 12; // STRING -> Error | -> Error
+ // Takes nothing and returns a reference to the implicit variable.
+ IMPLICIT_VAR = 13; // -> DATUM
+
+ // * Data Operators
+ // Returns a reference to a database.
+ DB = 14; // STRING -> Database
+ // Returns a reference to a table.
+ TABLE = 15; // Database, STRING, {read_mode:STRING, identifier_format:STRING} -> Table
+ // STRING, {read_mode:STRING, identifier_format:STRING} -> Table
+ // Gets a single element from a table by its primary or a secondary key.
+ GET = 16; // Table, STRING -> SingleSelection | Table, NUMBER -> SingleSelection |
+ // Table, STRING -> NULL | Table, NUMBER -> NULL |
+ GET_ALL = 78; // Table, DATUM..., {index:!STRING} => ARRAY
+
+ // Simple DATUM Ops
+ EQ = 17; // DATUM... -> BOOL
+ NE = 18; // DATUM... -> BOOL
+ LT = 19; // DATUM... -> BOOL
+ LE = 20; // DATUM... -> BOOL
+ GT = 21; // DATUM... -> BOOL
+ GE = 22; // DATUM... -> BOOL
+ NOT = 23; // BOOL -> BOOL
+ // ADD can either add two numbers or concatenate two arrays.
+ ADD = 24; // NUMBER... -> NUMBER | STRING... -> STRING
+ SUB = 25; // NUMBER... -> NUMBER
+ MUL = 26; // NUMBER... -> NUMBER
+ DIV = 27; // NUMBER... -> NUMBER
+ MOD = 28; // NUMBER, NUMBER -> NUMBER
+
+ FLOOR = 183; // NUMBER -> NUMBER
+ CEIL = 184; // NUMBER -> NUMBER
+ ROUND = 185; // NUMBER -> NUMBER
+
+ // DATUM Array Ops
+ // Append a single element to the end of an array (like `snoc`).
+ APPEND = 29; // ARRAY, DATUM -> ARRAY
+ // Prepend a single element to the end of an array (like `cons`).
+ PREPEND = 80; // ARRAY, DATUM -> ARRAY
+ //Remove the elements of one array from another array.
+ DIFFERENCE = 95; // ARRAY, ARRAY -> ARRAY
+
+ // DATUM Set Ops
+ // Set ops work on arrays. They don't use actual sets and thus have
+ // performance characteristics you would expect from arrays rather than
+ // from sets. All set operations have the post condition that they
+ // array they return contains no duplicate values.
+ SET_INSERT = 88; // ARRAY, DATUM -> ARRAY
+ SET_INTERSECTION = 89; // ARRAY, ARRAY -> ARRAY
+ SET_UNION = 90; // ARRAY, ARRAY -> ARRAY
+ SET_DIFFERENCE = 91; // ARRAY, ARRAY -> ARRAY
+
+ SLICE = 30; // Sequence, NUMBER, NUMBER -> Sequence
+ SKIP = 70; // Sequence, NUMBER -> Sequence
+ LIMIT = 71; // Sequence, NUMBER -> Sequence
+ OFFSETS_OF = 87; // Sequence, DATUM -> Sequence | Sequence, Function(1) -> Sequence
+ CONTAINS = 93; // Sequence, (DATUM | Function(1))... -> BOOL
+
+ // Stream/Object Ops
+ // Get a particular field from an object, or map that over a
+ // sequence.
+ GET_FIELD = 31; // OBJECT, STRING -> DATUM
+ // | Sequence, STRING -> Sequence
+ // Return an array containing the keys of the object.
+ KEYS = 94; // OBJECT -> ARRAY
+ // Return an array containing the values of the object.
+ VALUES = 186; // OBJECT -> ARRAY
+ // Creates an object
+ OBJECT = 143; // STRING, DATUM, ... -> OBJECT
+ // Check whether an object contains all the specified fields,
+ // or filters a sequence so that all objects inside of it
+ // contain all the specified fields.
+ HAS_FIELDS = 32; // OBJECT, Pathspec... -> BOOL
+ // x.with_fields(...) <=> x.has_fields(...).pluck(...)
+ WITH_FIELDS = 96; // Sequence, Pathspec... -> Sequence
+ // Get a subset of an object by selecting some attributes to preserve,
+ // or map that over a sequence. (Both pick and pluck, polymorphic.)
+ PLUCK = 33; // Sequence, Pathspec... -> Sequence | OBJECT, Pathspec... -> OBJECT
+ // Get a subset of an object by selecting some attributes to discard, or
+ // map that over a sequence. (Both unpick and without, polymorphic.)
+ WITHOUT = 34; // Sequence, Pathspec... -> Sequence | OBJECT, Pathspec... -> OBJECT
+ // Merge objects (right-preferential)
+ MERGE = 35; // OBJECT... -> OBJECT | Sequence -> Sequence
+
+ // Sequence Ops
+ // Get all elements of a sequence between two values.
+ // Half-open by default, but the openness of either side can be
+ // changed by passing 'closed' or 'open for `right_bound` or
+ // `left_bound`.
+ BETWEEN_DEPRECATED = 36; // Deprecated version of between, which allows `null` to specify unboundedness
+ // With the newer version, clients should use `r.minval` and `r.maxval` for unboundedness
+ BETWEEN = 182; // StreamSelection, DATUM, DATUM, {index:!STRING, right_bound:STRING, left_bound:STRING} -> StreamSelection
+ REDUCE = 37; // Sequence, Function(2) -> DATUM
+ MAP = 38; // Sequence, Function(1) -> Sequence
+ // The arity of the function should be
+ // Sequence..., Function(sizeof...(Sequence)) -> Sequence
+
+ FOLD = 187; // Sequence, Datum, Function(2), {Function(3), Function(1)
+
+ // Filter a sequence with either a function or a shortcut
+ // object (see API docs for details). The body of FILTER is
+ // wrapped in an implicit `.default(false)`, and you can
+ // change the default value by specifying the `default`
+ // optarg. If you make the default `r.error`, all errors
+ // caught by `default` will be rethrown as if the `default`
+ // did not exist.
+ FILTER = 39; // Sequence, Function(1), {default:DATUM} -> Sequence |
+ // Sequence, OBJECT, {default:DATUM} -> Sequence
+ // Map a function over a sequence and then concatenate the results together.
+ CONCAT_MAP = 40; // Sequence, Function(1) -> Sequence
+ // Order a sequence based on one or more attributes.
+ ORDER_BY = 41; // Sequence, (!STRING | Ordering)..., {index: (!STRING | Ordering)} -> Sequence
+ // Get all distinct elements of a sequence (like `uniq`).
+ DISTINCT = 42; // Sequence -> Sequence
+ // Count the number of elements in a sequence, or only the elements that match
+ // a given filter.
+ COUNT = 43; // Sequence -> NUMBER | Sequence, DATUM -> NUMBER | Sequence, Function(1) -> NUMBER
+ IS_EMPTY = 86; // Sequence -> BOOL
+ // Take the union of multiple sequences (preserves duplicate elements! (use distinct)).
+ UNION = 44; // Sequence... -> Sequence
+ // Get the Nth element of a sequence.
+ NTH = 45; // Sequence, NUMBER -> DATUM
+ // do NTH or GET_FIELD depending on target object
+ BRACKET = 170; // Sequence | OBJECT, NUMBER | STRING -> DATUM
+ // OBSOLETE_GROUPED_MAPREDUCE = 46;
+ // OBSOLETE_GROUPBY = 47;
+
+ INNER_JOIN = 48; // Sequence, Sequence, Function(2) -> Sequence
+ OUTER_JOIN = 49; // Sequence, Sequence, Function(2) -> Sequence
+ // An inner-join that does an equality comparison on two attributes.
+ EQ_JOIN = 50; // Sequence, !STRING, Sequence, {index:!STRING} -> Sequence
+ ZIP = 72; // Sequence -> Sequence
+ RANGE = 173; // -> Sequence [0, +inf)
+ // NUMBER -> Sequence [0, a)
+ // NUMBER, NUMBER -> Sequence [a, b)
+
+ // Array Ops
+ // Insert an element in to an array at a given index.
+ INSERT_AT = 82; // ARRAY, NUMBER, DATUM -> ARRAY
+ // Remove an element at a given index from an array.
+ DELETE_AT = 83; // ARRAY, NUMBER -> ARRAY |
+ // ARRAY, NUMBER, NUMBER -> ARRAY
+ // Change the element at a given index of an array.
+ CHANGE_AT = 84; // ARRAY, NUMBER, DATUM -> ARRAY
+ // Splice one array in to another array.
+ SPLICE_AT = 85; // ARRAY, NUMBER, ARRAY -> ARRAY
+
+ // * Type Ops
+ // Coerces a datum to a named type (e.g. "bool").
+ // If you previously used `stream_to_array`, you should use this instead
+ // with the type "array".
+ COERCE_TO = 51; // Top, STRING -> Top
+ // Returns the named type of a datum (e.g. TYPE_OF(true) = "BOOL")
+ TYPE_OF = 52; // Top -> STRING
+
+ // * Write Ops (the OBJECTs contain data about number of errors etc.)
+ // Updates all the rows in a selection. Calls its Function with the row
+ // to be updated, and then merges the result of that call.
+ UPDATE = 53; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT |
+ // SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT |
+ // StreamSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT |
+ // SingleSelection, OBJECT, {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT
+ // Deletes all the rows in a selection.
+ DELETE = 54; // StreamSelection, {durability:STRING, return_changes:BOOL} -> OBJECT | SingleSelection -> OBJECT
+ // Replaces all the rows in a selection. Calls its Function with the row
+ // to be replaced, and then discards it and stores the result of that
+ // call.
+ REPLACE = 55; // StreamSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT | SingleSelection, Function(1), {non_atomic:BOOL, durability:STRING, return_changes:BOOL} -> OBJECT
+ // Inserts into a table. If `conflict` is replace, overwrites
+ // entries with the same primary key. If `conflict` is
+ // update, does an update on the entry. If `conflict` is
+ // error, or is omitted, conflicts will trigger an error.
+ INSERT = 56; // Table, OBJECT, {conflict:STRING, durability:STRING, return_changes:BOOL} -> OBJECT | Table, Sequence, {conflict:STRING, durability:STRING, return_changes:BOOL} -> OBJECT
+
+ // * Administrative OPs
+ // Creates a database with a particular name.
+ DB_CREATE = 57; // STRING -> OBJECT
+ // Drops a database with a particular name.
+ DB_DROP = 58; // STRING -> OBJECT
+ // Lists all the databases by name. (Takes no arguments)
+ DB_LIST = 59; // -> ARRAY
+ // Creates a table with a particular name in a particular
+ // database. (You may omit the first argument to use the
+ // default database.)
+ TABLE_CREATE = 60; // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT
+ // Database, STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT
+ // STRING, {primary_key:STRING, shards:NUMBER, replicas:NUMBER, primary_replica_tag:STRING} -> OBJECT
+ // STRING, {primary_key:STRING, shards:NUMBER, replicas:OBJECT, primary_replica_tag:STRING} -> OBJECT
+ // Drops a table with a particular name from a particular
+ // database. (You may omit the first argument to use the
+ // default database.)
+ TABLE_DROP = 61; // Database, STRING -> OBJECT
+ // STRING -> OBJECT
+ // Lists all the tables in a particular database. (You may
+ // omit the first argument to use the default database.)
+ TABLE_LIST = 62; // Database -> ARRAY
+ // -> ARRAY
+ // Returns the row in the `rethinkdb.table_config` or `rethinkdb.db_config` table
+ // that corresponds to the given database or table.
+ CONFIG = 174; // Database -> SingleSelection
+ // Table -> SingleSelection
+ // Returns the row in the `rethinkdb.table_status` table that corresponds to the
+ // given table.
+ STATUS = 175; // Table -> SingleSelection
+ // Called on a table, waits for that table to be ready for read/write operations.
+ // Called on a database, waits for all of the tables in the database to be ready.
+ // Returns the corresponding row or rows from the `rethinkdb.table_status` table.
+ WAIT = 177; // Table -> OBJECT
+ // Database -> OBJECT
+ // Generates a new config for the given table, or all tables in the given database
+ // The `shards` and `replicas` arguments are required. If `emergency_repair` is
+ // specified, it will enter a completely different mode of repairing a table
+ // which has lost half or more of its replicas.
+ RECONFIGURE = 176; // Database|Table, {shards:NUMBER, replicas:NUMBER [,
+ // dry_run:BOOLEAN]
+ // } -> OBJECT
+ // Database|Table, {shards:NUMBER, replicas:OBJECT [,
+ // primary_replica_tag:STRING,
+ // nonvoting_replica_tags:ARRAY,
+ // dry_run:BOOLEAN]
+ // } -> OBJECT
+ // Table, {emergency_repair:STRING, dry_run:BOOLEAN} -> OBJECT
+ // Balances the table's shards but leaves everything else the same. Can also be
+ // applied to an entire database at once.
+ REBALANCE = 179; // Table -> OBJECT
+ // Database -> OBJECT
+
+ // Ensures that previously issued soft-durability writes are complete and
+ // written to disk.
+ SYNC = 138; // Table -> OBJECT
+
+ // Set global, database, or table-specific permissions
+ GRANT = 188; // -> OBJECT
+ // Database -> OBJECT
+ // Table -> OBJECT
+
+ // * Secondary indexes OPs
+ // Creates a new secondary index with a particular name and definition.
+ INDEX_CREATE = 75; // Table, STRING, Function(1), {multi:BOOL} -> OBJECT
+ // Drops a secondary index with a particular name from the specified table.
+ INDEX_DROP = 76; // Table, STRING -> OBJECT
+ // Lists all secondary indexes on a particular table.
+ INDEX_LIST = 77; // Table -> ARRAY
+ // Gets information about whether or not a set of indexes are ready to
+ // be accessed. Returns a list of objects that look like this:
+ // {index:STRING, ready:BOOL[, progress:NUMBER]}
+ INDEX_STATUS = 139; // Table, STRING... -> ARRAY
+ // Blocks until a set of indexes are ready to be accessed. Returns the
+ // same values INDEX_STATUS.
+ INDEX_WAIT = 140; // Table, STRING... -> ARRAY
+ // Renames the given index to a new name
+ INDEX_RENAME = 156; // Table, STRING, STRING, {overwrite:BOOL} -> OBJECT
+
+ // * Control Operators
+ // Calls a function on data
+ FUNCALL = 64; // Function(*), DATUM... -> DATUM
+ // Executes its first argument, and returns its second argument if it
+ // got [true] or its third argument if it got [false] (like an `if`
+ // statement).
+ BRANCH = 65; // BOOL, Top, Top -> Top
+ // Returns true if any of its arguments returns true (short-circuits).
+ OR = 66; // BOOL... -> BOOL
+ // Returns true if all of its arguments return true (short-circuits).
+ AND = 67; // BOOL... -> BOOL
+ // Calls its Function with each entry in the sequence
+ // and executes the array of terms that Function returns.
+ FOR_EACH = 68; // Sequence, Function(1) -> OBJECT
+
+////////////////////////////////////////////////////////////////////////////////
+////////// Special Terms
+////////////////////////////////////////////////////////////////////////////////
+
+ // An anonymous function. Takes an array of numbers representing
+ // variables (see [VAR] above), and a [Term] to execute with those in
+ // scope. Returns a function that may be passed an array of arguments,
+ // then executes the Term with those bound to the variable names. The
+ // user will never construct this directly. We use it internally for
+ // things like `map` which take a function. The "arity" of a [Function] is
+ // the number of arguments it takes.
+ // For example, here's what `_X_.map{|x| x+2}` turns into:
+ // Term {
+ // type = MAP;
+ // args = [_X_,
+ // Term {
+ // type = Function;
+ // args = [Term {
+ // type = DATUM;
+ // datum = Datum {
+ // type = R_ARRAY;
+ // r_array = [Datum { type = R_NUM; r_num = 1; }];
+ // };
+ // },
+ // Term {
+ // type = ADD;
+ // args = [Term {
+ // type = VAR;
+ // args = [Term {
+ // type = DATUM;
+ // datum = Datum { type = R_NUM;
+ // r_num = 1};
+ // }];
+ // },
+ // Term {
+ // type = DATUM;
+ // datum = Datum { type = R_NUM; r_num = 2; };
+ // }];
+ // }];
+ // }];
+ FUNC = 69; // ARRAY, Top -> ARRAY -> Top
+
+ // Indicates to ORDER_BY that this attribute is to be sorted in ascending order.
+ ASC = 73; // !STRING -> Ordering
+ // Indicates to ORDER_BY that this attribute is to be sorted in descending order.
+ DESC = 74; // !STRING -> Ordering
+
+ // Gets info about anything. INFO is most commonly called on tables.
+ INFO = 79; // Top -> OBJECT
+
+ // `a.match(b)` returns a match object if the string `a`
+ // matches the regular expression `b`.
+ MATCH = 97; // STRING, STRING -> DATUM
+
+ // Change the case of a string.
+ UPCASE = 141; // STRING -> STRING
+ DOWNCASE = 142; // STRING -> STRING
+
+ // Select a number of elements from sequence with uniform distribution.
+ SAMPLE = 81; // Sequence, NUMBER -> Sequence
+
+ // Evaluates its first argument. If that argument returns
+ // NULL or throws an error related to the absence of an
+ // expected value (for instance, accessing a non-existent
+ // field or adding NULL to an integer), DEFAULT will either
+ // return its second argument or execute it if it's a
+ // function. If the second argument is a function, it will be
+ // passed either the text of the error or NULL as its
+ // argument.
+ DEFAULT = 92; // Top, Top -> Top
+
+ // Parses its first argument as a json string and returns it as a
+ // datum.
+ JSON = 98; // STRING -> DATUM
+ // Returns the datum as a JSON string.
+ // N.B.: we would really prefer this be named TO_JSON and that exists as
+ // an alias in Python and JavaScript drivers; however it conflicts with the
+ // standard `to_json` method defined by Ruby's standard json library.
+ TO_JSON_STRING = 172; // DATUM -> STRING
+
+ // Parses its first arguments as an ISO 8601 time and returns it as a
+ // datum.
+ ISO8601 = 99; // STRING -> PSEUDOTYPE(TIME)
+ // Prints a time as an ISO 8601 time.
+ TO_ISO8601 = 100; // PSEUDOTYPE(TIME) -> STRING
+
+ // Returns a time given seconds since epoch in UTC.
+ EPOCH_TIME = 101; // NUMBER -> PSEUDOTYPE(TIME)
+ // Returns seconds since epoch in UTC given a time.
+ TO_EPOCH_TIME = 102; // PSEUDOTYPE(TIME) -> NUMBER
+
+ // The time the query was received by the server.
+ NOW = 103; // -> PSEUDOTYPE(TIME)
+ // Puts a time into an ISO 8601 timezone.
+ IN_TIMEZONE = 104; // PSEUDOTYPE(TIME), STRING -> PSEUDOTYPE(TIME)
+ // a.during(b, c) returns whether a is in the range [b, c)
+ DURING = 105; // PSEUDOTYPE(TIME), PSEUDOTYPE(TIME), PSEUDOTYPE(TIME) -> BOOL
+ // Retrieves the date portion of a time.
+ DATE = 106; // PSEUDOTYPE(TIME) -> PSEUDOTYPE(TIME)
+ // x.time_of_day == x.date - x
+ TIME_OF_DAY = 126; // PSEUDOTYPE(TIME) -> NUMBER
+ // Returns the timezone of a time.
+ TIMEZONE = 127; // PSEUDOTYPE(TIME) -> STRING
+
+ // These access the various components of a time.
+ YEAR = 128; // PSEUDOTYPE(TIME) -> NUMBER
+ MONTH = 129; // PSEUDOTYPE(TIME) -> NUMBER
+ DAY = 130; // PSEUDOTYPE(TIME) -> NUMBER
+ DAY_OF_WEEK = 131; // PSEUDOTYPE(TIME) -> NUMBER
+ DAY_OF_YEAR = 132; // PSEUDOTYPE(TIME) -> NUMBER
+ HOURS = 133; // PSEUDOTYPE(TIME) -> NUMBER
+ MINUTES = 134; // PSEUDOTYPE(TIME) -> NUMBER
+ SECONDS = 135; // PSEUDOTYPE(TIME) -> NUMBER
+
+ // Construct a time from a date and optional timezone or a
+ // date+time and optional timezone.
+ TIME = 136; // NUMBER, NUMBER, NUMBER, STRING -> PSEUDOTYPE(TIME) |
+ // NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, STRING -> PSEUDOTYPE(TIME) |
+
+ // Constants for ISO 8601 days of the week.
+ MONDAY = 107; // -> 1
+ TUESDAY = 108; // -> 2
+ WEDNESDAY = 109; // -> 3
+ THURSDAY = 110; // -> 4
+ FRIDAY = 111; // -> 5
+ SATURDAY = 112; // -> 6
+ SUNDAY = 113; // -> 7
+
+ // Constants for ISO 8601 months.
+ JANUARY = 114; // -> 1
+ FEBRUARY = 115; // -> 2
+ MARCH = 116; // -> 3
+ APRIL = 117; // -> 4
+ MAY = 118; // -> 5
+ JUNE = 119; // -> 6
+ JULY = 120; // -> 7
+ AUGUST = 121; // -> 8
+ SEPTEMBER = 122; // -> 9
+ OCTOBER = 123; // -> 10
+ NOVEMBER = 124; // -> 11
+ DECEMBER = 125; // -> 12
+
+ // Indicates to MERGE to replace, or remove in case of an empty literal, the
+ // other object rather than merge it.
+ LITERAL = 137; // -> Merging
+ // JSON -> Merging
+
+ // SEQUENCE, STRING -> GROUPED_SEQUENCE | SEQUENCE, FUNCTION -> GROUPED_SEQUENCE
+ GROUP = 144;
+ SUM = 145;
+ AVG = 146;
+ MIN = 147;
+ MAX = 148;
+
+ // `str.split()` splits on whitespace
+ // `str.split(" ")` splits on spaces only
+ // `str.split(" ", 5)` splits on spaces with at most 5 results
+ // `str.split(nil, 5)` splits on whitespace with at most 5 results
+ SPLIT = 149; // STRING -> ARRAY | STRING, STRING -> ARRAY | STRING, STRING, NUMBER -> ARRAY | STRING, NULL, NUMBER -> ARRAY
+
+ UNGROUP = 150; // GROUPED_DATA -> ARRAY
+
+ // Takes a range of numbers and returns a random number within the range
+ RANDOM = 151; // NUMBER, NUMBER {float:BOOL} -> DATUM
+
+ CHANGES = 152; // TABLE -> STREAM
+ ARGS = 154; // ARRAY -> SPECIAL (used to splice arguments)
+
+ // BINARY is client-only at the moment, it is not supported on the server
+ BINARY = 155; // STRING -> PSEUDOTYPE(BINARY)
+
+ GEOJSON = 157; // OBJECT -> PSEUDOTYPE(GEOMETRY)
+ TO_GEOJSON = 158; // PSEUDOTYPE(GEOMETRY) -> OBJECT
+ POINT = 159; // NUMBER, NUMBER -> PSEUDOTYPE(GEOMETRY)
+ LINE = 160; // (ARRAY | PSEUDOTYPE(GEOMETRY))... -> PSEUDOTYPE(GEOMETRY)
+ POLYGON = 161; // (ARRAY | PSEUDOTYPE(GEOMETRY))... -> PSEUDOTYPE(GEOMETRY)
+ DISTANCE = 162; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) {geo_system:STRING, unit:STRING} -> NUMBER
+ INTERSECTS = 163; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> BOOL
+ INCLUDES = 164; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> BOOL
+ CIRCLE = 165; // PSEUDOTYPE(GEOMETRY), NUMBER {num_vertices:NUMBER, geo_system:STRING, unit:STRING, fill:BOOL} -> PSEUDOTYPE(GEOMETRY)
+ GET_INTERSECTING = 166; // TABLE, PSEUDOTYPE(GEOMETRY) {index:!STRING} -> StreamSelection
+ FILL = 167; // PSEUDOTYPE(GEOMETRY) -> PSEUDOTYPE(GEOMETRY)
+ GET_NEAREST = 168; // TABLE, PSEUDOTYPE(GEOMETRY) {index:!STRING, max_results:NUM, max_dist:NUM, geo_system:STRING, unit:STRING} -> ARRAY
+ POLYGON_SUB = 171; // PSEUDOTYPE(GEOMETRY), PSEUDOTYPE(GEOMETRY) -> PSEUDOTYPE(GEOMETRY)
+
+ // Constants for specifying key ranges
+ MINVAL = 180;
+ MAXVAL = 181;
+ }
+ optional TermType type = 1;
+
+ // This is only used when type is DATUM.
+ optional Datum datum = 2;
+
+ repeated Term args = 3; // Holds the positional arguments of the query.
+ message AssocPair {
+ optional string key = 1;
+ optional Term val = 2;
+ }
+ repeated AssocPair optargs = 4; // Holds the optional arguments of the query.
+ // (Note that the order of the optional arguments doesn't matter; think of a
+ // Hash.)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// EXAMPLE //
+////////////////////////////////////////////////////////////////////////////////
+// ```ruby
+// r.table('tbl', {:read_mode => 'outdated'}).insert([{:id => 0}, {:id => 1}])
+// ```
+// Would turn into:
+// Term {
+// type = INSERT;
+// args = [Term {
+// type = TABLE;
+// args = [Term {
+// type = DATUM;
+// datum = Datum { type = R_STR; r_str = "tbl"; };
+// }];
+// optargs = [["read_mode",
+// Term {
+// type = DATUM;
+// datum = Datum { type = R_STR; r_bool = "outdated"; };
+// }]];
+// },
+// Term {
+// type = MAKE_ARRAY;
+// args = [Term {
+// type = DATUM;
+// datum = Datum { type = R_OBJECT; r_object = [["id", 0]]; };
+// },
+// Term {
+// type = DATUM;
+// datum = Datum { type = R_OBJECT; r_object = [["id", 1]]; };
+// }];
+// }]
+// }
+// And the server would reply:
+// Response {
+// type = SUCCESS_ATOM;
+// token = 1;
+// response = [Datum { type = R_OBJECT; r_object = [["inserted", 2]]; }];
+// }
+// Or, if there were an error:
+// Response {
+// type = RUNTIME_ERROR;
+// token = 1;
+// response = [Datum { type = R_STR; r_str = "The table `tbl` doesn't exist!"; }];
+// backtrace = [Frame { type = POS; pos = 0; }, Frame { type = POS; pos = 0; }];
+// }
diff --git a/ext/librethinkdbxx/src/connection.cc b/ext/librethinkdbxx/src/connection.cc
new file mode 100644
index 00000000..53d106ec
--- /dev/null
+++ b/ext/librethinkdbxx/src/connection.cc
@@ -0,0 +1,434 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <netdb.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cstring>
+#include <cinttypes>
+#include <memory>
+
+#include "connection.h"
+#include "connection_p.h"
+#include "json_p.h"
+#include "exceptions.h"
+#include "term.h"
+#include "cursor_p.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/rapidjson.h"
+#include "rapidjson/encodedstream.h"
+#include "rapidjson/document.h"
+
+namespace RethinkDB {
+
+using QueryType = Protocol::Query::QueryType;
+
+// constants
+const int debug_net = 0;
+const uint32_t version_magic =
+ static_cast<uint32_t>(Protocol::VersionDummy::Version::V0_4);
+const uint32_t json_magic =
+ static_cast<uint32_t>(Protocol::VersionDummy::Protocol::JSON);
+
+std::unique_ptr<Connection> connect(std::string host, int port, std::string auth_key) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ char port_str[16];
+ snprintf(port_str, 16, "%d", port);
+ struct addrinfo *servinfo;
+ int ret = getaddrinfo(host.c_str(), port_str, &hints, &servinfo);
+ if (ret) throw Error("getaddrinfo: %s\n", gai_strerror(ret));
+
+ struct addrinfo *p;
+ Error error;
+ int sockfd;
+ for (p = servinfo; p != NULL; p = p->ai_next) {
+ sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (sockfd == -1) {
+ error = Error::from_errno("socket");
+ continue;
+ }
+
+ if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+ ::close(sockfd);
+ error = Error::from_errno("connect");
+ continue;
+ }
+
+ break;
+ }
+
+ if (p == NULL) {
+ throw error;
+ }
+
+ freeaddrinfo(servinfo);
+
+ std::unique_ptr<ConnectionPrivate> conn_private(new ConnectionPrivate(sockfd));
+ WriteLock writer(conn_private.get());
+ {
+ size_t size = auth_key.size();
+ char buf[12 + size];
+ memcpy(buf, &version_magic, 4);
+ uint32_t n = size;
+ memcpy(buf + 4, &n, 4);
+ memcpy(buf + 8, auth_key.data(), size);
+ memcpy(buf + 8 + size, &json_magic, 4);
+ writer.send(buf, sizeof buf);
+ }
+
+ ReadLock reader(conn_private.get());
+ {
+ const size_t max_response_length = 1024;
+ char buf[max_response_length + 1];
+ size_t len = reader.recv_cstring(buf, max_response_length);
+ if (len == max_response_length || strcmp(buf, "SUCCESS")) {
+ buf[len] = 0;
+ ::close(sockfd);
+ throw Error("Server rejected connection with message: %s", buf);
+ }
+ }
+
+ return std::unique_ptr<Connection>(new Connection(conn_private.release()));
+}
+
+Connection::Connection(ConnectionPrivate *dd) : d(dd) { }
+Connection::~Connection() {
+ // close();
+ if (d->guarded_sockfd >= 0)
+ ::close(d->guarded_sockfd);
+}
+
+size_t ReadLock::recv_some(char* buf, size_t size, double wait) {
+ if (wait != FOREVER) {
+ while (true) {
+ fd_set readfds;
+ struct timeval tv;
+
+ FD_ZERO(&readfds);
+ FD_SET(conn->guarded_sockfd, &readfds);
+
+ tv.tv_sec = (int)wait;
+ tv.tv_usec = (int)((wait - (int)wait) / MICROSECOND);
+ int rv = select(conn->guarded_sockfd + 1, &readfds, NULL, NULL, &tv);
+ if (rv == -1) {
+ throw Error::from_errno("select");
+ } else if (rv == 0) {
+ throw TimeoutException();
+ }
+
+ if (FD_ISSET(conn->guarded_sockfd, &readfds)) {
+ break;
+ }
+ }
+ }
+
+ ssize_t numbytes = ::recv(conn->guarded_sockfd, buf, size, 0);
+ if (numbytes <= 0) throw Error::from_errno("recv");
+ if (debug_net > 1) {
+ fprintf(stderr, "<< %s\n", write_datum(std::string(buf, numbytes)).c_str());
+ }
+
+ return numbytes;
+}
+
+void ReadLock::recv(char* buf, size_t size, double wait) {
+ while (size) {
+ size_t numbytes = recv_some(buf, size, wait);
+
+ buf += numbytes;
+ size -= numbytes;
+ }
+}
+
+size_t ReadLock::recv_cstring(char* buf, size_t max_size){
+ size_t size = 0;
+ for (; size < max_size; size++) {
+ recv(buf, 1, FOREVER);
+ if (*buf == 0) {
+ break;
+ }
+ buf++;
+ }
+ return size;
+}
+
+void WriteLock::send(const char* buf, size_t size) {
+ while (size) {
+ ssize_t numbytes = ::write(conn->guarded_sockfd, buf, size);
+ if (numbytes == -1) throw Error::from_errno("write");
+ if (debug_net > 1) {
+ fprintf(stderr, ">> %s\n", write_datum(std::string(buf, numbytes)).c_str());
+ }
+
+ buf += numbytes;
+ size -= numbytes;
+ }
+}
+
+void WriteLock::send(const std::string data) {
+ send(data.data(), data.size());
+}
+
+std::string ReadLock::recv(size_t size) {
+ char buf[size];
+ recv(buf, size, FOREVER);
+ return buf;
+}
+
+void Connection::close() {
+ CacheLock guard(d.get());
+ for (auto& it : d->guarded_cache) {
+ stop_query(it.first);
+ }
+
+ int ret = ::close(d->guarded_sockfd);
+ if (ret == -1) {
+ throw Error::from_errno("close");
+ }
+ d->guarded_sockfd = -1;
+}
+
+Response ConnectionPrivate::wait_for_response(uint64_t token_want, double wait) {
+ CacheLock guard(this);
+ ConnectionPrivate::TokenCache& cache = guarded_cache[token_want];
+
+ while (true) {
+ if (!cache.responses.empty()) {
+ Response response(std::move(cache.responses.front()));
+ cache.responses.pop();
+ if (cache.closed && cache.responses.empty()) {
+ guarded_cache.erase(token_want);
+ }
+
+ return response;
+ }
+
+ if (cache.closed) {
+ throw Error("Trying to read from a closed token");
+ }
+
+ if (guarded_loop_active) {
+ cache.cond.wait(guard.inner_lock);
+ } else {
+ break;
+ }
+ }
+
+ ReadLock reader(this);
+ return reader.read_loop(token_want, std::move(guard), wait);
+}
+
+Response ReadLock::read_loop(uint64_t token_want, CacheLock&& guard, double wait) {
+ if (!guard.inner_lock) {
+ guard.lock();
+ }
+ if (conn->guarded_loop_active) {
+ throw Error("Cannot run more than one read loop on the same connection");
+ }
+ conn->guarded_loop_active = true;
+ guard.unlock();
+
+ try {
+ while (true) {
+ char buf[12];
+ bzero(buf, sizeof(buf));
+ recv(buf, 12, wait);
+ uint64_t token_got;
+ memcpy(&token_got, buf, 8);
+ uint32_t length;
+ memcpy(&length, buf + 8, 4);
+
+ std::unique_ptr<char[]> bufmem(new char[length + 1]);
+ char *buffer = bufmem.get();
+ bzero(buffer, length + 1);
+ recv(buffer, length, wait);
+ buffer[length] = '\0';
+
+ rapidjson::Document json;
+ json.ParseInsitu(buffer);
+ if (json.HasParseError()) {
+ fprintf(stderr, "json parse error, code: %d, position: %d\n",
+ (int)json.GetParseError(), (int)json.GetErrorOffset());
+ } else if (json.IsNull()) {
+ fprintf(stderr, "null value, read: %s\n", buffer);
+ }
+
+ Datum datum = read_datum(json);
+ if (debug_net > 0) {
+ fprintf(stderr, "[%" PRIu64 "] << %s\n", token_got, write_datum(datum).c_str());
+ }
+
+ Response response(std::move(datum));
+
+ if (token_got == token_want) {
+ guard.lock();
+ if (response.type != Protocol::Response::ResponseType::SUCCESS_PARTIAL) {
+ auto it = conn->guarded_cache.find(token_got);
+ if (it != conn->guarded_cache.end()) {
+ it->second.closed = true;
+ it->second.cond.notify_all();
+ }
+ conn->guarded_cache.erase(it);
+ }
+ conn->guarded_loop_active = false;
+ for (auto& it : conn->guarded_cache) {
+ it.second.cond.notify_all();
+ }
+ return response;
+ } else {
+ guard.lock();
+ auto it = conn->guarded_cache.find(token_got);
+ if (it == conn->guarded_cache.end()) {
+ // drop the response
+ } else if (!it->second.closed) {
+ it->second.responses.emplace(std::move(response));
+ if (response.type != Protocol::Response::ResponseType::SUCCESS_PARTIAL) {
+ it->second.closed = true;
+ }
+ }
+ it->second.cond.notify_all();
+ guard.unlock();
+ }
+ }
+ } catch (const TimeoutException &e) {
+ if (!guard.inner_lock){
+ guard.lock();
+ }
+ conn->guarded_loop_active = false;
+ throw e;
+ }
+}
+
+void ConnectionPrivate::run_query(Query query, bool no_reply) {
+ WriteLock writer(this);
+ writer.send(query.serialize());
+}
+
+Cursor Connection::start_query(Term *term, OptArgs&& opts) {
+ bool no_reply = false;
+ auto it = opts.find("noreply");
+ if (it != opts.end()) {
+ no_reply = *(it->second.datum.get_boolean());
+ }
+
+ uint64_t token = d->new_token();
+ {
+ CacheLock guard(d.get());
+ d->guarded_cache[token];
+ }
+
+ d->run_query(Query{QueryType::START, token, term->datum, std::move(opts)});
+ if (no_reply) {
+ return Cursor(new CursorPrivate(token, this, Nil()));
+ }
+
+ Cursor cursor(new CursorPrivate(token, this));
+ Response response = d->wait_for_response(token, FOREVER);
+ cursor.d->add_response(std::move(response));
+ return cursor;
+}
+
+void Connection::stop_query(uint64_t token) {
+ const auto& it = d->guarded_cache.find(token);
+ if (it != d->guarded_cache.end() && !it->second.closed) {
+ d->run_query(Query{QueryType::STOP, token}, true);
+ }
+}
+
+void Connection::continue_query(uint64_t token) {
+ d->run_query(Query{QueryType::CONTINUE, token}, true);
+}
+
+Error Response::as_error() {
+ std::string repr;
+ if (result.size() == 1) {
+ std::string* string = result[0].get_string();
+ if (string) {
+ repr = *string;
+ } else {
+ repr = write_datum(result[0]);
+ }
+ } else {
+ repr = write_datum(Datum(result));
+ }
+ std::string err;
+ using RT = Protocol::Response::ResponseType;
+ using ET = Protocol::Response::ErrorType;
+ switch (type) {
+ case RT::SUCCESS_SEQUENCE: err = "unexpected response: SUCCESS_SEQUENCE"; break;
+ case RT::SUCCESS_PARTIAL: err = "unexpected response: SUCCESS_PARTIAL"; break;
+ case RT::SUCCESS_ATOM: err = "unexpected response: SUCCESS_ATOM"; break;
+ case RT::WAIT_COMPLETE: err = "unexpected response: WAIT_COMPLETE"; break;
+ case RT::SERVER_INFO: err = "unexpected response: SERVER_INFO"; break;
+ case RT::CLIENT_ERROR: err = "ReqlDriverError"; break;
+ case RT::COMPILE_ERROR: err = "ReqlCompileError"; break;
+ case RT::RUNTIME_ERROR:
+ switch (error_type) {
+ case ET::INTERNAL: err = "ReqlInternalError"; break;
+ case ET::RESOURCE_LIMIT: err = "ReqlResourceLimitError"; break;
+ case ET::QUERY_LOGIC: err = "ReqlQueryLogicError"; break;
+ case ET::NON_EXISTENCE: err = "ReqlNonExistenceError"; break;
+ case ET::OP_FAILED: err = "ReqlOpFailedError"; break;
+ case ET::OP_INDETERMINATE: err = "ReqlOpIndeterminateError"; break;
+ case ET::USER: err = "ReqlUserError"; break;
+ case ET::PERMISSION_ERROR: err = "ReqlPermissionError"; break;
+ default: err = "ReqlRuntimeError"; break;
+ }
+ }
+ throw Error("%s: %s", err.c_str(), repr.c_str());
+}
+
+Protocol::Response::ResponseType response_type(double t) {
+ int n = static_cast<int>(t);
+ using RT = Protocol::Response::ResponseType;
+ switch (n) {
+ case static_cast<int>(RT::SUCCESS_ATOM):
+ return RT::SUCCESS_ATOM;
+ case static_cast<int>(RT::SUCCESS_SEQUENCE):
+ return RT::SUCCESS_SEQUENCE;
+ case static_cast<int>(RT::SUCCESS_PARTIAL):
+ return RT::SUCCESS_PARTIAL;
+ case static_cast<int>(RT::WAIT_COMPLETE):
+ return RT::WAIT_COMPLETE;
+ case static_cast<int>(RT::CLIENT_ERROR):
+ return RT::CLIENT_ERROR;
+ case static_cast<int>(RT::COMPILE_ERROR):
+ return RT::COMPILE_ERROR;
+ case static_cast<int>(RT::RUNTIME_ERROR):
+ return RT::RUNTIME_ERROR;
+ default:
+ throw Error("Unknown response type");
+ }
+}
+
+Protocol::Response::ErrorType runtime_error_type(double t) {
+ int n = static_cast<int>(t);
+ using ET = Protocol::Response::ErrorType;
+ switch (n) {
+ case static_cast<int>(ET::INTERNAL):
+ return ET::INTERNAL;
+ case static_cast<int>(ET::RESOURCE_LIMIT):
+ return ET::RESOURCE_LIMIT;
+ case static_cast<int>(ET::QUERY_LOGIC):
+ return ET::QUERY_LOGIC;
+ case static_cast<int>(ET::NON_EXISTENCE):
+ return ET::NON_EXISTENCE;
+ case static_cast<int>(ET::OP_FAILED):
+ return ET::OP_FAILED;
+ case static_cast<int>(ET::OP_INDETERMINATE):
+ return ET::OP_INDETERMINATE;
+ case static_cast<int>(ET::USER):
+ return ET::USER;
+ default:
+ throw Error("Unknown error type");
+ }
+}
+
+}
diff --git a/ext/librethinkdbxx/src/connection.h b/ext/librethinkdbxx/src/connection.h
new file mode 100644
index 00000000..d3882857
--- /dev/null
+++ b/ext/librethinkdbxx/src/connection.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <string>
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+
+#include "protocol_defs.h"
+#include "datum.h"
+#include "error.h"
+
+#define FOREVER (-1)
+#define SECOND 1
+#define MICROSECOND 0.000001
+
+namespace RethinkDB {
+
+class Term;
+using OptArgs = std::map<std::string, Term>;
+
+// A connection to a RethinkDB server
+// It contains:
+// * A socket
+// * Read and write locks
+// * A cache of responses that have not been read by the corresponding Cursor
+class ConnectionPrivate;
+class Connection {
+public:
+ Connection() = delete;
+ Connection(const Connection&) noexcept = delete;
+ Connection(Connection&&) noexcept = delete;
+ Connection& operator=(Connection&&) noexcept = delete;
+ Connection& operator=(const Connection&) noexcept = delete;
+ ~Connection();
+
+ void close();
+
+private:
+ explicit Connection(ConnectionPrivate *dd);
+ std::unique_ptr<ConnectionPrivate> d;
+
+ Cursor start_query(Term *term, OptArgs&& args);
+ void stop_query(uint64_t);
+ void continue_query(uint64_t);
+
+ friend class Cursor;
+ friend class CursorPrivate;
+ friend class Token;
+ friend class Term;
+ friend std::unique_ptr<Connection>
+ connect(std::string host, int port, std::string auth_key);
+
+};
+
+// $doc(connect)
+std::unique_ptr<Connection> connect(std::string host = "localhost", int port = 28015, std::string auth_key = "");
+
+}
diff --git a/ext/librethinkdbxx/src/connection_p.h b/ext/librethinkdbxx/src/connection_p.h
new file mode 100644
index 00000000..d8a95e3c
--- /dev/null
+++ b/ext/librethinkdbxx/src/connection_p.h
@@ -0,0 +1,133 @@
+#ifndef CONNECTION_P_H
+#define CONNECTION_P_H
+
+#include <inttypes.h>
+
+#include "connection.h"
+#include "term.h"
+#include "json_p.h"
+
+namespace RethinkDB {
+
+extern const int debug_net;
+
+struct Query {
+ Protocol::Query::QueryType type;
+ uint64_t token;
+ Datum term;
+ OptArgs optArgs;
+
+ std::string serialize() {
+ Array query_arr{static_cast<double>(type)};
+ if (term.is_valid()) query_arr.emplace_back(term);
+ if (!optArgs.empty())
+ query_arr.emplace_back(Term(std::move(optArgs)).datum);
+
+ std::string query_str = write_datum(query_arr);
+ if (debug_net > 0) {
+ fprintf(stderr, "[%" PRIu64 "] >> %s\n", token, query_str.c_str());
+ }
+
+ char header[12];
+ memcpy(header, &token, 8);
+ uint32_t size = query_str.size();
+ memcpy(header + 8, &size, 4);
+ query_str.insert(0, header, 12);
+ return query_str;
+ }
+};
+
+// Used internally to convert a raw response type into an enum
+Protocol::Response::ResponseType response_type(double t);
+Protocol::Response::ErrorType runtime_error_type(double t);
+
+// Contains a response from the server. Use the Cursor class to interact with these responses
+class Response {
+public:
+ Response() = delete;
+ explicit Response(Datum&& datum) :
+ type(response_type(std::move(datum).extract_field("t").extract_number())),
+ error_type(datum.get_field("e") ?
+ runtime_error_type(std::move(datum).extract_field("e").extract_number()) :
+ Protocol::Response::ErrorType(0)),
+ result(std::move(datum).extract_field("r").extract_array()) { }
+ Error as_error();
+ Protocol::Response::ResponseType type;
+ Protocol::Response::ErrorType error_type;
+ Array result;
+};
+
+class Token;
+class ConnectionPrivate {
+public:
+ ConnectionPrivate(int sockfd)
+ : guarded_next_token(1), guarded_sockfd(sockfd), guarded_loop_active(false)
+ { }
+
+ void run_query(Query query, bool no_reply = false);
+
+ Response wait_for_response(uint64_t, double);
+ uint64_t new_token() {
+ return guarded_next_token++;
+ }
+
+ std::mutex read_lock;
+ std::mutex write_lock;
+ std::mutex cache_lock;
+
+ struct TokenCache {
+ bool closed = false;
+ std::condition_variable cond;
+ std::queue<Response> responses;
+ };
+
+ std::map<uint64_t, TokenCache> guarded_cache;
+ uint64_t guarded_next_token;
+ int guarded_sockfd;
+ bool guarded_loop_active;
+};
+
+class CacheLock {
+public:
+ CacheLock(ConnectionPrivate* conn) : inner_lock(conn->cache_lock) { }
+
+ void lock() {
+ inner_lock.lock();
+ }
+
+ void unlock() {
+ inner_lock.unlock();
+ }
+
+ std::unique_lock<std::mutex> inner_lock;
+};
+
+class ReadLock {
+public:
+ ReadLock(ConnectionPrivate* conn_) : lock(conn_->read_lock), conn(conn_) { }
+
+ size_t recv_some(char*, size_t, double wait);
+ void recv(char*, size_t, double wait);
+ std::string recv(size_t);
+ size_t recv_cstring(char*, size_t);
+
+ Response read_loop(uint64_t, CacheLock&&, double);
+
+ std::lock_guard<std::mutex> lock;
+ ConnectionPrivate* conn;
+};
+
+class WriteLock {
+public:
+ WriteLock(ConnectionPrivate* conn_) : lock(conn_->write_lock), conn(conn_) { }
+
+ void send(const char*, size_t);
+ void send(std::string);
+
+ std::lock_guard<std::mutex> lock;
+ ConnectionPrivate* conn;
+};
+
+} // namespace RethinkDB
+
+#endif // CONNECTION_P_H
diff --git a/ext/librethinkdbxx/src/cursor.cc b/ext/librethinkdbxx/src/cursor.cc
new file mode 100644
index 00000000..df0621eb
--- /dev/null
+++ b/ext/librethinkdbxx/src/cursor.cc
@@ -0,0 +1,223 @@
+#include "cursor.h"
+#include "cursor_p.h"
+#include "exceptions.h"
+
+namespace RethinkDB {
+
+// for type completion, in order to forward declare with unique_ptr
+Cursor::Cursor(Cursor&&) = default;
+Cursor& Cursor::operator=(Cursor&&) = default;
+
+CursorPrivate::CursorPrivate(uint64_t token_, Connection *conn_)
+ : single(false), no_more(false), index(0),
+ token(token_), conn(conn_)
+{ }
+
+CursorPrivate::CursorPrivate(uint64_t token_, Connection *conn_, Datum&& datum)
+ : single(true), no_more(true), index(0), buffer(Array{std::move(datum)}),
+ token(token_), conn(conn_)
+{ }
+
+Cursor::Cursor(CursorPrivate *dd) : d(dd) {}
+
+Cursor::~Cursor() {
+ try {
+ if (d && d->conn) {
+ close();
+ }
+ } catch ( ... ) {}
+}
+
+Datum& Cursor::next(double wait) const {
+ if (!has_next(wait)) {
+ throw Error("next: No more data");
+ }
+
+ return d->buffer[d->index++];
+}
+
+Datum& Cursor::peek(double wait) const {
+ if (!has_next(wait)) {
+ throw Error("next: No more data");
+ }
+
+ return d->buffer[d->index];
+}
+
+void Cursor::each(std::function<void(Datum&&)> f, double wait) const {
+ while (has_next(wait)) {
+ f(std::move(d->buffer[d->index++]));
+ }
+}
+
+void CursorPrivate::convert_single() const {
+ if (index != 0) {
+ throw Error("Cursor: already consumed");
+ }
+
+ if (buffer.size() != 1) {
+ throw Error("Cursor: invalid response from server");
+ }
+
+ if (!buffer[0].is_array()) {
+ throw Error("Cursor: not an array");
+ }
+
+ buffer.swap(buffer[0].extract_array());
+ single = false;
+}
+
+void CursorPrivate::clear_and_read_all() const {
+ if (single) {
+ convert_single();
+ }
+ if (index != 0) {
+ buffer.erase(buffer.begin(), buffer.begin() + index);
+ index = 0;
+ }
+ while (!no_more) {
+ add_response(conn->d->wait_for_response(token, FOREVER));
+ }
+}
+
+Array&& Cursor::to_array() && {
+ d->clear_and_read_all();
+ return std::move(d->buffer);
+}
+
+Array Cursor::to_array() const & {
+ d->clear_and_read_all();
+ return d->buffer;
+}
+
+Datum Cursor::to_datum() const & {
+ if (d->single) {
+ if (d->index != 0) {
+ throw Error("to_datum: already consumed");
+ }
+ return d->buffer[0];
+ }
+
+ d->clear_and_read_all();
+ return d->buffer;
+}
+
+Datum Cursor::to_datum() && {
+ Datum ret((Nil()));
+ if (d->single) {
+ if (d->index != 0) {
+ throw Error("to_datum: already consumed");
+ }
+ ret = std::move(d->buffer[0]);
+ } else {
+ d->clear_and_read_all();
+ ret = std::move(d->buffer);
+ }
+
+ return ret;
+}
+
+void Cursor::close() const {
+ d->conn->stop_query(d->token);
+ d->no_more = true;
+}
+
+bool Cursor::has_next(double wait) const {
+ if (d->single) {
+ d->convert_single();
+ }
+
+ while (true) {
+ if (d->index >= d->buffer.size()) {
+ if (d->no_more) {
+ return false;
+ }
+ d->add_response(d->conn->d->wait_for_response(d->token, wait));
+ } else {
+ return true;
+ }
+ }
+}
+
+bool Cursor::is_single() const {
+ return d->single;
+}
+
+void CursorPrivate::add_results(Array&& results) const {
+ if (index >= buffer.size()) {
+ buffer = std::move(results);
+ index = 0;
+ } else {
+ for (auto& it : results) {
+ buffer.emplace_back(std::move(it));
+ }
+ }
+}
+
+void CursorPrivate::add_response(Response&& response) const {
+ using RT = Protocol::Response::ResponseType;
+ switch (response.type) {
+ case RT::SUCCESS_SEQUENCE:
+ add_results(std::move(response.result));
+ no_more = true;
+ break;
+ case RT::SUCCESS_PARTIAL:
+ conn->continue_query(token);
+ add_results(std::move(response.result));
+ break;
+ case RT::SUCCESS_ATOM:
+ add_results(std::move(response.result));
+ single = true;
+ no_more = true;
+ break;
+ case RT::SERVER_INFO:
+ add_results(std::move(response.result));
+ single = true;
+ no_more = true;
+ break;
+ case RT::WAIT_COMPLETE:
+ case RT::CLIENT_ERROR:
+ case RT::COMPILE_ERROR:
+ case RT::RUNTIME_ERROR:
+ no_more = true;
+ throw response.as_error();
+ }
+}
+
+Cursor::iterator Cursor::begin() {
+ return iterator(this);
+}
+
+Cursor::iterator Cursor::end() {
+ return iterator(nullptr);
+}
+
+Cursor::iterator::iterator(Cursor* cursor_) : cursor(cursor_) {}
+
+Cursor::iterator& Cursor::iterator::operator++ () {
+ if (cursor == nullptr) {
+ throw Error("incrementing an exhausted Cursor iterator");
+ }
+
+ cursor->next();
+ return *this;
+}
+
+Datum& Cursor::iterator::operator* () {
+ if (cursor == nullptr) {
+ throw Error("reading from empty Cursor iterator");
+ }
+
+ return cursor->peek();
+}
+
+bool Cursor::iterator::operator!= (const Cursor::iterator& other) const {
+ if (cursor == other.cursor) {
+ return false;
+ }
+
+ return !((cursor == nullptr && !other.cursor->has_next()) ||
+ (other.cursor == nullptr && !cursor->has_next()));
+}
+
+}
diff --git a/ext/librethinkdbxx/src/cursor.h b/ext/librethinkdbxx/src/cursor.h
new file mode 100644
index 00000000..60ae1817
--- /dev/null
+++ b/ext/librethinkdbxx/src/cursor.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "connection.h"
+
+namespace RethinkDB {
+
+// The response from the server, as returned by run.
+// The response is either a single datum or a stream:
+// * If it is a stream, the cursor represents each element of the stream.
+// - Batches are fetched from the server as needed.
+// * If it is a single datum, is_single() returns true.
+// - If it is an array, the cursor represents each element of that array
+// - Otherwise, to_datum() returns the datum and iteration throws an exception.
+// The cursor can only be iterated over once, it discards data that has already been read.
+class CursorPrivate;
+class Cursor {
+public:
+ Cursor() = delete;
+ ~Cursor();
+
+ Cursor(Cursor&&); // movable
+ Cursor& operator=(Cursor&&);
+ Cursor(const Cursor&) = delete; // not copyable
+ Cursor& operator=(const Cursor&) = delete;
+
+ // Returned by begin() and end()
+ class iterator {
+ public:
+ iterator(Cursor*);
+ iterator& operator++ ();
+ Datum& operator* ();
+ bool operator!= (const iterator&) const;
+
+ private:
+ Cursor *cursor;
+ };
+
+ // Consume the next element
+ Datum& next(double wait = FOREVER) const;
+
+ // Peek at the next element
+ Datum& peek(double wait = FOREVER) const;
+
+ // Call f on every element of the Cursor
+ void each(std::function<void(Datum&&)> f, double wait = FOREVER) const;
+
+ // Consume and return all elements
+ Array&& to_array() &&;
+
+ // If is_single(), returns the single datum. Otherwise returns to_array().
+ Datum to_datum() &&;
+ Datum to_datum() const &;
+
+ // Efficiently consume and return all elements
+ Array to_array() const &;
+
+ // Close the cursor
+ void close() const;
+
+ // Returns false if there are no more elements
+ bool has_next(double wait = FOREVER) const;
+
+ // Returns false if the cursor is a stream
+ bool is_single() const;
+
+ iterator begin();
+ iterator end();
+
+private:
+ explicit Cursor(CursorPrivate *dd);
+ std::unique_ptr<CursorPrivate> d;
+
+ friend class Connection;
+};
+
+}
diff --git a/ext/librethinkdbxx/src/cursor_p.h b/ext/librethinkdbxx/src/cursor_p.h
new file mode 100644
index 00000000..ce584cd7
--- /dev/null
+++ b/ext/librethinkdbxx/src/cursor_p.h
@@ -0,0 +1,29 @@
+#ifndef CURSOR_P_H
+#define CURSOR_P_H
+
+#include "connection_p.h"
+
+namespace RethinkDB {
+
+class CursorPrivate {
+public:
+ CursorPrivate(uint64_t token, Connection *conn);
+ CursorPrivate(uint64_t token, Connection *conn, Datum&&);
+
+ void add_response(Response&&) const;
+ void add_results(Array&&) const;
+ void clear_and_read_all() const;
+ void convert_single() const;
+
+ mutable bool single = false;
+ mutable bool no_more = false;
+ mutable size_t index = 0;
+ mutable Array buffer;
+
+ uint64_t token;
+ Connection *conn;
+};
+
+} // namespace RethinkDB
+
+#endif // CURSOR_P_H \ No newline at end of file
diff --git a/ext/librethinkdbxx/src/datum.cc b/ext/librethinkdbxx/src/datum.cc
new file mode 100644
index 00000000..e4dbc8dc
--- /dev/null
+++ b/ext/librethinkdbxx/src/datum.cc
@@ -0,0 +1,449 @@
+#include <float.h>
+#include <cmath>
+
+#include "datum.h"
+#include "json_p.h"
+#include "utils.h"
+#include "cursor.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/prettywriter.h"
+#include "rapidjson/stringbuffer.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+bool Datum::is_nil() const {
+ return type == Type::NIL;
+}
+
+bool Datum::is_boolean() const {
+ return type == Type::BOOLEAN;
+}
+
+bool Datum::is_number() const {
+ return type == Type::NUMBER;
+}
+
+bool Datum::is_string() const {
+ return type == Type::STRING;
+}
+
+bool Datum::is_object() const {
+ return type == Type::OBJECT;
+}
+
+bool Datum::is_array() const {
+ return type == Type::ARRAY;
+}
+
+bool Datum::is_binary() const {
+ return type == Type::BINARY;
+}
+
+bool Datum::is_time() const {
+ return type == Type::TIME;
+}
+
+bool* Datum::get_boolean() {
+ if (type == Type::BOOLEAN) {
+ return &value.boolean;
+ } else {
+ return NULL;
+ }
+}
+
+const bool* Datum::get_boolean() const {
+ if (type == Type::BOOLEAN) {
+ return &value.boolean;
+ } else {
+ return NULL;
+ }
+}
+
+double* Datum::get_number() {
+ if (type == Type::NUMBER) {
+ return &value.number;
+ } else {
+ return NULL;
+ }
+}
+
+const double* Datum::get_number() const {
+ if (type == Type::NUMBER) {
+ return &value.number;
+ } else {
+ return NULL;
+ }
+}
+
+std::string* Datum::get_string() {
+ if (type == Type::STRING) {
+ return &value.string;
+ } else {
+ return NULL;
+ }
+}
+
+const std::string* Datum::get_string() const {
+ if (type == Type::STRING) {
+ return &value.string;
+ } else {
+ return NULL;
+ }
+}
+
+Datum* Datum::get_field(std::string key) {
+ if (type != Type::OBJECT) {
+ return NULL;
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ return NULL;
+ }
+ return &it->second;
+}
+
+const Datum* Datum::get_field(std::string key) const {
+ if (type != Type::OBJECT) {
+ return NULL;
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ return NULL;
+ }
+ return &it->second;
+}
+
+Datum* Datum::get_nth(size_t i) {
+ if (type != Type::ARRAY) {
+ return NULL;
+ }
+ if (i >= value.array.size()) {
+ return NULL;
+ }
+ return &value.array[i];
+}
+
+const Datum* Datum::get_nth(size_t i) const {
+ if (type != Type::ARRAY) {
+ return NULL;
+ }
+ if (i >= value.array.size()) {
+ return NULL;
+ }
+ return &value.array[i];
+}
+
+Object* Datum::get_object() {
+ if (type == Type::OBJECT) {
+ return &value.object;
+ } else {
+ return NULL;
+ }
+}
+
+const Object* Datum::get_object() const {
+ if (type == Type::OBJECT) {
+ return &value.object;
+ } else {
+ return NULL;
+ }
+}
+
+Array* Datum::get_array() {
+ if (type == Type::ARRAY) {
+ return &value.array;
+ } else {
+ return NULL;
+ }
+}
+
+const Array* Datum::get_array() const {
+ if (type == Type::ARRAY) {
+ return &value.array;
+ } else {
+ return NULL;
+ }
+}
+
+Binary* Datum::get_binary() {
+ if (type == Type::BINARY) {
+ return &value.binary;
+ } else {
+ return NULL;
+ }
+}
+
+const Binary* Datum::get_binary() const {
+ if (type == Type::BINARY) {
+ return &value.binary;
+ } else {
+ return NULL;
+ }
+}
+
+Time* Datum::get_time() {
+ if (type == Type::TIME) {
+ return &value.time;
+ } else {
+ return NULL;
+ }
+}
+
+const Time* Datum::get_time() const {
+ if (type == Type::TIME) {
+ return &value.time;
+ } else {
+ return NULL;
+ }
+}
+
+bool& Datum::extract_boolean() {
+ if (type != Type::BOOLEAN) {
+ throw Error("extract_bool: Not a boolean");
+ }
+ return value.boolean;
+}
+
+double& Datum::extract_number() {
+ if (type != Type::NUMBER) {
+ throw Error("extract_number: Not a number: %s", write_datum(*this).c_str());
+ }
+ return value.number;
+}
+
+std::string& Datum::extract_string() {
+ if (type != Type::STRING) {
+ throw Error("extract_string: Not a string");
+ }
+ return value.string;
+}
+
+Object& Datum::extract_object() {
+ if (type != Type::OBJECT) {
+ throw Error("extract_object: Not an object");
+ }
+ return value.object;
+}
+
+Datum& Datum::extract_field(std::string key) {
+ if (type != Type::OBJECT) {
+ throw Error("extract_field: Not an object");
+ }
+ auto it = value.object.find(key);
+ if (it == value.object.end()) {
+ throw Error("extract_field: No such key in object");
+ }
+ return it->second;
+}
+
+Datum& Datum::extract_nth(size_t i) {
+ if (type != Type::ARRAY) {
+ throw Error("extract_nth: Not an array");
+ }
+ if (i >= value.array.size()) {
+ throw Error("extract_nth: index too large");
+ }
+ return value.array[i];
+}
+
+Array& Datum::extract_array() {
+ if (type != Type::ARRAY) {
+ throw Error("get_array: Not an array");
+ }
+ return value.array;
+}
+
+Binary& Datum::extract_binary() {
+ if (type != Type::BINARY) {
+ throw Error("get_binary: Not a binary");
+ }
+ return value.binary;
+}
+
+Time& Datum::extract_time() {
+ if (type != Type::TIME) {
+ throw Error("get_time: Not a time");
+ }
+ return value.time;
+}
+
+int Datum::compare(const Datum& other) const {
+#define COMPARE(a, b) do { \
+ if (a < b) { return -1; } \
+ if (a > b) { return 1; } } while(0)
+#define COMPARE_OTHER(x) COMPARE(x, other.x)
+
+ COMPARE_OTHER(type);
+ int c;
+ switch (type) {
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: COMPARE_OTHER(value.boolean); break;
+ case Type::NUMBER: COMPARE_OTHER(value.number); break;
+ case Type::STRING:
+ c = value.string.compare(other.value.string);
+ COMPARE(c, 0);
+ break;
+ case Type::BINARY:
+ c = value.binary.data.compare(other.value.binary.data);
+ COMPARE(c, 0);
+ break;
+ case Type::TIME:
+ COMPARE(value.time.epoch_time, other.value.time.epoch_time);
+ COMPARE(value.time.utc_offset, other.value.time.utc_offset);
+ break;
+ case Type::ARRAY:
+ COMPARE_OTHER(value.array.size());
+ for (size_t i = 0; i < value.array.size(); i++) {
+ c = value.array[i].compare(other.value.array[i]);
+ COMPARE(c, 0);
+ }
+ break;
+ case Type::OBJECT:
+ COMPARE_OTHER(value.object.size());
+ for (Object::const_iterator l = value.object.begin(),
+ r = other.value.object.begin();
+ l != value.object.end();
+ ++l, ++r) {
+ COMPARE(l->first, r->first);
+ c = l->second.compare(r->second);
+ COMPARE(c, 0);
+ }
+ break;
+ default:
+ throw Error("cannot compare invalid datum");
+ }
+ return 0;
+#undef COMPARE_OTHER
+#undef COMPARE
+}
+
+bool Datum::operator== (const Datum& other) const {
+ return compare(other) == 0;
+}
+
+Datum Datum::from_raw() const {
+ do {
+ const Datum* type_field = get_field("$reql_type$");
+ if (!type_field) break;
+ const std::string* type = type_field->get_string();
+ if (!type) break;;
+ if (!strcmp(type->c_str(), "BINARY")) {
+ const Datum* data_field = get_field("data");
+ if (!data_field) break;
+ const std::string* encoded_data = data_field->get_string();
+ if (!encoded_data) break;
+ Binary binary("");
+ if (base64_decode(*encoded_data, binary.data)) {
+ return binary;
+ }
+ } else if (!strcmp(type->c_str(), "TIME")) {
+ const Datum* epoch_field = get_field("epoch_time");
+ if (!epoch_field) break;
+ const Datum* tz_field = get_field("timezone");
+ if (!tz_field) break;
+ const double* epoch_time = epoch_field->get_number();
+ if (!epoch_time) break;
+ const std::string* tz = tz_field->get_string();
+ if (!tz) break;
+ double offset;
+ if (!Time::parse_utc_offset(*tz, &offset)) break;
+ return Time(*epoch_time, offset);
+ }
+ } while (0);
+ return *this;
+}
+
+Datum Datum::to_raw() const {
+ if (type == Type::BINARY) {
+ return Object{
+ {"$reql_type$", "BINARY"},
+ {"data", base64_encode(value.binary.data)}};
+ } else if (type == Type::TIME) {
+ return Object{
+ {"$reql_type$", "TIME"},
+ {"epoch_time", value.time.epoch_time},
+ {"timezone", Time::utc_offset_string(value.time.utc_offset)}};
+ }
+ return *this;
+}
+
+Datum::Datum(Cursor&& cursor) : Datum(cursor.to_datum()) { }
+Datum::Datum(const Cursor& cursor) : Datum(cursor.to_datum()) { }
+
+static const double max_dbl_int = 0x1LL << DBL_MANT_DIG;
+static const double min_dbl_int = max_dbl_int * -1;
+bool number_as_integer(double d, int64_t *i_out) {
+ static_assert(DBL_MANT_DIG == 53, "Doubles are wrong size.");
+
+ if (min_dbl_int <= d && d <= max_dbl_int) {
+ int64_t i = d;
+ if (static_cast<double>(i) == d) {
+ *i_out = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+template void Datum::write_json(
+ rapidjson::Writer<rapidjson::StringBuffer> *writer) const;
+template void Datum::write_json(
+ rapidjson::PrettyWriter<rapidjson::StringBuffer> *writer) const;
+
+template <class json_writer_t>
+void Datum::write_json(json_writer_t *writer) const {
+ switch (type) {
+ case Type::NIL: writer->Null(); break;
+ case Type::BOOLEAN: writer->Bool(value.boolean); break;
+ case Type::NUMBER: {
+ const double d = value.number;
+ // Always print -0.0 as a double since integers cannot represent -0.
+ // Otherwise check if the number is an integer and print it as such.
+ int64_t i;
+ if (!(d == 0.0 && std::signbit(d)) && number_as_integer(d, &i)) {
+ writer->Int64(i);
+ } else {
+ writer->Double(d);
+ }
+ } break;
+ case Type::STRING: writer->String(value.string.data(), value.string.size()); break;
+ case Type::ARRAY: {
+ writer->StartArray();
+ for (auto it : value.array) {
+ it.write_json(writer);
+ }
+ writer->EndArray();
+ } break;
+ case Type::OBJECT: {
+ writer->StartObject();
+ for (auto it : value.object) {
+ writer->Key(it.first.data(), it.first.size());
+ it.second.write_json(writer);
+ }
+ writer->EndObject();
+ } break;
+
+ case Type::BINARY:
+ case Type::TIME:
+ to_raw().write_json(writer);
+ break;
+ default:
+ throw Error("cannot write invalid datum");
+ }
+}
+
+std::string Datum::as_json() const {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ write_json(&writer);
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+Datum Datum::from_json(const std::string& json) {
+ return read_datum(json);
+}
+
+} // namespace RethinkDB
diff --git a/ext/librethinkdbxx/src/datum.h b/ext/librethinkdbxx/src/datum.h
new file mode 100644
index 00000000..051e2ca2
--- /dev/null
+++ b/ext/librethinkdbxx/src/datum.h
@@ -0,0 +1,287 @@
+#pragma once
+
+#include <string>
+#include <vector>
+#include <map>
+#include <functional>
+
+#include "protocol_defs.h"
+#include "error.h"
+#include "types.h"
+
+namespace RethinkDB {
+
+class Cursor;
+
+// The type of data stored in a RethinkDB database.
+// The following JSON types are represented in a Datum as
+// * null -> Nil
+// * boolean -> bool
+// * number -> double
+// * unicode strings -> std::string
+// * array -> Array (aka std::vector<Datum>
+// * object -> Object (aka std::map<std::string, Datum>>
+// Datums can also contain one of the following extra types
+// * binary strings -> Binary
+// * timestamps -> Time
+// * points. lines and polygons -> not implemented
+class Datum {
+public:
+ Datum() : type(Type::INVALID), value() {}
+ Datum(Nil) : type(Type::NIL), value() { }
+ Datum(bool boolean_) : type(Type::BOOLEAN), value(boolean_) { }
+ Datum(double number_) : type(Type::NUMBER), value(number_) { }
+ Datum(const std::string& string_) : type(Type::STRING), value(string_) { }
+ Datum(std::string&& string_) : type(Type::STRING), value(std::move(string_)) { }
+ Datum(const Array& array_) : type(Type::ARRAY), value(array_) { }
+ Datum(Array&& array_) : type(Type::ARRAY), value(std::move(array_)) { }
+ Datum(const Binary& binary) : type(Type::BINARY), value(binary) { }
+ Datum(Binary&& binary) : type(Type::BINARY), value(std::move(binary)) { }
+ Datum(const Time time) : type(Type::TIME), value(time) { }
+ Datum(const Object& object_) : type(Type::OBJECT), value(object_) { }
+ Datum(Object&& object_) : type(Type::OBJECT), value(std::move(object_)) { }
+ Datum(const Datum& other) : type(other.type), value(other.type, other.value) { }
+ Datum(Datum&& other) : type(other.type), value(other.type, std::move(other.value)) { }
+
+ Datum& operator=(const Datum& other) {
+ value.destroy(type);
+ type = other.type;
+ value.set(type, other.value);
+ return *this;
+ }
+
+ Datum& operator=(Datum&& other) {
+ value.destroy(type);
+ type = other.type;
+ value.set(type, std::move(other.value));
+ return *this;
+ }
+
+ Datum(unsigned short number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed short number_) : Datum(static_cast<double>(number_)) { }
+ Datum(unsigned int number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed int number_) : Datum(static_cast<double>(number_)) { }
+ Datum(unsigned long number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed long number_) : Datum(static_cast<double>(number_)) { }
+ Datum(unsigned long long number_) : Datum(static_cast<double>(number_)) { }
+ Datum(signed long long number_) : Datum(static_cast<double>(number_)) { }
+
+ Datum(Protocol::Term::TermType type) : Datum(static_cast<double>(type)) { }
+ Datum(const char* string) : Datum(static_cast<std::string>(string)) { }
+
+ // Cursors are implicitly converted into datums
+ Datum(Cursor&&);
+ Datum(const Cursor&);
+
+ template <class T>
+ Datum(const std::map<std::string, T>& map) : type(Type::OBJECT), value(Object()) {
+ for (const auto& it : map) {
+ value.object.emplace(it.left, Datum(it.right));
+ }
+ }
+
+ template <class T>
+ Datum(std::map<std::string, T>&& map) : type(Type::OBJECT), value(Object()) {
+ for (auto& it : map) {
+ value.object.emplace(it.first, Datum(std::move(it.second)));
+ }
+ }
+
+ template <class T>
+ Datum(const std::vector<T>& vec) : type(Type::ARRAY), value(Array()) {
+ for (const auto& it : vec) {
+ value.array.emplace_back(it);
+ }
+ }
+
+ template <class T>
+ Datum(std::vector<T>&& vec) : type(Type::ARRAY), value(Array()) {
+ for (auto& it : vec) {
+ value.array.emplace_back(std::move(it));
+ }
+ }
+
+ ~Datum() {
+ value.destroy(type);
+ }
+
+ // Apply a visitor
+ template <class R, class F, class ...A>
+ R apply(F f, A&& ...args) const & {
+ switch (type) {
+ case Type::NIL: return f(Nil(), std::forward<A>(args)...); break;
+ case Type::BOOLEAN: return f(value.boolean, std::forward<A>(args)...); break;
+ case Type::NUMBER: return f(value.number, std::forward<A>(args)...); break;
+ case Type::STRING: return f(value.string, std::forward<A>(args)...); break;
+ case Type::OBJECT: return f(value.object, std::forward<A>(args)...); break;
+ case Type::ARRAY: return f(value.array, std::forward<A>(args)...); break;
+ case Type::BINARY: return f(value.binary, std::forward<A>(args)...); break;
+ case Type::TIME: return f(value.time, std::forward<A>(args)...); break;
+ default:
+ throw Error("internal error: no such datum type %d", static_cast<int>(type));
+ }
+ }
+
+ template <class R, class F, class ...A>
+ R apply(F f, A&& ...args) && {
+ switch (type) {
+ case Type::NIL: return f(Nil(), std::forward<A>(args)...); break;
+ case Type::BOOLEAN: return f(std::move(value.boolean), std::forward<A>(args)...); break;
+ case Type::NUMBER: return f(std::move(value.number), std::forward<A>(args)...); break;
+ case Type::STRING: return f(std::move(value.string), std::forward<A>(args)...); break;
+ case Type::OBJECT: return f(std::move(value.object), std::forward<A>(args)...); break;
+ case Type::ARRAY: return f(std::move(value.array), std::forward<A>(args)...); break;
+ case Type::BINARY: return f(std::move(value.binary), std::forward<A>(args)...); break;
+ case Type::TIME: return f(std::move(value.time), std::forward<A>(args)...); break;
+ default:
+ throw Error("internal error: no such datum type %d", static_cast<int>(type));
+ }
+ }
+
+ bool is_nil() const;
+ bool is_boolean() const;
+ bool is_number() const;
+ bool is_string() const;
+ bool is_object() const;
+ bool is_array() const;
+ bool is_binary() const;
+ bool is_time() const;
+
+ // get_* returns nullptr if the datum has a different type
+
+ bool* get_boolean();
+ const bool* get_boolean() const;
+ double* get_number();
+ const double* get_number() const;
+ std::string* get_string();
+ const std::string* get_string() const;
+ Object* get_object();
+ const Object* get_object() const;
+ Datum* get_field(std::string);
+ const Datum* get_field(std::string) const;
+ Array* get_array();
+ const Array* get_array() const;
+ Datum* get_nth(size_t);
+ const Datum* get_nth(size_t) const;
+ Binary* get_binary();
+ const Binary* get_binary() const;
+ Time* get_time();
+ const Time* get_time() const;
+
+ // extract_* throws an exception if the types don't match
+
+ bool& extract_boolean();
+ double& extract_number();
+ std::string& extract_string();
+ Object& extract_object();
+ Datum& extract_field(std::string);
+ Array& extract_array();
+ Datum& extract_nth(size_t);
+ Binary& extract_binary();
+ Time& extract_time();
+
+ // negative, zero or positive if this datum is smaller, identical or larger than the other one, respectively
+ // This is meant to match the results of RethinkDB's comparison operators
+ int compare(const Datum&) const;
+
+ // Deep equality
+ bool operator== (const Datum&) const;
+
+ // Recusively replace non-JSON types into objects that represent them
+ Datum to_raw() const;
+
+ // Recursively replace objects with a $reql_type$ field into the datum they represent
+ Datum from_raw() const;
+
+ template <class json_writer_t> void write_json(json_writer_t *writer) const;
+
+ std::string as_json() const;
+ static Datum from_json(const std::string&);
+
+ bool is_valid() const { return type != Type::INVALID; }
+
+private:
+ enum class Type {
+ INVALID, // default constructed
+ ARRAY, BOOLEAN, NIL, NUMBER, OBJECT, BINARY, STRING, TIME
+ // POINT, LINE, POLYGON
+ };
+ Type type;
+
+ union datum_value {
+ bool boolean;
+ double number;
+ std::string string;
+ Object object;
+ Array array;
+ Binary binary;
+ Time time;
+
+ datum_value() { }
+ datum_value(bool boolean_) : boolean(boolean_) { }
+ datum_value(double number_) : number(number_) { }
+ datum_value(const std::string& string_) : string(string_) { }
+ datum_value(std::string&& string_) : string(std::move(string_)) { }
+ datum_value(const Object& object_) : object(object_) { }
+ datum_value(Object&& object_) : object(std::move(object_)) { }
+ datum_value(const Array& array_) : array(array_) { }
+ datum_value(Array&& array_) : array(std::move(array_)) { }
+ datum_value(const Binary& binary_) : binary(binary_) { }
+ datum_value(Binary&& binary_) : binary(std::move(binary_)) { }
+ datum_value(Time time) : time(std::move(time)) { }
+
+ datum_value(Type type, const datum_value& other){
+ set(type, other);
+ }
+
+ datum_value(Type type, datum_value&& other){
+ set(type, std::move(other));
+ }
+
+ void set(Type type, datum_value&& other) {
+ switch(type){
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: new (this) bool(other.boolean); break;
+ case Type::NUMBER: new (this) double(other.number); break;
+ case Type::STRING: new (this) std::string(std::move(other.string)); break;
+ case Type::OBJECT: new (this) Object(std::move(other.object)); break;
+ case Type::ARRAY: new (this) Array(std::move(other.array)); break;
+ case Type::BINARY: new (this) Binary(std::move(other.binary)); break;
+ case Type::TIME: new (this) Time(std::move(other.time)); break;
+ }
+ }
+
+ void set(Type type, const datum_value& other) {
+ switch(type){
+ case Type::NIL: case Type::INVALID: break;
+ case Type::BOOLEAN: new (this) bool(other.boolean); break;
+ case Type::NUMBER: new (this) double(other.number); break;
+ case Type::STRING: new (this) std::string(other.string); break;
+ case Type::OBJECT: new (this) Object(other.object); break;
+ case Type::ARRAY: new (this) Array(other.array); break;
+ case Type::BINARY: new (this) Binary(other.binary); break;
+ case Type::TIME: new (this) Time(other.time); break;
+ }
+ }
+
+ void destroy(Type type) {
+ switch(type){
+ case Type::INVALID: break;
+ case Type::NIL: break;
+ case Type::BOOLEAN: break;
+ case Type::NUMBER: break;
+ case Type::STRING: { typedef std::string str; string.~str(); } break;
+ case Type::OBJECT: object.~Object(); break;
+ case Type::ARRAY: array.~Array(); break;
+ case Type::BINARY: binary.~Binary(); break;
+ case Type::TIME: time.~Time(); break;
+ }
+ }
+
+ ~datum_value() { }
+ };
+
+ datum_value value;
+};
+
+}
diff --git a/ext/librethinkdbxx/src/error.h b/ext/librethinkdbxx/src/error.h
new file mode 100644
index 00000000..ab75e248
--- /dev/null
+++ b/ext/librethinkdbxx/src/error.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <cstdarg>
+#include <cstring>
+#include <string>
+#include <cerrno>
+
+namespace RethinkDB {
+
+// All errors thrown by the server have this type
+struct Error {
+ template <class ...T>
+ explicit Error(const char* format_, T... A) {
+ format(format_, A...);
+ }
+
+ Error() = default;
+ Error(Error&&) = default;
+ Error(const Error&) = default;
+
+ Error& operator= (Error&& other) {
+ message = std::move(other.message);
+ return *this;
+ }
+
+ static Error from_errno(const char* str){
+ return Error("%s: %s", str, strerror(errno));
+ }
+
+ // The error message
+ std::string message;
+
+private:
+ const size_t max_message_size = 2048;
+
+ void format(const char* format_, ...) {
+ va_list args;
+ va_start(args, format_);
+ char message_[max_message_size];
+ vsnprintf(message_, max_message_size, format_, args);
+ va_end(args);
+ message = message_;
+ }
+};
+
+}
diff --git a/ext/librethinkdbxx/src/exceptions.h b/ext/librethinkdbxx/src/exceptions.h
new file mode 100644
index 00000000..08c0b0a0
--- /dev/null
+++ b/ext/librethinkdbxx/src/exceptions.h
@@ -0,0 +1,13 @@
+#ifndef EXCEPTIONS_H
+#define EXCEPTIONS_H
+
+namespace RethinkDB {
+
+class TimeoutException : public std::exception {
+public:
+ const char *what() const throw () { return "operation timed out"; }
+};
+
+}
+
+#endif // EXCEPTIONS_H
diff --git a/ext/librethinkdbxx/src/json.cc b/ext/librethinkdbxx/src/json.cc
new file mode 100644
index 00000000..c908eefb
--- /dev/null
+++ b/ext/librethinkdbxx/src/json.cc
@@ -0,0 +1,62 @@
+#include "json_p.h"
+#include "error.h"
+#include "utils.h"
+
+#include "rapidjson-config.h"
+#include "rapidjson/document.h"
+#include "rapidjson/stringbuffer.h"
+#include "rapidjson/writer.h"
+#include "rapidjson/prettywriter.h"
+
+namespace RethinkDB {
+
+Datum read_datum(const std::string& json) {
+ rapidjson::Document document;
+ document.Parse(json);
+ return read_datum(document);
+}
+
+Datum read_datum(const rapidjson::Value &json) {
+ switch(json.GetType()) {
+ case rapidjson::kNullType: return Nil();
+ case rapidjson::kFalseType: return false;
+ case rapidjson::kTrueType: return true;
+ case rapidjson::kNumberType: return json.GetDouble();
+ case rapidjson::kStringType:
+ return std::string(json.GetString(), json.GetStringLength());
+
+ case rapidjson::kObjectType: {
+ Object result;
+ for (rapidjson::Value::ConstMemberIterator it = json.MemberBegin();
+ it != json.MemberEnd(); ++it) {
+ result.insert(std::make_pair(std::string(it->name.GetString(),
+ it->name.GetStringLength()),
+ read_datum(it->value)));
+ }
+
+ if (result.count("$reql_type$"))
+ return Datum(std::move(result)).from_raw();
+ return std::move(result);
+ } break;
+ case rapidjson::kArrayType: {
+ Array result;
+ result.reserve(json.Size());
+ for (rapidjson::Value::ConstValueIterator it = json.Begin();
+ it != json.End(); ++it) {
+ result.push_back(read_datum(*it));
+ }
+ return std::move(result);
+ } break;
+ default:
+ throw Error("invalid rapidjson value");
+ }
+}
+
+std::string write_datum(const Datum& datum) {
+ rapidjson::StringBuffer buffer;
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
+ datum.write_json(&writer);
+ return std::string(buffer.GetString(), buffer.GetSize());
+}
+
+}
diff --git a/ext/librethinkdbxx/src/json_p.h b/ext/librethinkdbxx/src/json_p.h
new file mode 100644
index 00000000..ebf537a9
--- /dev/null
+++ b/ext/librethinkdbxx/src/json_p.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "datum.h"
+
+namespace rapidjson {
+ class CrtAllocator;
+ template<typename> struct UTF8;
+ template <typename, typename> class GenericValue;
+ template <typename> class MemoryPoolAllocator;
+ typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+}
+
+namespace RethinkDB {
+
+Datum read_datum(const std::string&);
+Datum read_datum(const rapidjson::Value &json);
+std::string write_datum(const Datum&);
+
+}
diff --git a/ext/librethinkdbxx/src/rapidjson-config.h b/ext/librethinkdbxx/src/rapidjson-config.h
new file mode 100644
index 00000000..320c4048
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson-config.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#define RAPIDJSON_HAS_STDSTRING 1
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
+#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
+#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag
diff --git a/ext/librethinkdbxx/src/rapidjson/allocators.h b/ext/librethinkdbxx/src/rapidjson/allocators.h
new file mode 100644
index 00000000..c7059697
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/allocators.h
@@ -0,0 +1,263 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ALLOCATORS_H_
+#define RAPIDJSON_ALLOCATORS_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Allocator
+
+/*! \class rapidjson::Allocator
+ \brief Concept for allocating, resizing and freeing memory block.
+
+ Note that Malloc() and Realloc() are non-static but Free() is static.
+
+ So if an allocator need to support Free(), it needs to put its pointer in
+ the header of memory block.
+
+\code
+concept Allocator {
+ static const bool kNeedFree; //!< Whether this allocator needs to call Free().
+
+ // Allocate a memory block.
+ // \param size of the memory block in bytes.
+ // \returns pointer to the memory block.
+ void* Malloc(size_t size);
+
+ // Resize a memory block.
+ // \param originalPtr The pointer to current memory block. Null pointer is permitted.
+ // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
+ // \param newSize the new size in bytes.
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
+
+ // Free a memory block.
+ // \param pointer to the memory block. Null pointer is permitted.
+ static void Free(void *ptr);
+};
+\endcode
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+// CrtAllocator
+
+//! C-runtime library allocator.
+/*! This class is just wrapper for standard C library memory routines.
+ \note implements Allocator concept
+*/
+class CrtAllocator {
+public:
+ static const bool kNeedFree = true;
+ void* Malloc(size_t size) {
+ if (size) // behavior of malloc(0) is implementation defined.
+ return std::malloc(size);
+ else
+ return NULL; // standardize to returning NULL.
+ }
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+ (void)originalSize;
+ if (newSize == 0) {
+ std::free(originalPtr);
+ return NULL;
+ }
+ return std::realloc(originalPtr, newSize);
+ }
+ static void Free(void *ptr) { std::free(ptr); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryPoolAllocator
+
+//! Default memory allocator used by the parser and DOM.
+/*! This allocator allocate memory blocks from pre-allocated memory chunks.
+
+ It does not free memory blocks. And Realloc() only allocate new memory.
+
+ The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
+
+ User may also supply a buffer as the first chunk.
+
+ If the user-buffer is full then additional chunks are allocated by BaseAllocator.
+
+ The user-buffer is not deallocated by this allocator.
+
+ \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
+ \note implements Allocator concept
+*/
+template <typename BaseAllocator = CrtAllocator>
+class MemoryPoolAllocator {
+public:
+ static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
+
+ //! Constructor with chunkSize.
+ /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+ \param baseAllocator The allocator for allocating memory chunks.
+ */
+ MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+ chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+ {
+ }
+
+ //! Constructor with user-supplied buffer.
+ /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
+
+ The user buffer will not be deallocated when this allocator is destructed.
+
+ \param buffer User supplied buffer.
+ \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
+ \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+ \param baseAllocator The allocator for allocating memory chunks.
+ */
+ MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+ chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
+ {
+ RAPIDJSON_ASSERT(buffer != 0);
+ RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
+ chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
+ chunkHead_->capacity = size - sizeof(ChunkHeader);
+ chunkHead_->size = 0;
+ chunkHead_->next = 0;
+ }
+
+ //! Destructor.
+ /*! This deallocates all memory chunks, excluding the user-supplied buffer.
+ */
+ ~MemoryPoolAllocator() {
+ Clear();
+ RAPIDJSON_DELETE(ownBaseAllocator_);
+ }
+
+ //! Deallocates all memory chunks, excluding the user-supplied buffer.
+ void Clear() {
+ while (chunkHead_ && chunkHead_ != userBuffer_) {
+ ChunkHeader* next = chunkHead_->next;
+ baseAllocator_->Free(chunkHead_);
+ chunkHead_ = next;
+ }
+ if (chunkHead_ && chunkHead_ == userBuffer_)
+ chunkHead_->size = 0; // Clear user buffer
+ }
+
+ //! Computes the total capacity of allocated memory chunks.
+ /*! \return total capacity in bytes.
+ */
+ size_t Capacity() const {
+ size_t capacity = 0;
+ for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+ capacity += c->capacity;
+ return capacity;
+ }
+
+ //! Computes the memory blocks allocated.
+ /*! \return total used bytes.
+ */
+ size_t Size() const {
+ size_t size = 0;
+ for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
+ size += c->size;
+ return size;
+ }
+
+ //! Allocates a memory block. (concept Allocator)
+ void* Malloc(size_t size) {
+ if (!size)
+ return NULL;
+
+ size = RAPIDJSON_ALIGN(size);
+ if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
+ AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
+
+ void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
+ chunkHead_->size += size;
+ return buffer;
+ }
+
+ //! Resizes a memory block (concept Allocator)
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+ if (originalPtr == 0)
+ return Malloc(newSize);
+
+ if (newSize == 0)
+ return NULL;
+
+ originalSize = RAPIDJSON_ALIGN(originalSize);
+ newSize = RAPIDJSON_ALIGN(newSize);
+
+ // Do not shrink if new size is smaller than original
+ if (originalSize >= newSize)
+ return originalPtr;
+
+ // Simply expand it if it is the last allocation and there is sufficient space
+ if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
+ size_t increment = static_cast<size_t>(newSize - originalSize);
+ if (chunkHead_->size + increment <= chunkHead_->capacity) {
+ chunkHead_->size += increment;
+ return originalPtr;
+ }
+ }
+
+ // Realloc process: allocate and copy memory, do not free original buffer.
+ void* newBuffer = Malloc(newSize);
+ RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
+ if (originalSize)
+ std::memcpy(newBuffer, originalPtr, originalSize);
+ return newBuffer;
+ }
+
+ //! Frees a memory block (concept Allocator)
+ static void Free(void *ptr) { (void)ptr; } // Do nothing
+
+private:
+ //! Copy constructor is not permitted.
+ MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
+ //! Copy assignment operator is not permitted.
+ MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
+
+ //! Creates a new chunk.
+ /*! \param capacity Capacity of the chunk in bytes.
+ */
+ void AddChunk(size_t capacity) {
+ if (!baseAllocator_)
+ ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
+ ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity));
+ chunk->capacity = capacity;
+ chunk->size = 0;
+ chunk->next = chunkHead_;
+ chunkHead_ = chunk;
+ }
+
+ static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
+
+ //! Chunk header for perpending to each chunk.
+ /*! Chunks are stored as a singly linked list.
+ */
+ struct ChunkHeader {
+ size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
+ size_t size; //!< Current size of allocated memory in bytes.
+ ChunkHeader *next; //!< Next chunk in the linked list.
+ };
+
+ ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
+ size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
+ void *userBuffer_; //!< User supplied buffer.
+ BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
+ BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/document.h b/ext/librethinkdbxx/src/rapidjson/document.h
new file mode 100644
index 00000000..f7f846f2
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/document.h
@@ -0,0 +1,2575 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_DOCUMENT_H_
+#define RAPIDJSON_DOCUMENT_H_
+
+/*! \file document.h */
+
+#include "reader.h"
+#include "internal/meta.h"
+#include "internal/strfunc.h"
+#include "memorystream.h"
+#include "encodedstream.h"
+#include <new> // placement new
+#include <limits>
+
+RAPIDJSON_DIAG_PUSH
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_OFF(effc++)
+#if __GNUC__ >= 6
+RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions
+#endif
+#endif // __GNUC__
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+#include <iterator> // std::iterator, std::random_access_iterator_tag
+#endif
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// Forward declaration.
+template <typename Encoding, typename Allocator>
+class GenericValue;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+//! Name-value pair in a JSON object value.
+/*!
+ This class was internal to GenericValue. It used to be a inner struct.
+ But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct.
+ https://code.google.com/p/rapidjson/issues/detail?id=64
+*/
+template <typename Encoding, typename Allocator>
+struct GenericMember {
+ GenericValue<Encoding, Allocator> name; //!< name of member (must be a string)
+ GenericValue<Encoding, Allocator> value; //!< value of member.
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericMemberIterator
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+
+//! (Constant) member iterator for a JSON object value
+/*!
+ \tparam Const Is this a constant iterator?
+ \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
+ \tparam Allocator Allocator type for allocating memory of object, array and string.
+
+ This class implements a Random Access Iterator for GenericMember elements
+ of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements].
+
+ \note This iterator implementation is mainly intended to avoid implicit
+ conversions from iterator values to \c NULL,
+ e.g. from GenericValue::FindMember.
+
+ \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a
+ pointer-based implementation, if your platform doesn't provide
+ the C++ <iterator> header.
+
+ \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator
+ */
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator
+ : public std::iterator<std::random_access_iterator_tag
+ , typename internal::MaybeAddConst<Const,GenericMember<Encoding,Allocator> >::Type> {
+
+ friend class GenericValue<Encoding,Allocator>;
+ template <bool, typename, typename> friend class GenericMemberIterator;
+
+ typedef GenericMember<Encoding,Allocator> PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef std::iterator<std::random_access_iterator_tag,ValueType> BaseType;
+
+public:
+ //! Iterator type itself
+ typedef GenericMemberIterator Iterator;
+ //! Constant iterator type
+ typedef GenericMemberIterator<true,Encoding,Allocator> ConstIterator;
+ //! Non-constant iterator type
+ typedef GenericMemberIterator<false,Encoding,Allocator> NonConstIterator;
+
+ //! Pointer to (const) GenericMember
+ typedef typename BaseType::pointer Pointer;
+ //! Reference to (const) GenericMember
+ typedef typename BaseType::reference Reference;
+ //! Signed integer type (e.g. \c ptrdiff_t)
+ typedef typename BaseType::difference_type DifferenceType;
+
+ //! Default constructor (singular value)
+ /*! Creates an iterator pointing to no element.
+ \note All operations, except for comparisons, are undefined on such values.
+ */
+ GenericMemberIterator() : ptr_() {}
+
+ //! Iterator conversions to more const
+ /*!
+ \param it (Non-const) iterator to copy from
+
+ Allows the creation of an iterator from another GenericMemberIterator
+ that is "less const". Especially, creating a non-constant iterator
+ from a constant iterator are disabled:
+ \li const -> non-const (not ok)
+ \li const -> const (ok)
+ \li non-const -> const (ok)
+ \li non-const -> non-const (ok)
+
+ \note If the \c Const template parameter is already \c false, this
+ constructor effectively defines a regular copy-constructor.
+ Otherwise, the copy constructor is implicitly defined.
+ */
+ GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {}
+ Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; }
+
+ //! @name stepping
+ //@{
+ Iterator& operator++(){ ++ptr_; return *this; }
+ Iterator& operator--(){ --ptr_; return *this; }
+ Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; }
+ Iterator operator--(int){ Iterator old(*this); --ptr_; return old; }
+ //@}
+
+ //! @name increment/decrement
+ //@{
+ Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); }
+ Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); }
+
+ Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; }
+ Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; }
+ //@}
+
+ //! @name relations
+ //@{
+ bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; }
+ bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; }
+ bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; }
+ bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; }
+ bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; }
+ bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; }
+ //@}
+
+ //! @name dereference
+ //@{
+ Reference operator*() const { return *ptr_; }
+ Pointer operator->() const { return ptr_; }
+ Reference operator[](DifferenceType n) const { return ptr_[n]; }
+ //@}
+
+ //! Distance
+ DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; }
+
+private:
+ //! Internal constructor from plain pointer
+ explicit GenericMemberIterator(Pointer p) : ptr_(p) {}
+
+ Pointer ptr_; //!< raw pointer
+};
+
+#else // RAPIDJSON_NOMEMBERITERATORCLASS
+
+// class-based member iterator implementation disabled, use plain pointers
+
+template <bool Const, typename Encoding, typename Allocator>
+struct GenericMemberIterator;
+
+//! non-const GenericMemberIterator
+template <typename Encoding, typename Allocator>
+struct GenericMemberIterator<false,Encoding,Allocator> {
+ //! use plain pointer as iterator type
+ typedef GenericMember<Encoding,Allocator>* Iterator;
+};
+//! const GenericMemberIterator
+template <typename Encoding, typename Allocator>
+struct GenericMemberIterator<true,Encoding,Allocator> {
+ //! use plain const pointer as iterator type
+ typedef const GenericMember<Encoding,Allocator>* Iterator;
+};
+
+#endif // RAPIDJSON_NOMEMBERITERATORCLASS
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericStringRef
+
+//! Reference to a constant string (not taking a copy)
+/*!
+ \tparam CharType character type of the string
+
+ This helper class is used to automatically infer constant string
+ references for string literals, especially from \c const \b (!)
+ character arrays.
+
+ The main use is for creating JSON string values without copying the
+ source string via an \ref Allocator. This requires that the referenced
+ string pointers have a sufficient lifetime, which exceeds the lifetime
+ of the associated GenericValue.
+
+ \b Example
+ \code
+ Value v("foo"); // ok, no need to copy & calculate length
+ const char foo[] = "foo";
+ v.SetString(foo); // ok
+
+ const char* bar = foo;
+ // Value x(bar); // not ok, can't rely on bar's lifetime
+ Value x(StringRef(bar)); // lifetime explicitly guaranteed by user
+ Value y(StringRef(bar, 3)); // ok, explicitly pass length
+ \endcode
+
+ \see StringRef, GenericValue::SetString
+*/
+template<typename CharType>
+struct GenericStringRef {
+ typedef CharType Ch; //!< character type of the string
+
+ //! Create string reference from \c const character array
+#ifndef __clang__ // -Wdocumentation
+ /*!
+ This constructor implicitly creates a constant string reference from
+ a \c const character array. It has better performance than
+ \ref StringRef(const CharType*) by inferring the string \ref length
+ from the array length, and also supports strings containing null
+ characters.
+
+ \tparam N length of the string, automatically inferred
+
+ \param str Constant character array, lifetime assumed to be longer
+ than the use of the string in e.g. a GenericValue
+
+ \post \ref s == str
+
+ \note Constant complexity.
+ \note There is a hidden, private overload to disallow references to
+ non-const character arrays to be created via this constructor.
+ By this, e.g. function-scope arrays used to be filled via
+ \c snprintf are excluded from consideration.
+ In such cases, the referenced string should be \b copied to the
+ GenericValue instead.
+ */
+#endif
+ template<SizeType N>
+ GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT
+ : s(str), length(N-1) {}
+
+ //! Explicitly create string reference from \c const character pointer
+#ifndef __clang__ // -Wdocumentation
+ /*!
+ This constructor can be used to \b explicitly create a reference to
+ a constant string pointer.
+
+ \see StringRef(const CharType*)
+
+ \param str Constant character pointer, lifetime assumed to be longer
+ than the use of the string in e.g. a GenericValue
+
+ \post \ref s == str
+
+ \note There is a hidden, private overload to disallow references to
+ non-const character arrays to be created via this constructor.
+ By this, e.g. function-scope arrays used to be filled via
+ \c snprintf are excluded from consideration.
+ In such cases, the referenced string should be \b copied to the
+ GenericValue instead.
+ */
+#endif
+ explicit GenericStringRef(const CharType* str)
+ : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); }
+
+ //! Create constant string reference from pointer and length
+#ifndef __clang__ // -Wdocumentation
+ /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \param len length of the string, excluding the trailing NULL terminator
+
+ \post \ref s == str && \ref length == len
+ \note Constant complexity.
+ */
+#endif
+ GenericStringRef(const CharType* str, SizeType len)
+ : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); }
+
+ GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {}
+
+ GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; }
+
+ //! implicit conversion to plain CharType pointer
+ operator const Ch *() const { return s; }
+
+ const Ch* const s; //!< plain CharType pointer
+ const SizeType length; //!< length of the string (excluding the trailing NULL terminator)
+
+private:
+ //! Disallow construction from non-const array
+ template<SizeType N>
+ GenericStringRef(CharType (&str)[N]) /* = delete */;
+};
+
+//! Mark a character pointer as constant string
+/*! Mark a plain character pointer as a "string literal". This function
+ can be used to avoid copying a character string to be referenced as a
+ value in a JSON GenericValue object, if the string's lifetime is known
+ to be valid long enough.
+ \tparam CharType Character type of the string
+ \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \return GenericStringRef string reference object
+ \relatesalso GenericStringRef
+
+ \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const CharType* str) {
+ return GenericStringRef<CharType>(str, internal::StrLen(str));
+}
+
+//! Mark a character pointer as constant string
+/*! Mark a plain character pointer as a "string literal". This function
+ can be used to avoid copying a character string to be referenced as a
+ value in a JSON GenericValue object, if the string's lifetime is known
+ to be valid long enough.
+
+ This version has better performance with supplied length, and also
+ supports string containing null characters.
+
+ \tparam CharType character type of the string
+ \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \param length The length of source string.
+ \return GenericStringRef string reference object
+ \relatesalso GenericStringRef
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length) {
+ return GenericStringRef<CharType>(str, SizeType(length));
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+//! Mark a string object as constant string
+/*! Mark a string object (e.g. \c std::string) as a "string literal".
+ This function can be used to avoid copying a string to be referenced as a
+ value in a JSON GenericValue object, if the string's lifetime is known
+ to be valid long enough.
+
+ \tparam CharType character type of the string
+ \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
+ \return GenericStringRef string reference object
+ \relatesalso GenericStringRef
+ \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+*/
+template<typename CharType>
+inline GenericStringRef<CharType> StringRef(const std::basic_string<CharType>& str) {
+ return GenericStringRef<CharType>(str.data(), SizeType(str.size()));
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericValue type traits
+namespace internal {
+
+template <typename T, typename Encoding = void, typename Allocator = void>
+struct IsGenericValueImpl : FalseType {};
+
+// select candidates according to nested encoding and allocator types
+template <typename T> struct IsGenericValueImpl<T, typename Void<typename T::EncodingType>::Type, typename Void<typename T::AllocatorType>::Type>
+ : IsBaseOf<GenericValue<typename T::EncodingType, typename T::AllocatorType>, T>::Type {};
+
+// helper to match arbitrary GenericValue instantiations, including derived classes
+template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// TypeHelper
+
+namespace internal {
+
+template <typename ValueType, typename T>
+struct TypeHelper {};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, bool> {
+ static bool Is(const ValueType& v) { return v.IsBool(); }
+ static bool Get(const ValueType& v) { return v.GetBool(); }
+ static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); }
+ static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, int> {
+ static bool Is(const ValueType& v) { return v.IsInt(); }
+ static int Get(const ValueType& v) { return v.GetInt(); }
+ static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); }
+ static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, unsigned> {
+ static bool Is(const ValueType& v) { return v.IsUint(); }
+ static unsigned Get(const ValueType& v) { return v.GetUint(); }
+ static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); }
+ static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, int64_t> {
+ static bool Is(const ValueType& v) { return v.IsInt64(); }
+ static int64_t Get(const ValueType& v) { return v.GetInt64(); }
+ static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); }
+ static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, uint64_t> {
+ static bool Is(const ValueType& v) { return v.IsUint64(); }
+ static uint64_t Get(const ValueType& v) { return v.GetUint64(); }
+ static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); }
+ static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, double> {
+ static bool Is(const ValueType& v) { return v.IsDouble(); }
+ static double Get(const ValueType& v) { return v.GetDouble(); }
+ static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); }
+ static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, float> {
+ static bool Is(const ValueType& v) { return v.IsFloat(); }
+ static float Get(const ValueType& v) { return v.GetFloat(); }
+ static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); }
+ static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, const typename ValueType::Ch*> {
+ typedef const typename ValueType::Ch* StringType;
+ static bool Is(const ValueType& v) { return v.IsString(); }
+ static StringType Get(const ValueType& v) { return v.GetString(); }
+ static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); }
+ static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+
+#if RAPIDJSON_HAS_STDSTRING
+template<typename ValueType>
+struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > {
+ typedef std::basic_string<typename ValueType::Ch> StringType;
+ static bool Is(const ValueType& v) { return v.IsString(); }
+ static StringType Get(const ValueType& v) { return v.GetString(); }
+ static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); }
+};
+#endif
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::Array> {
+ typedef typename ValueType::Array ArrayType;
+ static bool Is(const ValueType& v) { return v.IsArray(); }
+ static ArrayType Get(ValueType& v) { return v.GetArray(); }
+ static ValueType& Set(ValueType& v, ArrayType data) { return v = data; }
+ static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::ConstArray> {
+ typedef typename ValueType::ConstArray ArrayType;
+ static bool Is(const ValueType& v) { return v.IsArray(); }
+ static ArrayType Get(const ValueType& v) { return v.GetArray(); }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::Object> {
+ typedef typename ValueType::Object ObjectType;
+ static bool Is(const ValueType& v) { return v.IsObject(); }
+ static ObjectType Get(ValueType& v) { return v.GetObject(); }
+ static ValueType& Set(ValueType& v, ObjectType data) { return v = data; }
+ static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; }
+};
+
+template<typename ValueType>
+struct TypeHelper<ValueType, typename ValueType::ConstObject> {
+ typedef typename ValueType::ConstObject ObjectType;
+ static bool Is(const ValueType& v) { return v.IsObject(); }
+ static ObjectType Get(const ValueType& v) { return v.GetObject(); }
+};
+
+} // namespace internal
+
+// Forward declarations
+template <bool, typename> class GenericArray;
+template <bool, typename> class GenericObject;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericValue
+
+//! Represents a JSON value. Use Value for UTF8 encoding and default allocator.
+/*!
+ A JSON value can be one of 7 types. This class is a variant type supporting
+ these types.
+
+ Use the Value if UTF8 and default allocator
+
+ \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
+ \tparam Allocator Allocator type for allocating memory of object, array and string.
+*/
+template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
+class GenericValue {
+public:
+ //! Name-value pair in an object.
+ typedef GenericMember<Encoding, Allocator> Member;
+ typedef Encoding EncodingType; //!< Encoding type from template parameter.
+ typedef Allocator AllocatorType; //!< Allocator type from template parameter.
+ typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
+ typedef GenericStringRef<Ch> StringRefType; //!< Reference to a constant string
+ typedef typename GenericMemberIterator<false,Encoding,Allocator>::Iterator MemberIterator; //!< Member iterator for iterating in object.
+ typedef typename GenericMemberIterator<true,Encoding,Allocator>::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object.
+ typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
+ typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
+ typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself.
+ typedef GenericArray<false, ValueType> Array;
+ typedef GenericArray<true, ValueType> ConstArray;
+ typedef GenericObject<false, ValueType> Object;
+ typedef GenericObject<true, ValueType> ConstObject;
+
+ //!@name Constructors and destructor.
+ //@{
+
+ //! Default constructor creates a null value.
+ GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) {
+ rhs.data_.f.flags = kNullFlag; // give up contents
+ }
+#endif
+
+private:
+ //! Copy constructor is not permitted.
+ GenericValue(const GenericValue& rhs);
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Moving from a GenericDocument is not permitted.
+ template <typename StackAllocator>
+ GenericValue(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs);
+
+ //! Move assignment from a GenericDocument is not permitted.
+ template <typename StackAllocator>
+ GenericValue& operator=(GenericDocument<Encoding,Allocator,StackAllocator>&& rhs);
+#endif
+
+public:
+
+ //! Constructor with JSON value type.
+ /*! This creates a Value of specified type with default content.
+ \param type Type of the value.
+ \note Default content for number is zero.
+ */
+ explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() {
+ static const uint16_t defaultFlags[7] = {
+ kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag,
+ kNumberAnyFlag
+ };
+ RAPIDJSON_ASSERT(type <= kNumberType);
+ data_.f.flags = defaultFlags[type];
+
+ // Use ShortString to store empty string.
+ if (type == kStringType)
+ data_.ss.SetLength(0);
+ }
+
+ //! Explicit copy constructor (with allocator)
+ /*! Creates a copy of a Value by using the given Allocator
+ \tparam SourceAllocator allocator of \c rhs
+ \param rhs Value to copy from (read-only)
+ \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator().
+ \see CopyFrom()
+ */
+ template< typename SourceAllocator >
+ GenericValue(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator & allocator);
+
+ //! Constructor for boolean value.
+ /*! \param b Boolean value
+ \note This constructor is limited to \em real boolean values and rejects
+ implicitly converted types like arbitrary pointers. Use an explicit cast
+ to \c bool, if you want to construct a boolean JSON value in such cases.
+ */
+#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
+ template <typename T>
+ explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<bool, T>))) RAPIDJSON_NOEXCEPT // See #472
+#else
+ explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
+#endif
+ : data_() {
+ // safe-guard against failing SFINAE
+ RAPIDJSON_STATIC_ASSERT((internal::IsSame<bool,T>::Value));
+ data_.f.flags = b ? kTrueFlag : kFalseFlag;
+ }
+
+ //! Constructor for int value.
+ explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.i64 = i;
+ data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag;
+ }
+
+ //! Constructor for unsigned value.
+ explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.u64 = u;
+ data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag);
+ }
+
+ //! Constructor for int64_t value.
+ explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.i64 = i64;
+ data_.f.flags = kNumberInt64Flag;
+ if (i64 >= 0) {
+ data_.f.flags |= kNumberUint64Flag;
+ if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
+ data_.f.flags |= kUintFlag;
+ if (!(static_cast<uint64_t>(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+ data_.f.flags |= kIntFlag;
+ }
+ else if (i64 >= static_cast<int64_t>(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+ data_.f.flags |= kIntFlag;
+ }
+
+ //! Constructor for uint64_t value.
+ explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() {
+ data_.n.u64 = u64;
+ data_.f.flags = kNumberUint64Flag;
+ if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
+ data_.f.flags |= kInt64Flag;
+ if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
+ data_.f.flags |= kUintFlag;
+ if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
+ data_.f.flags |= kIntFlag;
+ }
+
+ //! Constructor for double value.
+ explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; }
+
+ //! Constructor for constant string (i.e. do not make a copy of string)
+ GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); }
+
+ //! Constructor for constant string (i.e. do not make a copy of string)
+ explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); }
+
+ //! Constructor for copy-string (i.e. do make a copy of string)
+ GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); }
+
+ //! Constructor for copy-string (i.e. do make a copy of string)
+ GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Constructor for copy-string from a string object (i.e. do make a copy of string)
+ /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ GenericValue(const std::basic_string<Ch>& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); }
+#endif
+
+ //! Constructor for Array.
+ /*!
+ \param a An array obtained by \c GetArray().
+ \note \c Array is always pass-by-value.
+ \note the source array is moved into this value and the sourec array becomes empty.
+ */
+ GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) {
+ a.value_.data_ = Data();
+ a.value_.data_.f.flags = kArrayFlag;
+ }
+
+ //! Constructor for Object.
+ /*!
+ \param o An object obtained by \c GetObject().
+ \note \c Object is always pass-by-value.
+ \note the source object is moved into this value and the sourec object becomes empty.
+ */
+ GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) {
+ o.value_.data_ = Data();
+ o.value_.data_.f.flags = kObjectFlag;
+ }
+
+ //! Destructor.
+ /*! Need to destruct elements of array, members of object, or copy-string.
+ */
+ ~GenericValue() {
+ if (Allocator::kNeedFree) { // Shortcut by Allocator's trait
+ switch(data_.f.flags) {
+ case kArrayFlag:
+ {
+ GenericValue* e = GetElementsPointer();
+ for (GenericValue* v = e; v != e + data_.a.size; ++v)
+ v->~GenericValue();
+ Allocator::Free(e);
+ }
+ break;
+
+ case kObjectFlag:
+ for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+ m->~Member();
+ Allocator::Free(GetMembersPointer());
+ break;
+
+ case kCopyStringFlag:
+ Allocator::Free(const_cast<Ch*>(GetStringPointer()));
+ break;
+
+ default:
+ break; // Do nothing for other types.
+ }
+ }
+ }
+
+ //@}
+
+ //!@name Assignment operators
+ //@{
+
+ //! Assignment with move semantics.
+ /*! \param rhs Source of the assignment. It will become a null value after assignment.
+ */
+ GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_ASSERT(this != &rhs);
+ this->~GenericValue();
+ RawAssign(rhs);
+ return *this;
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move assignment in C++11
+ GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT {
+ return *this = rhs.Move();
+ }
+#endif
+
+ //! Assignment of constant string reference (no copy)
+ /*! \param str Constant string reference to be assigned
+ \note This overload is needed to avoid clashes with the generic primitive type assignment overload below.
+ \see GenericStringRef, operator=(T)
+ */
+ GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT {
+ GenericValue s(str);
+ return *this = s;
+ }
+
+ //! Assignment with primitive types.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param value The value to be assigned.
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref SetString(const Ch*, Allocator&) (for copying) or
+ \ref StringRef() (to explicitly mark the pointer as constant) instead.
+ All other pointer types would implicitly convert to \c bool,
+ use \ref SetBool() instead.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer<T>), (GenericValue&))
+ operator=(T value) {
+ GenericValue v(value);
+ return *this = v;
+ }
+
+ //! Deep-copy assignment from Value
+ /*! Assigns a \b copy of the Value to the current Value object
+ \tparam SourceAllocator Allocator type of \c rhs
+ \param rhs Value to copy from (read-only)
+ \param allocator Allocator to use for copying
+ */
+ template <typename SourceAllocator>
+ GenericValue& CopyFrom(const GenericValue<Encoding, SourceAllocator>& rhs, Allocator& allocator) {
+ RAPIDJSON_ASSERT(static_cast<void*>(this) != static_cast<void const*>(&rhs));
+ this->~GenericValue();
+ new (this) GenericValue(rhs, allocator);
+ return *this;
+ }
+
+ //! Exchange the contents of this value with those of other.
+ /*!
+ \param other Another value.
+ \note Constant complexity.
+ */
+ GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT {
+ GenericValue temp;
+ temp.RawAssign(*this);
+ RawAssign(other);
+ other.RawAssign(temp);
+ return *this;
+ }
+
+ //! free-standing swap function helper
+ /*!
+ Helper function to enable support for common swap implementation pattern based on \c std::swap:
+ \code
+ void swap(MyClass& a, MyClass& b) {
+ using std::swap;
+ swap(a.value, b.value);
+ // ...
+ }
+ \endcode
+ \see Swap()
+ */
+ friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+
+ //! Prepare Value for move semantics
+ /*! \return *this */
+ GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; }
+ //@}
+
+ //!@name Equal-to and not-equal-to operators
+ //@{
+ //! Equal-to operator
+ /*!
+ \note If an object contains duplicated named member, comparing equality with any object is always \c false.
+ \note Linear time complexity (number of all values in the subtree and total lengths of all strings).
+ */
+ template <typename SourceAllocator>
+ bool operator==(const GenericValue<Encoding, SourceAllocator>& rhs) const {
+ typedef GenericValue<Encoding, SourceAllocator> RhsType;
+ if (GetType() != rhs.GetType())
+ return false;
+
+ switch (GetType()) {
+ case kObjectType: // Warning: O(n^2) inner-loop
+ if (data_.o.size != rhs.data_.o.size)
+ return false;
+ for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) {
+ typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name);
+ if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value)
+ return false;
+ }
+ return true;
+
+ case kArrayType:
+ if (data_.a.size != rhs.data_.a.size)
+ return false;
+ for (SizeType i = 0; i < data_.a.size; i++)
+ if ((*this)[i] != rhs[i])
+ return false;
+ return true;
+
+ case kStringType:
+ return StringEqual(rhs);
+
+ case kNumberType:
+ if (IsDouble() || rhs.IsDouble()) {
+ double a = GetDouble(); // May convert from integer to double.
+ double b = rhs.GetDouble(); // Ditto
+ return a >= b && a <= b; // Prevent -Wfloat-equal
+ }
+ else
+ return data_.n.u64 == rhs.data_.n.u64;
+
+ default:
+ return true;
+ }
+ }
+
+ //! Equal-to operator with const C-string pointer
+ bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Equal-to operator with string object
+ /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ bool operator==(const std::basic_string<Ch>& rhs) const { return *this == GenericValue(StringRef(rhs)); }
+#endif
+
+ //! Equal-to operator with primitive types
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false
+ */
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>,internal::IsGenericValue<T> >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); }
+
+ //! Not-equal-to operator
+ /*! \return !(*this == rhs)
+ */
+ template <typename SourceAllocator>
+ bool operator!=(const GenericValue<Encoding, SourceAllocator>& rhs) const { return !(*this == rhs); }
+
+ //! Not-equal-to operator with const C-string pointer
+ bool operator!=(const Ch* rhs) const { return !(*this == rhs); }
+
+ //! Not-equal-to operator with arbitrary types
+ /*! \return !(*this == rhs)
+ */
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); }
+
+ //! Equal-to operator with arbitrary types (symmetric version)
+ /*! \return (rhs == lhs)
+ */
+ template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; }
+
+ //! Not-Equal-to operator with arbitrary types (symmetric version)
+ /*! \return !(rhs == lhs)
+ */
+ template <typename T> friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue<T>), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); }
+ //@}
+
+ //!@name Type
+ //@{
+
+ Type GetType() const { return static_cast<Type>(data_.f.flags & kTypeMask); }
+ bool IsNull() const { return data_.f.flags == kNullFlag; }
+ bool IsFalse() const { return data_.f.flags == kFalseFlag; }
+ bool IsTrue() const { return data_.f.flags == kTrueFlag; }
+ bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; }
+ bool IsObject() const { return data_.f.flags == kObjectFlag; }
+ bool IsArray() const { return data_.f.flags == kArrayFlag; }
+ bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; }
+ bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; }
+ bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; }
+ bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; }
+ bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; }
+ bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; }
+ bool IsString() const { return (data_.f.flags & kStringFlag) != 0; }
+
+ // Checks whether a number can be losslessly converted to a double.
+ bool IsLosslessDouble() const {
+ if (!IsNumber()) return false;
+ if (IsUint64()) {
+ uint64_t u = GetUint64();
+ volatile double d = static_cast<double>(u);
+ return (d >= 0.0)
+ && (d < static_cast<double>(std::numeric_limits<uint64_t>::max()))
+ && (u == static_cast<uint64_t>(d));
+ }
+ if (IsInt64()) {
+ int64_t i = GetInt64();
+ volatile double d = static_cast<double>(i);
+ return (d >= static_cast<double>(std::numeric_limits<int64_t>::min()))
+ && (d < static_cast<double>(std::numeric_limits<int64_t>::max()))
+ && (i == static_cast<int64_t>(d));
+ }
+ return true; // double, int, uint are always lossless
+ }
+
+ // Checks whether a number is a float (possible lossy).
+ bool IsFloat() const {
+ if ((data_.f.flags & kDoubleFlag) == 0)
+ return false;
+ double d = GetDouble();
+ return d >= -3.4028234e38 && d <= 3.4028234e38;
+ }
+ // Checks whether a number can be losslessly converted to a float.
+ bool IsLosslessFloat() const {
+ if (!IsNumber()) return false;
+ double a = GetDouble();
+ if (a < static_cast<double>(-std::numeric_limits<float>::max())
+ || a > static_cast<double>(std::numeric_limits<float>::max()))
+ return false;
+ double b = static_cast<double>(static_cast<float>(a));
+ return a >= b && a <= b; // Prevent -Wfloat-equal
+ }
+
+ //@}
+
+ //!@name Null
+ //@{
+
+ GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; }
+
+ //@}
+
+ //!@name Bool
+ //@{
+
+ bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; }
+ //!< Set boolean value
+ /*! \post IsBool() == true */
+ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; }
+
+ //@}
+
+ //!@name Object
+ //@{
+
+ //! Set this value as an empty object.
+ /*! \post IsObject() == true */
+ GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; }
+
+ //! Get the number of members in the object.
+ SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; }
+
+ //! Check whether the object is empty.
+ bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
+
+ //! Get a value from an object associated with the name.
+ /*! \pre IsObject() == true
+ \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType))
+ \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7.
+ Since 0.2, if the name is not correct, it will assert.
+ If user is unsure whether a member exists, user should use HasMember() first.
+ A better approach is to use FindMember().
+ \note Linear time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(GenericValue&)) operator[](T* name) {
+ GenericValue n(StringRef(name));
+ return (*this)[n];
+ }
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast<GenericValue&>(*this)[name]; }
+
+ //! Get a value from an object associated with the name.
+ /*! \pre IsObject() == true
+ \tparam SourceAllocator Allocator of the \c name value
+
+ \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen().
+ And it can also handle strings with embedded null characters.
+
+ \note Linear time complexity.
+ */
+ template <typename SourceAllocator>
+ GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) {
+ MemberIterator member = FindMember(name);
+ if (member != MemberEnd())
+ return member->value;
+ else {
+ RAPIDJSON_ASSERT(false); // see above note
+
+ // This will generate -Wexit-time-destructors in clang
+ // static GenericValue NullValue;
+ // return NullValue;
+
+ // Use static buffer and placement-new to prevent destruction
+ static char buffer[sizeof(GenericValue)];
+ return *new (buffer) GenericValue();
+ }
+ }
+ template <typename SourceAllocator>
+ const GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this)[name]; }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Get a value from an object associated with name (string object).
+ GenericValue& operator[](const std::basic_string<Ch>& name) { return (*this)[GenericValue(StringRef(name))]; }
+ const GenericValue& operator[](const std::basic_string<Ch>& name) const { return (*this)[GenericValue(StringRef(name))]; }
+#endif
+
+ //! Const member iterator
+ /*! \pre IsObject() == true */
+ ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); }
+ //! Const \em past-the-end member iterator
+ /*! \pre IsObject() == true */
+ ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); }
+ //! Member iterator
+ /*! \pre IsObject() == true */
+ MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); }
+ //! \em Past-the-end member iterator
+ /*! \pre IsObject() == true */
+ MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); }
+
+ //! Check whether a member exists in the object.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Whether a member with that name exists.
+ \note It is better to use FindMember() directly if you need the obtain the value as well.
+ \note Linear time complexity.
+ */
+ bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Check whether a member exists in the object with string object.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Whether a member with that name exists.
+ \note It is better to use FindMember() directly if you need the obtain the value as well.
+ \note Linear time complexity.
+ */
+ bool HasMember(const std::basic_string<Ch>& name) const { return FindMember(name) != MemberEnd(); }
+#endif
+
+ //! Check whether a member exists in the object with GenericValue name.
+ /*!
+ This version is faster because it does not need a StrLen(). It can also handle string with null character.
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Whether a member with that name exists.
+ \note It is better to use FindMember() directly if you need the obtain the value as well.
+ \note Linear time complexity.
+ */
+ template <typename SourceAllocator>
+ bool HasMember(const GenericValue<Encoding, SourceAllocator>& name) const { return FindMember(name) != MemberEnd(); }
+
+ //! Find member by name.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Iterator to member, if it exists.
+ Otherwise returns \ref MemberEnd().
+
+ \note Earlier versions of Rapidjson returned a \c NULL pointer, in case
+ the requested member doesn't exist. For consistency with e.g.
+ \c std::map, this has been changed to MemberEnd() now.
+ \note Linear time complexity.
+ */
+ MemberIterator FindMember(const Ch* name) {
+ GenericValue n(StringRef(name));
+ return FindMember(n);
+ }
+
+ ConstMemberIterator FindMember(const Ch* name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
+
+ //! Find member by name.
+ /*!
+ This version is faster because it does not need a StrLen(). It can also handle string with null character.
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Iterator to member, if it exists.
+ Otherwise returns \ref MemberEnd().
+
+ \note Earlier versions of Rapidjson returned a \c NULL pointer, in case
+ the requested member doesn't exist. For consistency with e.g.
+ \c std::map, this has been changed to MemberEnd() now.
+ \note Linear time complexity.
+ */
+ template <typename SourceAllocator>
+ MemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(name.IsString());
+ MemberIterator member = MemberBegin();
+ for ( ; member != MemberEnd(); ++member)
+ if (name.StringEqual(member->name))
+ break;
+ return member;
+ }
+ template <typename SourceAllocator> ConstMemberIterator FindMember(const GenericValue<Encoding, SourceAllocator>& name) const { return const_cast<GenericValue&>(*this).FindMember(name); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Find member by string object name.
+ /*!
+ \param name Member name to be searched.
+ \pre IsObject() == true
+ \return Iterator to member, if it exists.
+ Otherwise returns \ref MemberEnd().
+ */
+ MemberIterator FindMember(const std::basic_string<Ch>& name) { return FindMember(StringRef(name)); }
+ ConstMemberIterator FindMember(const std::basic_string<Ch>& name) const { return FindMember(StringRef(name)); }
+#endif
+
+ //! Add a member (name-value pair) to the object.
+ /*! \param name A string value as name of member.
+ \param value Value of any type.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \note The ownership of \c name and \c value will be transferred to this object on success.
+ \pre IsObject() && name.IsString()
+ \post name.IsNull() && value.IsNull()
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(name.IsString());
+
+ ObjectData& o = data_.o;
+ if (o.size >= o.capacity) {
+ if (o.capacity == 0) {
+ o.capacity = kDefaultObjectCapacity;
+ SetMembersPointer(reinterpret_cast<Member*>(allocator.Malloc(o.capacity * sizeof(Member))));
+ }
+ else {
+ SizeType oldCapacity = o.capacity;
+ o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5
+ SetMembersPointer(reinterpret_cast<Member*>(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member))));
+ }
+ }
+ Member* members = GetMembersPointer();
+ members[o.size].name.RawAssign(name);
+ members[o.size].value.RawAssign(value);
+ o.size++;
+ return *this;
+ }
+
+ //! Add a constant string value as member (name-value pair) to the object.
+ /*! \param name A string value as name of member.
+ \param value constant string reference as value of member.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+ \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) {
+ GenericValue v(value);
+ return AddMember(name, v, allocator);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Add a string object as member (name-value pair) to the object.
+ /*! \param name A string value as name of member.
+ \param value constant string reference as value of member.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+ \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below.
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(GenericValue& name, std::basic_string<Ch>& value, Allocator& allocator) {
+ GenericValue v(value, allocator);
+ return AddMember(name, v, allocator);
+ }
+#endif
+
+ //! Add any primitive value as member (name-value pair) to the object.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param name A string value as name of member.
+ \param value Value of primitive type \c T as value of member
+ \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
+ AddMember(StringRefType, StringRefType, Allocator&).
+ All other pointer types would implicitly convert to \c bool,
+ use an explicit cast instead, if needed.
+ \note Amortized Constant time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+ AddMember(GenericValue& name, T value, Allocator& allocator) {
+ GenericValue v(value);
+ return AddMember(name, v, allocator);
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) {
+ return AddMember(name, value, allocator);
+ }
+ GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) {
+ return AddMember(name, value, allocator);
+ }
+ GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) {
+ return AddMember(name, value, allocator);
+ }
+ GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) {
+ GenericValue n(name);
+ return AddMember(n, value, allocator);
+ }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+
+ //! Add a member (name-value pair) to the object.
+ /*! \param name A constant string reference as name of member.
+ \param value Value of any type.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \note The ownership of \c value will be transferred to this object on success.
+ \pre IsObject()
+ \post value.IsNull()
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) {
+ GenericValue n(name);
+ return AddMember(n, value, allocator);
+ }
+
+ //! Add a constant string value as member (name-value pair) to the object.
+ /*! \param name A constant string reference as name of member.
+ \param value constant string reference as value of member.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+ \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below.
+ \note Amortized Constant time complexity.
+ */
+ GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) {
+ GenericValue v(value);
+ return AddMember(name, v, allocator);
+ }
+
+ //! Add any primitive value as member (name-value pair) to the object.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param name A constant string reference as name of member.
+ \param value Value of primitive type \c T as value of member
+ \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \pre IsObject()
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref
+ AddMember(StringRefType, StringRefType, Allocator&).
+ All other pointer types would implicitly convert to \c bool,
+ use an explicit cast instead, if needed.
+ \note Amortized Constant time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+ AddMember(StringRefType name, T value, Allocator& allocator) {
+ GenericValue n(name);
+ return AddMember(n, value, allocator);
+ }
+
+ //! Remove all members in the object.
+ /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged.
+ \note Linear time complexity.
+ */
+ void RemoveAllMembers() {
+ RAPIDJSON_ASSERT(IsObject());
+ for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m)
+ m->~Member();
+ data_.o.size = 0;
+ }
+
+ //! Remove a member in object by its name.
+ /*! \param name Name of member to be removed.
+ \return Whether the member existed.
+ \note This function may reorder the object members. Use \ref
+ EraseMember(ConstMemberIterator) if you need to preserve the
+ relative order of the remaining members.
+ \note Linear time complexity.
+ */
+ bool RemoveMember(const Ch* name) {
+ GenericValue n(StringRef(name));
+ return RemoveMember(n);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool RemoveMember(const std::basic_string<Ch>& name) { return RemoveMember(GenericValue(StringRef(name))); }
+#endif
+
+ template <typename SourceAllocator>
+ bool RemoveMember(const GenericValue<Encoding, SourceAllocator>& name) {
+ MemberIterator m = FindMember(name);
+ if (m != MemberEnd()) {
+ RemoveMember(m);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ //! Remove a member in object by iterator.
+ /*! \param m member iterator (obtained by FindMember() or MemberBegin()).
+ \return the new iterator after removal.
+ \note This function may reorder the object members. Use \ref
+ EraseMember(ConstMemberIterator) if you need to preserve the
+ relative order of the remaining members.
+ \note Constant time complexity.
+ */
+ MemberIterator RemoveMember(MemberIterator m) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(data_.o.size > 0);
+ RAPIDJSON_ASSERT(GetMembersPointer() != 0);
+ RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd());
+
+ MemberIterator last(GetMembersPointer() + (data_.o.size - 1));
+ if (data_.o.size > 1 && m != last)
+ *m = *last; // Move the last one to this place
+ else
+ m->~Member(); // Only one left, just destroy
+ --data_.o.size;
+ return m;
+ }
+
+ //! Remove a member from an object by iterator.
+ /*! \param pos iterator to the member to remove
+ \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd()
+ \return Iterator following the removed element.
+ If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned.
+ \note This function preserves the relative order of the remaining object
+ members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator).
+ \note Linear time complexity.
+ */
+ MemberIterator EraseMember(ConstMemberIterator pos) {
+ return EraseMember(pos, pos +1);
+ }
+
+ //! Remove members in the range [first, last) from an object.
+ /*! \param first iterator to the first member to remove
+ \param last iterator following the last member to remove
+ \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd()
+ \return Iterator following the last removed element.
+ \note This function preserves the relative order of the remaining object
+ members.
+ \note Linear time complexity.
+ */
+ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
+ RAPIDJSON_ASSERT(IsObject());
+ RAPIDJSON_ASSERT(data_.o.size > 0);
+ RAPIDJSON_ASSERT(GetMembersPointer() != 0);
+ RAPIDJSON_ASSERT(first >= MemberBegin());
+ RAPIDJSON_ASSERT(first <= last);
+ RAPIDJSON_ASSERT(last <= MemberEnd());
+
+ MemberIterator pos = MemberBegin() + (first - MemberBegin());
+ for (MemberIterator itr = pos; itr != last; ++itr)
+ itr->~Member();
+ std::memmove(&*pos, &*last, static_cast<size_t>(MemberEnd() - last) * sizeof(Member));
+ data_.o.size -= static_cast<SizeType>(last - first);
+ return pos;
+ }
+
+ //! Erase a member in object by its name.
+ /*! \param name Name of member to be removed.
+ \return Whether the member existed.
+ \note Linear time complexity.
+ */
+ bool EraseMember(const Ch* name) {
+ GenericValue n(StringRef(name));
+ return EraseMember(n);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool EraseMember(const std::basic_string<Ch>& name) { return EraseMember(GenericValue(StringRef(name))); }
+#endif
+
+ template <typename SourceAllocator>
+ bool EraseMember(const GenericValue<Encoding, SourceAllocator>& name) {
+ MemberIterator m = FindMember(name);
+ if (m != MemberEnd()) {
+ EraseMember(m);
+ return true;
+ }
+ else
+ return false;
+ }
+
+ Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); }
+ ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); }
+
+ //@}
+
+ //!@name Array
+ //@{
+
+ //! Set this value as an empty array.
+ /*! \post IsArray == true */
+ GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; }
+
+ //! Get the number of elements in array.
+ SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; }
+
+ //! Get the capacity of array.
+ SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; }
+
+ //! Check whether the array is empty.
+ bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; }
+
+ //! Remove all elements in the array.
+ /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged.
+ \note Linear time complexity.
+ */
+ void Clear() {
+ RAPIDJSON_ASSERT(IsArray());
+ GenericValue* e = GetElementsPointer();
+ for (GenericValue* v = e; v != e + data_.a.size; ++v)
+ v->~GenericValue();
+ data_.a.size = 0;
+ }
+
+ //! Get an element from array by index.
+ /*! \pre IsArray() == true
+ \param index Zero-based index of element.
+ \see operator[](T*)
+ */
+ GenericValue& operator[](SizeType index) {
+ RAPIDJSON_ASSERT(IsArray());
+ RAPIDJSON_ASSERT(index < data_.a.size);
+ return GetElementsPointer()[index];
+ }
+ const GenericValue& operator[](SizeType index) const { return const_cast<GenericValue&>(*this)[index]; }
+
+ //! Element iterator
+ /*! \pre IsArray() == true */
+ ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); }
+ //! \em Past-the-end element iterator
+ /*! \pre IsArray() == true */
+ ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; }
+ //! Constant element iterator
+ /*! \pre IsArray() == true */
+ ConstValueIterator Begin() const { return const_cast<GenericValue&>(*this).Begin(); }
+ //! Constant \em past-the-end element iterator
+ /*! \pre IsArray() == true */
+ ConstValueIterator End() const { return const_cast<GenericValue&>(*this).End(); }
+
+ //! Request the array to have enough capacity to store elements.
+ /*! \param newCapacity The capacity that the array at least need to have.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \note Linear time complexity.
+ */
+ GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) {
+ RAPIDJSON_ASSERT(IsArray());
+ if (newCapacity > data_.a.capacity) {
+ SetElementsPointer(reinterpret_cast<GenericValue*>(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))));
+ data_.a.capacity = newCapacity;
+ }
+ return *this;
+ }
+
+ //! Append a GenericValue at the end of the array.
+ /*! \param value Value to be appended.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \pre IsArray() == true
+ \post value.IsNull() == true
+ \return The value itself for fluent API.
+ \note The ownership of \c value will be transferred to this array on success.
+ \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+ \note Amortized constant time complexity.
+ */
+ GenericValue& PushBack(GenericValue& value, Allocator& allocator) {
+ RAPIDJSON_ASSERT(IsArray());
+ if (data_.a.size >= data_.a.capacity)
+ Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator);
+ GetElementsPointer()[data_.a.size++].RawAssign(value);
+ return *this;
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericValue& PushBack(GenericValue&& value, Allocator& allocator) {
+ return PushBack(value, allocator);
+ }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+ //! Append a constant string reference at the end of the array.
+ /*! \param value Constant string reference to be appended.
+ \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator().
+ \pre IsArray() == true
+ \return The value itself for fluent API.
+ \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+ \note Amortized constant time complexity.
+ \see GenericStringRef
+ */
+ GenericValue& PushBack(StringRefType value, Allocator& allocator) {
+ return (*this).template PushBack<StringRefType>(value, allocator);
+ }
+
+ //! Append a primitive value at the end of the array.
+ /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t
+ \param value Value of primitive type T to be appended.
+ \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator().
+ \pre IsArray() == true
+ \return The value itself for fluent API.
+ \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient.
+
+ \note The source type \c T explicitly disallows all pointer types,
+ especially (\c const) \ref Ch*. This helps avoiding implicitly
+ referencing character strings with insufficient lifetime, use
+ \ref PushBack(GenericValue&, Allocator&) or \ref
+ PushBack(StringRefType, Allocator&).
+ All other pointer types would implicitly convert to \c bool,
+ use an explicit cast instead, if needed.
+ \note Amortized constant time complexity.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericValue&))
+ PushBack(T value, Allocator& allocator) {
+ GenericValue v(value);
+ return PushBack(v, allocator);
+ }
+
+ //! Remove the last element in the array.
+ /*!
+ \note Constant time complexity.
+ */
+ GenericValue& PopBack() {
+ RAPIDJSON_ASSERT(IsArray());
+ RAPIDJSON_ASSERT(!Empty());
+ GetElementsPointer()[--data_.a.size].~GenericValue();
+ return *this;
+ }
+
+ //! Remove an element of array by iterator.
+ /*!
+ \param pos iterator to the element to remove
+ \pre IsArray() == true && \ref Begin() <= \c pos < \ref End()
+ \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned.
+ \note Linear time complexity.
+ */
+ ValueIterator Erase(ConstValueIterator pos) {
+ return Erase(pos, pos + 1);
+ }
+
+ //! Remove elements in the range [first, last) of the array.
+ /*!
+ \param first iterator to the first element to remove
+ \param last iterator following the last element to remove
+ \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End()
+ \return Iterator following the last removed element.
+ \note Linear time complexity.
+ */
+ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) {
+ RAPIDJSON_ASSERT(IsArray());
+ RAPIDJSON_ASSERT(data_.a.size > 0);
+ RAPIDJSON_ASSERT(GetElementsPointer() != 0);
+ RAPIDJSON_ASSERT(first >= Begin());
+ RAPIDJSON_ASSERT(first <= last);
+ RAPIDJSON_ASSERT(last <= End());
+ ValueIterator pos = Begin() + (first - Begin());
+ for (ValueIterator itr = pos; itr != last; ++itr)
+ itr->~GenericValue();
+ std::memmove(pos, last, static_cast<size_t>(End() - last) * sizeof(GenericValue));
+ data_.a.size -= static_cast<SizeType>(last - first);
+ return pos;
+ }
+
+ Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); }
+ ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); }
+
+ //@}
+
+ //!@name Number
+ //@{
+
+ int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; }
+ unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; }
+ int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; }
+ uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; }
+
+ //! Get the value as double type.
+ /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless.
+ */
+ double GetDouble() const {
+ RAPIDJSON_ASSERT(IsNumber());
+ if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion.
+ if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double
+ if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double
+ if ((data_.f.flags & kInt64Flag) != 0) return static_cast<double>(data_.n.i64); // int64_t -> double (may lose precision)
+ RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast<double>(data_.n.u64); // uint64_t -> double (may lose precision)
+ }
+
+ //! Get the value as float type.
+ /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless.
+ */
+ float GetFloat() const {
+ return static_cast<float>(GetDouble());
+ }
+
+ GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; }
+ GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; }
+ GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; }
+ GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; }
+ GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; }
+ GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; }
+
+ //@}
+
+ //!@name String
+ //@{
+
+ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); }
+
+ //! Get the length of string.
+ /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength().
+ */
+ SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); }
+
+ //! Set this value as a string without copying source string.
+ /*! This version has better performance with supplied length, and also support string containing null character.
+ \param s source string pointer.
+ \param length The length of source string, excluding the trailing null terminator.
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() == s && GetStringLength() == length
+ \see SetString(StringRefType)
+ */
+ GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); }
+
+ //! Set this value as a string without copying source string.
+ /*! \param s source string reference
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() == s && GetStringLength() == s.length
+ */
+ GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; }
+
+ //! Set this value as a string by copying from source string.
+ /*! This version has better performance with supplied length, and also support string containing null character.
+ \param s source string.
+ \param length The length of source string, excluding the trailing null terminator.
+ \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+ */
+ GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; }
+
+ //! Set this value as a string by copying from source string.
+ /*! \param s source string.
+ \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length
+ */
+ GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Set this value as a string by copying from source string.
+ /*! \param s source string.
+ \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
+ \return The value itself for fluent API.
+ \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size()
+ \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ GenericValue& SetString(const std::basic_string<Ch>& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); }
+#endif
+
+ //@}
+
+ //!@name Array
+ //@{
+
+ //! Templated version for checking whether this value is type T.
+ /*!
+ \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string<Ch>
+ */
+ template <typename T>
+ bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); }
+
+ template <typename T>
+ T Get() const { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+ template <typename T>
+ T Get() { return internal::TypeHelper<ValueType, T>::Get(*this); }
+
+ template<typename T>
+ ValueType& Set(const T& data) { return internal::TypeHelper<ValueType, T>::Set(*this, data); }
+
+ template<typename T>
+ ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); }
+
+ //@}
+
+ //! Generate events of this value to a Handler.
+ /*! This function adopts the GoF visitor pattern.
+ Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
+ It can also be used to deep clone this value via GenericDocument, which is also a Handler.
+ \tparam Handler type of handler.
+ \param handler An object implementing concept Handler.
+ */
+ template <typename Handler>
+ bool Accept(Handler& handler) const {
+ switch(GetType()) {
+ case kNullType: return handler.Null();
+ case kFalseType: return handler.Bool(false);
+ case kTrueType: return handler.Bool(true);
+
+ case kObjectType:
+ if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
+ return false;
+ for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
+ RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator.
+ if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0)))
+ return false;
+ if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler)))
+ return false;
+ }
+ return handler.EndObject(data_.o.size);
+
+ case kArrayType:
+ if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
+ return false;
+ for (const GenericValue* v = Begin(); v != End(); ++v)
+ if (RAPIDJSON_UNLIKELY(!v->Accept(handler)))
+ return false;
+ return handler.EndArray(data_.a.size);
+
+ case kStringType:
+ return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
+
+ default:
+ RAPIDJSON_ASSERT(GetType() == kNumberType);
+ if (IsDouble()) return handler.Double(data_.n.d);
+ else if (IsInt()) return handler.Int(data_.n.i.i);
+ else if (IsUint()) return handler.Uint(data_.n.u.u);
+ else if (IsInt64()) return handler.Int64(data_.n.i64);
+ else return handler.Uint64(data_.n.u64);
+ }
+ }
+
+private:
+ template <typename, typename> friend class GenericValue;
+ template <typename, typename, typename> friend class GenericDocument;
+
+ enum {
+ kBoolFlag = 0x0008,
+ kNumberFlag = 0x0010,
+ kIntFlag = 0x0020,
+ kUintFlag = 0x0040,
+ kInt64Flag = 0x0080,
+ kUint64Flag = 0x0100,
+ kDoubleFlag = 0x0200,
+ kStringFlag = 0x0400,
+ kCopyFlag = 0x0800,
+ kInlineStrFlag = 0x1000,
+
+ // Initial flags of different types.
+ kNullFlag = kNullType,
+ kTrueFlag = kTrueType | kBoolFlag,
+ kFalseFlag = kFalseType | kBoolFlag,
+ kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag,
+ kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag,
+ kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag,
+ kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag,
+ kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag,
+ kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag,
+ kConstStringFlag = kStringType | kStringFlag,
+ kCopyStringFlag = kStringType | kStringFlag | kCopyFlag,
+ kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag,
+ kObjectFlag = kObjectType,
+ kArrayFlag = kArrayType,
+
+ kTypeMask = 0x07
+ };
+
+ static const SizeType kDefaultArrayCapacity = 16;
+ static const SizeType kDefaultObjectCapacity = 16;
+
+ struct Flag {
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION
+ char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer
+#elif RAPIDJSON_64BIT
+ char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes
+#else
+ char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes
+#endif
+ uint16_t flags;
+ };
+
+ struct String {
+ SizeType length;
+ SizeType hashcode; //!< reserved
+ const Ch* str;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars
+ // (excluding the terminating zero) and store a value to determine the length of the contained
+ // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string
+ // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as
+ // the string terminator as well. For getting the string length back from that value just use
+ // "MaxSize - str[LenPos]".
+ // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode,
+ // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings).
+ struct ShortString {
+ enum { MaxChars = sizeof(static_cast<Flag*>(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize };
+ Ch str[MaxChars];
+
+ inline static bool Usable(SizeType len) { return (MaxSize >= len); }
+ inline void SetLength(SizeType len) { str[LenPos] = static_cast<Ch>(MaxSize - len); }
+ inline SizeType GetLength() const { return static_cast<SizeType>(MaxSize - str[LenPos]); }
+ }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ // By using proper binary layout, retrieval of different integer types do not need conversions.
+ union Number {
+#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN
+ struct I {
+ int i;
+ char padding[4];
+ }i;
+ struct U {
+ unsigned u;
+ char padding2[4];
+ }u;
+#else
+ struct I {
+ char padding[4];
+ int i;
+ }i;
+ struct U {
+ char padding2[4];
+ unsigned u;
+ }u;
+#endif
+ int64_t i64;
+ uint64_t u64;
+ double d;
+ }; // 8 bytes
+
+ struct ObjectData {
+ SizeType size;
+ SizeType capacity;
+ Member* members;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ struct ArrayData {
+ SizeType size;
+ SizeType capacity;
+ GenericValue* elements;
+ }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode
+
+ union Data {
+ String s;
+ ShortString ss;
+ Number n;
+ ObjectData o;
+ ArrayData a;
+ Flag f;
+ }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+ RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); }
+ RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); }
+ RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); }
+ RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); }
+ RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); }
+ RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); }
+
+ // Initialize this value as array with initial data, without calling destructor.
+ void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
+ data_.f.flags = kArrayFlag;
+ if (count) {
+ GenericValue* e = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
+ SetElementsPointer(e);
+ std::memcpy(e, values, count * sizeof(GenericValue));
+ }
+ else
+ SetElementsPointer(0);
+ data_.a.size = data_.a.capacity = count;
+ }
+
+ //! Initialize this value as object with initial data, without calling destructor.
+ void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
+ data_.f.flags = kObjectFlag;
+ if (count) {
+ Member* m = static_cast<Member*>(allocator.Malloc(count * sizeof(Member)));
+ SetMembersPointer(m);
+ std::memcpy(m, members, count * sizeof(Member));
+ }
+ else
+ SetMembersPointer(0);
+ data_.o.size = data_.o.capacity = count;
+ }
+
+ //! Initialize this value as constant string, without calling destructor.
+ void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
+ data_.f.flags = kConstStringFlag;
+ SetStringPointer(s);
+ data_.s.length = s.length;
+ }
+
+ //! Initialize this value as copy string with initial data, without calling destructor.
+ void SetStringRaw(StringRefType s, Allocator& allocator) {
+ Ch* str = 0;
+ if (ShortString::Usable(s.length)) {
+ data_.f.flags = kShortStringFlag;
+ data_.ss.SetLength(s.length);
+ str = data_.ss.str;
+ } else {
+ data_.f.flags = kCopyStringFlag;
+ data_.s.length = s.length;
+ str = static_cast<Ch *>(allocator.Malloc((s.length + 1) * sizeof(Ch)));
+ SetStringPointer(str);
+ }
+ std::memcpy(str, s, s.length * sizeof(Ch));
+ str[s.length] = '\0';
+ }
+
+ //! Assignment without calling destructor
+ void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
+ data_ = rhs.data_;
+ // data_.f.flags = rhs.data_.f.flags;
+ rhs.data_.f.flags = kNullFlag;
+ }
+
+ template <typename SourceAllocator>
+ bool StringEqual(const GenericValue<Encoding, SourceAllocator>& rhs) const {
+ RAPIDJSON_ASSERT(IsString());
+ RAPIDJSON_ASSERT(rhs.IsString());
+
+ const SizeType len1 = GetStringLength();
+ const SizeType len2 = rhs.GetStringLength();
+ if(len1 != len2) { return false; }
+
+ const Ch* const str1 = GetString();
+ const Ch* const str2 = rhs.GetString();
+ if(str1 == str2) { return true; } // fast path for constant string
+
+ return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0);
+ }
+
+ Data data_;
+};
+
+//! GenericValue with UTF8 encoding
+typedef GenericValue<UTF8<> > Value;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericDocument
+
+//! A document for parsing JSON text as DOM.
+/*!
+ \note implements Handler concept
+ \tparam Encoding Encoding for both parsing and string storage.
+ \tparam Allocator Allocator for allocating memory for the DOM
+ \tparam StackAllocator Allocator for allocating memory for stack during parsing.
+ \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue.
+*/
+template <typename Encoding, typename Allocator = MemoryPoolAllocator<>, typename StackAllocator = CrtAllocator>
+class GenericDocument : public GenericValue<Encoding, Allocator> {
+public:
+ typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding.
+ typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of the document.
+ typedef Allocator AllocatorType; //!< Allocator type from template parameter.
+
+ //! Constructor
+ /*! Creates an empty document of specified type.
+ \param type Mandatory type of object to create.
+ \param allocator Optional allocator for allocating memory.
+ \param stackCapacity Optional initial capacity of stack in bytes.
+ \param stackAllocator Optional allocator for allocating memory for stack.
+ */
+ explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) :
+ GenericValue<Encoding, Allocator>(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+ }
+
+ //! Constructor
+ /*! Creates an empty document which type is Null.
+ \param allocator Optional allocator for allocating memory.
+ \param stackCapacity Optional initial capacity of stack in bytes.
+ \param stackAllocator Optional allocator for allocating memory for stack.
+ */
+ GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) :
+ allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
+ : ValueType(std::forward<ValueType>(rhs)), // explicit cast to avoid prohibited move from Document
+ allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ stack_(std::move(rhs.stack_)),
+ parseResult_(rhs.parseResult_)
+ {
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.parseResult_ = ParseResult();
+ }
+#endif
+
+ ~GenericDocument() {
+ Destroy();
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move assignment in C++11
+ GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
+ {
+ // The cast to ValueType is necessary here, because otherwise it would
+ // attempt to call GenericValue's templated assignment operator.
+ ValueType::operator=(std::forward<ValueType>(rhs));
+
+ // Calling the destructor here would prematurely call stack_'s destructor
+ Destroy();
+
+ allocator_ = rhs.allocator_;
+ ownAllocator_ = rhs.ownAllocator_;
+ stack_ = std::move(rhs.stack_);
+ parseResult_ = rhs.parseResult_;
+
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.parseResult_ = ParseResult();
+
+ return *this;
+ }
+#endif
+
+ //! Exchange the contents of this document with those of another.
+ /*!
+ \param rhs Another document.
+ \note Constant complexity.
+ \see GenericValue::Swap
+ */
+ GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT {
+ ValueType::Swap(rhs);
+ stack_.Swap(rhs.stack_);
+ internal::Swap(allocator_, rhs.allocator_);
+ internal::Swap(ownAllocator_, rhs.ownAllocator_);
+ internal::Swap(parseResult_, rhs.parseResult_);
+ return *this;
+ }
+
+ //! free-standing swap function helper
+ /*!
+ Helper function to enable support for common swap implementation pattern based on \c std::swap:
+ \code
+ void swap(MyClass& a, MyClass& b) {
+ using std::swap;
+ swap(a.doc, b.doc);
+ // ...
+ }
+ \endcode
+ \see Swap()
+ */
+ friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); }
+
+ //! Populate this document by a generator which produces SAX events.
+ /*! \tparam Generator A functor with <tt>bool f(Handler)</tt> prototype.
+ \param g Generator functor which sends SAX events to the parameter.
+ \return The document itself for fluent API.
+ */
+ template <typename Generator>
+ GenericDocument& Populate(Generator& g) {
+ ClearStackOnExit scope(*this);
+ if (g(*this)) {
+ RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+ ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+ }
+ return *this;
+ }
+
+ //!@name Parse from stream
+ //!@{
+
+ //! Parse JSON text from an input stream (with Encoding conversion)
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam SourceEncoding Encoding of input stream
+ \tparam InputStream Type of input stream, implementing Stream concept
+ \param is Input stream to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
+ GenericDocument& ParseStream(InputStream& is) {
+ GenericReader<SourceEncoding, Encoding, StackAllocator> reader(
+ stack_.HasAllocator() ? &stack_.GetAllocator() : 0);
+ ClearStackOnExit scope(*this);
+ parseResult_ = reader.template Parse<parseFlags>(is, *this);
+ if (parseResult_) {
+ RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
+ ValueType::operator=(*stack_.template Pop<ValueType>(1));// Move value from stack to document
+ }
+ return *this;
+ }
+
+ //! Parse JSON text from an input stream
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept
+ \param is Input stream to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <unsigned parseFlags, typename InputStream>
+ GenericDocument& ParseStream(InputStream& is) {
+ return ParseStream<parseFlags, Encoding, InputStream>(is);
+ }
+
+ //! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
+ /*! \tparam InputStream Type of input stream, implementing Stream concept
+ \param is Input stream to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <typename InputStream>
+ GenericDocument& ParseStream(InputStream& is) {
+ return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is);
+ }
+ //!@}
+
+ //!@name Parse in-place from mutable string
+ //!@{
+
+ //! Parse JSON text from a mutable string
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \param str Mutable zero-terminated string to be parsed.
+ \return The document itself for fluent API.
+ */
+ template <unsigned parseFlags>
+ GenericDocument& ParseInsitu(Ch* str) {
+ GenericInsituStringStream<Encoding> s(str);
+ return ParseStream<parseFlags | kParseInsituFlag>(s);
+ }
+
+ //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags)
+ /*! \param str Mutable zero-terminated string to be parsed.
+ \return The document itself for fluent API.
+ */
+ GenericDocument& ParseInsitu(Ch* str) {
+ return ParseInsitu<kParseDefaultFlags>(str);
+ }
+ //!@}
+
+ //!@name Parse from read-only string
+ //!@{
+
+ //! Parse JSON text from a read-only string (with Encoding conversion)
+ /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
+ \tparam SourceEncoding Transcoding from input Encoding
+ \param str Read-only zero-terminated string to be parsed.
+ */
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const typename SourceEncoding::Ch* str) {
+ RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+ GenericStringStream<SourceEncoding> s(str);
+ return ParseStream<parseFlags, SourceEncoding>(s);
+ }
+
+ //! Parse JSON text from a read-only string
+ /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
+ \param str Read-only zero-terminated string to be parsed.
+ */
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const Ch* str) {
+ return Parse<parseFlags, Encoding>(str);
+ }
+
+ //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags)
+ /*! \param str Read-only zero-terminated string to be parsed.
+ */
+ GenericDocument& Parse(const Ch* str) {
+ return Parse<kParseDefaultFlags>(str);
+ }
+
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) {
+ RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
+ MemoryStream ms(static_cast<const char*>(str), length * sizeof(typename SourceEncoding::Ch));
+ EncodedInputStream<SourceEncoding, MemoryStream> is(ms);
+ ParseStream<parseFlags, SourceEncoding>(is);
+ return *this;
+ }
+
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const Ch* str, size_t length) {
+ return Parse<parseFlags, Encoding>(str, length);
+ }
+
+ GenericDocument& Parse(const Ch* str, size_t length) {
+ return Parse<kParseDefaultFlags>(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ template <unsigned parseFlags, typename SourceEncoding>
+ GenericDocument& Parse(const std::basic_string<typename SourceEncoding::Ch>& str) {
+ // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t)
+ return Parse<parseFlags, SourceEncoding>(str.c_str());
+ }
+
+ template <unsigned parseFlags>
+ GenericDocument& Parse(const std::basic_string<Ch>& str) {
+ return Parse<parseFlags, Encoding>(str.c_str());
+ }
+
+ GenericDocument& Parse(const std::basic_string<Ch>& str) {
+ return Parse<kParseDefaultFlags>(str);
+ }
+#endif // RAPIDJSON_HAS_STDSTRING
+
+ //!@}
+
+ //!@name Handling parse errors
+ //!@{
+
+ //! Whether a parse error has occured in the last parsing.
+ bool HasParseError() const { return parseResult_.IsError(); }
+
+ //! Get the \ref ParseErrorCode of last parsing.
+ ParseErrorCode GetParseError() const { return parseResult_.Code(); }
+
+ //! Get the position of last parsing error in input, 0 otherwise.
+ size_t GetErrorOffset() const { return parseResult_.Offset(); }
+
+ //! Implicit conversion to get the last parse result
+#ifndef __clang // -Wdocumentation
+ /*! \return \ref ParseResult of the last parse operation
+
+ \code
+ Document doc;
+ ParseResult ok = doc.Parse(json);
+ if (!ok)
+ printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset());
+ \endcode
+ */
+#endif
+ operator ParseResult() const { return parseResult_; }
+ //!@}
+
+ //! Get the allocator of this document.
+ Allocator& GetAllocator() {
+ RAPIDJSON_ASSERT(allocator_);
+ return *allocator_;
+ }
+
+ //! Get the capacity of stack in bytes.
+ size_t GetStackCapacity() const { return stack_.GetCapacity(); }
+
+private:
+ // clear stack on any exit from ParseStream, e.g. due to exception
+ struct ClearStackOnExit {
+ explicit ClearStackOnExit(GenericDocument& d) : d_(d) {}
+ ~ClearStackOnExit() { d_.ClearStack(); }
+ private:
+ ClearStackOnExit(const ClearStackOnExit&);
+ ClearStackOnExit& operator=(const ClearStackOnExit&);
+ GenericDocument& d_;
+ };
+
+ // callers of the following private Handler functions
+ // template <typename,typename,typename> friend class GenericReader; // for parsing
+ template <typename, typename> friend class GenericValue; // for deep copying
+
+public:
+ // Implementation of Handler
+ bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
+ bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
+ bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
+ bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
+
+ bool RawNumber(const Ch* str, SizeType length, bool copy) {
+ if (copy)
+ new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+ else
+ new (stack_.template Push<ValueType>()) ValueType(str, length);
+ return true;
+ }
+
+ bool String(const Ch* str, SizeType length, bool copy) {
+ if (copy)
+ new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
+ else
+ new (stack_.template Push<ValueType>()) ValueType(str, length);
+ return true;
+ }
+
+ bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; }
+
+ bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); }
+
+ bool EndObject(SizeType memberCount) {
+ typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount);
+ stack_.template Top<ValueType>()->SetObjectRaw(members, memberCount, GetAllocator());
+ return true;
+ }
+
+ bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; }
+
+ bool EndArray(SizeType elementCount) {
+ ValueType* elements = stack_.template Pop<ValueType>(elementCount);
+ stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator());
+ return true;
+ }
+
+private:
+ //! Prohibit copying
+ GenericDocument(const GenericDocument&);
+ //! Prohibit assignment
+ GenericDocument& operator=(const GenericDocument&);
+
+ void ClearStack() {
+ if (Allocator::kNeedFree)
+ while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects)
+ (stack_.template Pop<ValueType>(1))->~ValueType();
+ else
+ stack_.Clear();
+ stack_.ShrinkToFit();
+ }
+
+ void Destroy() {
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ static const size_t kDefaultStackCapacity = 1024;
+ Allocator* allocator_;
+ Allocator* ownAllocator_;
+ internal::Stack<StackAllocator> stack_;
+ ParseResult parseResult_;
+};
+
+//! GenericDocument with UTF8 encoding
+typedef GenericDocument<UTF8<> > Document;
+
+// defined here due to the dependency on GenericDocument
+template <typename Encoding, typename Allocator>
+template <typename SourceAllocator>
+inline
+GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator)
+{
+ switch (rhs.GetType()) {
+ case kObjectType:
+ case kArrayType: { // perform deep copy via SAX Handler
+ GenericDocument<Encoding,Allocator> d(&allocator);
+ rhs.Accept(d);
+ RawAssign(*d.stack_.template Pop<GenericValue>(1));
+ }
+ break;
+ case kStringType:
+ if (rhs.data_.f.flags == kConstStringFlag) {
+ data_.f.flags = rhs.data_.f.flags;
+ data_ = *reinterpret_cast<const Data*>(&rhs.data_);
+ } else {
+ SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator);
+ }
+ break;
+ default:
+ data_.f.flags = rhs.data_.f.flags;
+ data_ = *reinterpret_cast<const Data*>(&rhs.data_);
+ break;
+ }
+}
+
+//! Helper class for accessing Value of array type.
+/*!
+ Instance of this helper class is obtained by \c GenericValue::GetArray().
+ In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericArray {
+public:
+ typedef GenericArray<true, ValueT> ConstArray;
+ typedef GenericArray<false, ValueT> Array;
+ typedef ValueT PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef ValueType* ValueIterator; // This may be const or non-const iterator
+ typedef const ValueT* ConstValueIterator;
+ typedef typename ValueType::AllocatorType AllocatorType;
+ typedef typename ValueType::StringRefType StringRefType;
+
+ template <typename, typename>
+ friend class GenericValue;
+
+ GenericArray(const GenericArray& rhs) : value_(rhs.value_) {}
+ GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; }
+ ~GenericArray() {}
+
+ SizeType Size() const { return value_.Size(); }
+ SizeType Capacity() const { return value_.Capacity(); }
+ bool Empty() const { return value_.Empty(); }
+ void Clear() const { value_.Clear(); }
+ ValueType& operator[](SizeType index) const { return value_[index]; }
+ ValueIterator Begin() const { return value_.Begin(); }
+ ValueIterator End() const { return value_.End(); }
+ GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; }
+ GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; }
+ GenericArray PopBack() const { value_.PopBack(); return *this; }
+ ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); }
+ ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+ ValueIterator begin() const { return value_.Begin(); }
+ ValueIterator end() const { return value_.End(); }
+#endif
+
+private:
+ GenericArray();
+ GenericArray(ValueType& value) : value_(value) {}
+ ValueType& value_;
+};
+
+//! Helper class for accessing Value of object type.
+/*!
+ Instance of this helper class is obtained by \c GenericValue::GetObject().
+ In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1.
+*/
+template <bool Const, typename ValueT>
+class GenericObject {
+public:
+ typedef GenericObject<true, ValueT> ConstObject;
+ typedef GenericObject<false, ValueT> Object;
+ typedef ValueT PlainType;
+ typedef typename internal::MaybeAddConst<Const,PlainType>::Type ValueType;
+ typedef GenericMemberIterator<Const, typename ValueT::EncodingType, typename ValueT::AllocatorType> MemberIterator; // This may be const or non-const iterator
+ typedef GenericMemberIterator<true, typename ValueT::EncodingType, typename ValueT::AllocatorType> ConstMemberIterator;
+ typedef typename ValueType::AllocatorType AllocatorType;
+ typedef typename ValueType::StringRefType StringRefType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename ValueType::Ch Ch;
+
+ template <typename, typename>
+ friend class GenericValue;
+
+ GenericObject(const GenericObject& rhs) : value_(rhs.value_) {}
+ GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; }
+ ~GenericObject() {}
+
+ SizeType MemberCount() const { return value_.MemberCount(); }
+ bool ObjectEmpty() const { return value_.ObjectEmpty(); }
+ template <typename T> ValueType& operator[](T* name) const { return value_[name]; }
+ template <typename SourceAllocator> ValueType& operator[](const GenericValue<EncodingType, SourceAllocator>& name) const { return value_[name]; }
+#if RAPIDJSON_HAS_STDSTRING
+ ValueType& operator[](const std::basic_string<Ch>& name) const { return value_[name]; }
+#endif
+ MemberIterator MemberBegin() const { return value_.MemberBegin(); }
+ MemberIterator MemberEnd() const { return value_.MemberEnd(); }
+ bool HasMember(const Ch* name) const { return value_.HasMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool HasMember(const std::basic_string<Ch>& name) const { return value_.HasMember(name); }
+#endif
+ template <typename SourceAllocator> bool HasMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.HasMember(name); }
+ MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); }
+ template <typename SourceAllocator> MemberIterator FindMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.FindMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ MemberIterator FindMember(const std::basic_string<Ch>& name) const { return value_.FindMember(name); }
+#endif
+ GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_STDSTRING
+ GenericObject AddMember(ValueType& name, std::basic_string<Ch>& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ template <typename T> RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; }
+ void RemoveAllMembers() { return value_.RemoveAllMembers(); }
+ bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool RemoveMember(const std::basic_string<Ch>& name) const { return value_.RemoveMember(name); }
+#endif
+ template <typename SourceAllocator> bool RemoveMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.RemoveMember(name); }
+ MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); }
+ MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); }
+ MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); }
+ bool EraseMember(const Ch* name) const { return value_.EraseMember(name); }
+#if RAPIDJSON_HAS_STDSTRING
+ bool EraseMember(const std::basic_string<Ch>& name) const { return EraseMember(ValueType(StringRef(name))); }
+#endif
+ template <typename SourceAllocator> bool EraseMember(const GenericValue<EncodingType, SourceAllocator>& name) const { return value_.EraseMember(name); }
+
+#if RAPIDJSON_HAS_CXX11_RANGE_FOR
+ MemberIterator begin() const { return value_.MemberBegin(); }
+ MemberIterator end() const { return value_.MemberEnd(); }
+#endif
+
+private:
+ GenericObject();
+ GenericObject(ValueType& value) : value_(value) {}
+ ValueType& value_;
+};
+
+RAPIDJSON_NAMESPACE_END
+RAPIDJSON_DIAG_POP
+
+#endif // RAPIDJSON_DOCUMENT_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/encodedstream.h b/ext/librethinkdbxx/src/rapidjson/encodedstream.h
new file mode 100644
index 00000000..14506838
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/encodedstream.h
@@ -0,0 +1,299 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ENCODEDSTREAM_H_
+#define RAPIDJSON_ENCODEDSTREAM_H_
+
+#include "stream.h"
+#include "memorystream.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Input byte stream wrapper with a statically bound encoding.
+/*!
+ \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
+ \tparam InputByteStream Type of input byte stream. For example, FileReadStream.
+*/
+template <typename Encoding, typename InputByteStream>
+class EncodedInputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+public:
+ typedef typename Encoding::Ch Ch;
+
+ EncodedInputStream(InputByteStream& is) : is_(is) {
+ current_ = Encoding::TakeBOM(is_);
+ }
+
+ Ch Peek() const { return current_; }
+ Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
+ size_t Tell() const { return is_.Tell(); }
+
+ // Not implemented
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ EncodedInputStream(const EncodedInputStream&);
+ EncodedInputStream& operator=(const EncodedInputStream&);
+
+ InputByteStream& is_;
+ Ch current_;
+};
+
+//! Specialized for UTF8 MemoryStream.
+template <>
+class EncodedInputStream<UTF8<>, MemoryStream> {
+public:
+ typedef UTF8<>::Ch Ch;
+
+ EncodedInputStream(MemoryStream& is) : is_(is) {
+ if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
+ if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
+ if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
+ }
+ Ch Peek() const { return is_.Peek(); }
+ Ch Take() { return is_.Take(); }
+ size_t Tell() const { return is_.Tell(); }
+
+ // Not implemented
+ void Put(Ch) {}
+ void Flush() {}
+ Ch* PutBegin() { return 0; }
+ size_t PutEnd(Ch*) { return 0; }
+
+ MemoryStream& is_;
+
+private:
+ EncodedInputStream(const EncodedInputStream&);
+ EncodedInputStream& operator=(const EncodedInputStream&);
+};
+
+//! Output byte stream wrapper with statically bound encoding.
+/*!
+ \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
+ \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
+*/
+template <typename Encoding, typename OutputByteStream>
+class EncodedOutputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+public:
+ typedef typename Encoding::Ch Ch;
+
+ EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
+ if (putBOM)
+ Encoding::PutBOM(os_);
+ }
+
+ void Put(Ch c) { Encoding::Put(os_, c); }
+ void Flush() { os_.Flush(); }
+
+ // Not implemented
+ Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
+ Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ EncodedOutputStream(const EncodedOutputStream&);
+ EncodedOutputStream& operator=(const EncodedOutputStream&);
+
+ OutputByteStream& os_;
+};
+
+#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
+
+//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
+/*!
+ \tparam CharType Type of character for reading.
+ \tparam InputByteStream type of input byte stream to be wrapped.
+*/
+template <typename CharType, typename InputByteStream>
+class AutoUTFInputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+public:
+ typedef CharType Ch;
+
+ //! Constructor.
+ /*!
+ \param is input stream to be wrapped.
+ \param type UTF encoding type if it is not detected from the stream.
+ */
+ AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
+ RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
+ DetectType();
+ static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
+ takeFunc_ = f[type_];
+ current_ = takeFunc_(*is_);
+ }
+
+ UTFType GetType() const { return type_; }
+ bool HasBOM() const { return hasBOM_; }
+
+ Ch Peek() const { return current_; }
+ Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
+ size_t Tell() const { return is_->Tell(); }
+
+ // Not implemented
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ AutoUTFInputStream(const AutoUTFInputStream&);
+ AutoUTFInputStream& operator=(const AutoUTFInputStream&);
+
+ // Detect encoding type with BOM or RFC 4627
+ void DetectType() {
+ // BOM (Byte Order Mark):
+ // 00 00 FE FF UTF-32BE
+ // FF FE 00 00 UTF-32LE
+ // FE FF UTF-16BE
+ // FF FE UTF-16LE
+ // EF BB BF UTF-8
+
+ const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
+ if (!c)
+ return;
+
+ unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
+ hasBOM_ = false;
+ if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
+ else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
+ else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
+ else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
+ else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
+
+ // RFC 4627: Section 3
+ // "Since the first two characters of a JSON text will always be ASCII
+ // characters [RFC0020], it is possible to determine whether an octet
+ // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
+ // at the pattern of nulls in the first four octets."
+ // 00 00 00 xx UTF-32BE
+ // 00 xx 00 xx UTF-16BE
+ // xx 00 00 00 UTF-32LE
+ // xx 00 xx 00 UTF-16LE
+ // xx xx xx xx UTF-8
+
+ if (!hasBOM_) {
+ unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
+ switch (pattern) {
+ case 0x08: type_ = kUTF32BE; break;
+ case 0x0A: type_ = kUTF16BE; break;
+ case 0x01: type_ = kUTF32LE; break;
+ case 0x05: type_ = kUTF16LE; break;
+ case 0x0F: type_ = kUTF8; break;
+ default: break; // Use type defined by user.
+ }
+ }
+
+ // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
+ if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
+ if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
+ }
+
+ typedef Ch (*TakeFunc)(InputByteStream& is);
+ InputByteStream* is_;
+ UTFType type_;
+ Ch current_;
+ TakeFunc takeFunc_;
+ bool hasBOM_;
+};
+
+//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
+/*!
+ \tparam CharType Type of character for writing.
+ \tparam OutputByteStream type of output byte stream to be wrapped.
+*/
+template <typename CharType, typename OutputByteStream>
+class AutoUTFOutputStream {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+public:
+ typedef CharType Ch;
+
+ //! Constructor.
+ /*!
+ \param os output stream to be wrapped.
+ \param type UTF encoding type.
+ \param putBOM Whether to write BOM at the beginning of the stream.
+ */
+ AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
+ RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
+
+ // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
+ if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
+ if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
+
+ static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
+ putFunc_ = f[type_];
+
+ if (putBOM)
+ PutBOM();
+ }
+
+ UTFType GetType() const { return type_; }
+
+ void Put(Ch c) { putFunc_(*os_, c); }
+ void Flush() { os_->Flush(); }
+
+ // Not implemented
+ Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
+ Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ AutoUTFOutputStream(const AutoUTFOutputStream&);
+ AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
+
+ void PutBOM() {
+ typedef void (*PutBOMFunc)(OutputByteStream&);
+ static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
+ f[type_](*os_);
+ }
+
+ typedef void (*PutFunc)(OutputByteStream&, Ch);
+
+ OutputByteStream* os_;
+ UTFType type_;
+ PutFunc putFunc_;
+};
+
+#undef RAPIDJSON_ENCODINGS_FUNC
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/encodings.h b/ext/librethinkdbxx/src/rapidjson/encodings.h
new file mode 100644
index 00000000..baa7c2b1
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/encodings.h
@@ -0,0 +1,716 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ENCODINGS_H_
+#define RAPIDJSON_ENCODINGS_H_
+
+#include "rapidjson.h"
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+#elif defined(__GNUC__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(overflow)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Encoding
+
+/*! \class rapidjson::Encoding
+ \brief Concept for encoding of Unicode characters.
+
+\code
+concept Encoding {
+ typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
+
+ enum { supportUnicode = 1 }; // or 0 if not supporting unicode
+
+ //! \brief Encode a Unicode codepoint to an output stream.
+ //! \param os Output stream.
+ //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint);
+
+ //! \brief Decode a Unicode codepoint from an input stream.
+ //! \param is Input stream.
+ //! \param codepoint Output of the unicode codepoint.
+ //! \return true if a valid codepoint can be decoded from the stream.
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint);
+
+ //! \brief Validate one Unicode codepoint from an encoded stream.
+ //! \param is Input stream to obtain codepoint.
+ //! \param os Output for copying one codepoint.
+ //! \return true if it is valid.
+ //! \note This function just validating and copying the codepoint without actually decode it.
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os);
+
+ // The following functions are deal with byte streams.
+
+ //! Take a character from input byte stream, skip BOM if exist.
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is);
+
+ //! Take a character from input byte stream.
+ template <typename InputByteStream>
+ static Ch Take(InputByteStream& is);
+
+ //! Put BOM to output byte stream.
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os);
+
+ //! Put a character to output byte stream.
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, Ch c);
+};
+\endcode
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF8
+
+//! UTF-8 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-8
+ http://tools.ietf.org/html/rfc3629
+ \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
+ \note implements Encoding concept
+*/
+template<typename CharType = char>
+struct UTF8 {
+ typedef CharType Ch;
+
+ enum { supportUnicode = 1 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ if (codepoint <= 0x7F)
+ os.Put(static_cast<Ch>(codepoint & 0xFF));
+ else if (codepoint <= 0x7FF) {
+ os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+ }
+ else if (codepoint <= 0xFFFF) {
+ os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+ os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ }
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ if (codepoint <= 0x7F)
+ PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+ else if (codepoint <= 0x7FF) {
+ PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
+ }
+ else if (codepoint <= 0xFFFF) {
+ PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
+ PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
+ }
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
+#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
+#define TAIL() COPY(); TRANS(0x70)
+ typename InputStream::Ch c = is.Take();
+ if (!(c & 0x80)) {
+ *codepoint = static_cast<unsigned char>(c);
+ return true;
+ }
+
+ unsigned char type = GetRange(static_cast<unsigned char>(c));
+ if (type >= 32) {
+ *codepoint = 0;
+ } else {
+ *codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
+ }
+ bool result = true;
+ switch (type) {
+ case 2: TAIL(); return result;
+ case 3: TAIL(); TAIL(); return result;
+ case 4: COPY(); TRANS(0x50); TAIL(); return result;
+ case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
+ case 6: TAIL(); TAIL(); TAIL(); return result;
+ case 10: COPY(); TRANS(0x20); TAIL(); return result;
+ case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
+ default: return false;
+ }
+#undef COPY
+#undef TRANS
+#undef TAIL
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+#define COPY() os.Put(c = is.Take())
+#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
+#define TAIL() COPY(); TRANS(0x70)
+ Ch c;
+ COPY();
+ if (!(c & 0x80))
+ return true;
+
+ bool result = true;
+ switch (GetRange(static_cast<unsigned char>(c))) {
+ case 2: TAIL(); return result;
+ case 3: TAIL(); TAIL(); return result;
+ case 4: COPY(); TRANS(0x50); TAIL(); return result;
+ case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
+ case 6: TAIL(); TAIL(); TAIL(); return result;
+ case 10: COPY(); TRANS(0x20); TAIL(); return result;
+ case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
+ default: return false;
+ }
+#undef COPY
+#undef TRANS
+#undef TAIL
+ }
+
+ static unsigned char GetRange(unsigned char c) {
+ // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
+ static const unsigned char type[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
+ 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
+ 8,8,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,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+ };
+ return type[c];
+ }
+
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ typename InputByteStream::Ch c = Take(is);
+ if (static_cast<unsigned char>(c) != 0xEFu) return c;
+ c = is.Take();
+ if (static_cast<unsigned char>(c) != 0xBBu) return c;
+ c = is.Take();
+ if (static_cast<unsigned char>(c) != 0xBFu) return c;
+ c = is.Take();
+ return c;
+ }
+
+ template <typename InputByteStream>
+ static Ch Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ return static_cast<Ch>(is.Take());
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, Ch c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(c));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF16
+
+//! UTF-16 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-16
+ http://tools.ietf.org/html/rfc2781
+ \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
+ \note implements Encoding concept
+
+ \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
+ For streaming, use UTF16LE and UTF16BE, which handle endianness.
+*/
+template<typename CharType = wchar_t>
+struct UTF16 {
+ typedef CharType Ch;
+ RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
+
+ enum { supportUnicode = 1 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ if (codepoint <= 0xFFFF) {
+ RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
+ os.Put(static_cast<typename OutputStream::Ch>(codepoint));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ unsigned v = codepoint - 0x10000;
+ os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+ os.Put((v & 0x3FF) | 0xDC00);
+ }
+ }
+
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ if (codepoint <= 0xFFFF) {
+ RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ unsigned v = codepoint - 0x10000;
+ PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
+ PutUnsafe(os, (v & 0x3FF) | 0xDC00);
+ }
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
+ typename InputStream::Ch c = is.Take();
+ if (c < 0xD800 || c > 0xDFFF) {
+ *codepoint = static_cast<unsigned>(c);
+ return true;
+ }
+ else if (c <= 0xDBFF) {
+ *codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
+ c = is.Take();
+ *codepoint |= (static_cast<unsigned>(c) & 0x3FF);
+ *codepoint += 0x10000;
+ return c >= 0xDC00 && c <= 0xDFFF;
+ }
+ return false;
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
+ typename InputStream::Ch c;
+ os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
+ if (c < 0xD800 || c > 0xDFFF)
+ return true;
+ else if (c <= 0xDBFF) {
+ os.Put(c = is.Take());
+ return c >= 0xDC00 && c <= 0xDFFF;
+ }
+ return false;
+ }
+};
+
+//! UTF-16 little endian encoding.
+template<typename CharType = wchar_t>
+struct UTF16LE : UTF16<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<uint8_t>(is.Take());
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
+ }
+};
+
+//! UTF-16 big endian encoding.
+template<typename CharType = wchar_t>
+struct UTF16BE : UTF16<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ c |= static_cast<uint8_t>(is.Take());
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// UTF32
+
+//! UTF-32 encoding.
+/*! http://en.wikipedia.org/wiki/UTF-32
+ \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
+ \note implements Encoding concept
+
+ \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
+ For streaming, use UTF32LE and UTF32BE, which handle endianness.
+*/
+template<typename CharType = unsigned>
+struct UTF32 {
+ typedef CharType Ch;
+ RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
+
+ enum { supportUnicode = 1 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ os.Put(codepoint);
+ }
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
+ RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
+ PutUnsafe(os, codepoint);
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
+ Ch c = is.Take();
+ *codepoint = c;
+ return c <= 0x10FFFF;
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
+ Ch c;
+ os.Put(c = is.Take());
+ return c <= 0x10FFFF;
+ }
+};
+
+//! UTF-32 little endian enocoding.
+template<typename CharType = unsigned>
+struct UTF32LE : UTF32<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<uint8_t>(is.Take());
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
+ }
+};
+
+//! UTF-32 big endian encoding.
+template<typename CharType = unsigned>
+struct UTF32BE : UTF32<CharType> {
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ CharType c = Take(is);
+ return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
+ }
+
+ template <typename InputByteStream>
+ static CharType Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
+ c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
+ return static_cast<CharType>(c);
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, CharType c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
+ os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ASCII
+
+//! ASCII encoding.
+/*! http://en.wikipedia.org/wiki/ASCII
+ \tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
+ \note implements Encoding concept
+*/
+template<typename CharType = char>
+struct ASCII {
+ typedef CharType Ch;
+
+ enum { supportUnicode = 0 };
+
+ template<typename OutputStream>
+ static void Encode(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_ASSERT(codepoint <= 0x7F);
+ os.Put(static_cast<Ch>(codepoint & 0xFF));
+ }
+
+ template<typename OutputStream>
+ static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ RAPIDJSON_ASSERT(codepoint <= 0x7F);
+ PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
+ }
+
+ template <typename InputStream>
+ static bool Decode(InputStream& is, unsigned* codepoint) {
+ uint8_t c = static_cast<uint8_t>(is.Take());
+ *codepoint = c;
+ return c <= 0X7F;
+ }
+
+ template <typename InputStream, typename OutputStream>
+ static bool Validate(InputStream& is, OutputStream& os) {
+ uint8_t c = static_cast<uint8_t>(is.Take());
+ os.Put(static_cast<typename OutputStream::Ch>(c));
+ return c <= 0x7F;
+ }
+
+ template <typename InputByteStream>
+ static CharType TakeBOM(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ uint8_t c = static_cast<uint8_t>(Take(is));
+ return static_cast<Ch>(c);
+ }
+
+ template <typename InputByteStream>
+ static Ch Take(InputByteStream& is) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
+ return static_cast<Ch>(is.Take());
+ }
+
+ template <typename OutputByteStream>
+ static void PutBOM(OutputByteStream& os) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ (void)os;
+ }
+
+ template <typename OutputByteStream>
+ static void Put(OutputByteStream& os, Ch c) {
+ RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
+ os.Put(static_cast<typename OutputByteStream::Ch>(c));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// AutoUTF
+
+//! Runtime-specified UTF encoding type of a stream.
+enum UTFType {
+ kUTF8 = 0, //!< UTF-8.
+ kUTF16LE = 1, //!< UTF-16 little endian.
+ kUTF16BE = 2, //!< UTF-16 big endian.
+ kUTF32LE = 3, //!< UTF-32 little endian.
+ kUTF32BE = 4 //!< UTF-32 big endian.
+};
+
+//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
+/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
+*/
+template<typename CharType>
+struct AutoUTF {
+ typedef CharType Ch;
+
+ enum { supportUnicode = 1 };
+
+#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
+
+ template<typename OutputStream>
+ RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
+ typedef void (*EncodeFunc)(OutputStream&, unsigned);
+ static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
+ (*f[os.GetType()])(os, codepoint);
+ }
+
+ template<typename OutputStream>
+ RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
+ typedef void (*EncodeFunc)(OutputStream&, unsigned);
+ static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
+ (*f[os.GetType()])(os, codepoint);
+ }
+
+ template <typename InputStream>
+ RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
+ typedef bool (*DecodeFunc)(InputStream&, unsigned*);
+ static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
+ return (*f[is.GetType()])(is, codepoint);
+ }
+
+ template <typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
+ typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
+ static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
+ return (*f[is.GetType()])(is, os);
+ }
+
+#undef RAPIDJSON_ENCODINGS_FUNC
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Transcoder
+
+//! Encoding conversion.
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder {
+ //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
+ unsigned codepoint;
+ if (!SourceEncoding::Decode(is, &codepoint))
+ return false;
+ TargetEncoding::Encode(os, codepoint);
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+ unsigned codepoint;
+ if (!SourceEncoding::Decode(is, &codepoint))
+ return false;
+ TargetEncoding::EncodeUnsafe(os, codepoint);
+ return true;
+ }
+
+ //! Validate one Unicode codepoint from an encoded stream.
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
+ return Transcode(is, os); // Since source/target encoding is different, must transcode.
+ }
+};
+
+// Forward declaration.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
+
+//! Specialization of Transcoder with same source and target encoding.
+template<typename Encoding>
+struct Transcoder<Encoding, Encoding> {
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
+ os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
+ PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
+ return true;
+ }
+
+ template<typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
+ return Encoding::Validate(is, os); // source/target encoding are the same
+ }
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/error/en.h b/ext/librethinkdbxx/src/rapidjson/error/en.h
new file mode 100644
index 00000000..2db838bf
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/error/en.h
@@ -0,0 +1,74 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ERROR_EN_H_
+#define RAPIDJSON_ERROR_EN_H_
+
+#include "error.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(covered-switch-default)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Maps error code of parsing into error message.
+/*!
+ \ingroup RAPIDJSON_ERRORS
+ \param parseErrorCode Error code obtained in parsing.
+ \return the error message.
+ \note User can make a copy of this function for localization.
+ Using switch-case is safer for future modification of error codes.
+*/
+inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
+ switch (parseErrorCode) {
+ case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
+
+ case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
+ case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
+
+ case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
+
+ case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
+ case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
+ case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
+
+ case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
+
+ case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
+ case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
+ case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
+ case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
+ case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
+
+ case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
+ case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
+ case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
+
+ case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
+ case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
+
+ default: return RAPIDJSON_ERROR_STRING("Unknown error.");
+ }
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ERROR_EN_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/error/error.h b/ext/librethinkdbxx/src/rapidjson/error/error.h
new file mode 100644
index 00000000..95cb31a7
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/error/error.h
@@ -0,0 +1,155 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ERROR_ERROR_H_
+#define RAPIDJSON_ERROR_ERROR_H_
+
+#include "../rapidjson.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+/*! \file error.h */
+
+/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ERROR_CHARTYPE
+
+//! Character type of error messages.
+/*! \ingroup RAPIDJSON_ERRORS
+ The default character type is \c char.
+ On Windows, user can define this macro as \c TCHAR for supporting both
+ unicode/non-unicode settings.
+*/
+#ifndef RAPIDJSON_ERROR_CHARTYPE
+#define RAPIDJSON_ERROR_CHARTYPE char
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ERROR_STRING
+
+//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
+/*! \ingroup RAPIDJSON_ERRORS
+ By default this conversion macro does nothing.
+ On Windows, user can define this macro as \c _T(x) for supporting both
+ unicode/non-unicode settings.
+*/
+#ifndef RAPIDJSON_ERROR_STRING
+#define RAPIDJSON_ERROR_STRING(x) x
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// ParseErrorCode
+
+//! Error code of parsing.
+/*! \ingroup RAPIDJSON_ERRORS
+ \see GenericReader::Parse, GenericReader::GetParseErrorCode
+*/
+enum ParseErrorCode {
+ kParseErrorNone = 0, //!< No error.
+
+ kParseErrorDocumentEmpty, //!< The document is empty.
+ kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
+
+ kParseErrorValueInvalid, //!< Invalid value.
+
+ kParseErrorObjectMissName, //!< Missing a name for object member.
+ kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
+ kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
+
+ kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
+
+ kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
+ kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
+ kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
+ kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
+ kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
+
+ kParseErrorNumberTooBig, //!< Number too big to be stored in double.
+ kParseErrorNumberMissFraction, //!< Miss fraction part in number.
+ kParseErrorNumberMissExponent, //!< Miss exponent in number.
+
+ kParseErrorTermination, //!< Parsing was terminated.
+ kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
+};
+
+//! Result of parsing (wraps ParseErrorCode)
+/*!
+ \ingroup RAPIDJSON_ERRORS
+ \code
+ Document doc;
+ ParseResult ok = doc.Parse("[42]");
+ if (!ok) {
+ fprintf(stderr, "JSON parse error: %s (%u)",
+ GetParseError_En(ok.Code()), ok.Offset());
+ exit(EXIT_FAILURE);
+ }
+ \endcode
+ \see GenericReader::Parse, GenericDocument::Parse
+*/
+struct ParseResult {
+public:
+ //! Default constructor, no error.
+ ParseResult() : code_(kParseErrorNone), offset_(0) {}
+ //! Constructor to set an error.
+ ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
+
+ //! Get the error code.
+ ParseErrorCode Code() const { return code_; }
+ //! Get the error offset, if \ref IsError(), 0 otherwise.
+ size_t Offset() const { return offset_; }
+
+ //! Conversion to \c bool, returns \c true, iff !\ref IsError().
+ operator bool() const { return !IsError(); }
+ //! Whether the result is an error.
+ bool IsError() const { return code_ != kParseErrorNone; }
+
+ bool operator==(const ParseResult& that) const { return code_ == that.code_; }
+ bool operator==(ParseErrorCode code) const { return code_ == code; }
+ friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
+
+ //! Reset error code.
+ void Clear() { Set(kParseErrorNone); }
+ //! Update error code and offset.
+ void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
+
+private:
+ ParseErrorCode code_;
+ size_t offset_;
+};
+
+//! Function pointer type of GetParseError().
+/*! \ingroup RAPIDJSON_ERRORS
+
+ This is the prototype for \c GetParseError_X(), where \c X is a locale.
+ User can dynamically change locale in runtime, e.g.:
+\code
+ GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
+ const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
+\endcode
+*/
+typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_ERROR_ERROR_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/filereadstream.h b/ext/librethinkdbxx/src/rapidjson/filereadstream.h
new file mode 100644
index 00000000..b56ea13b
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/filereadstream.h
@@ -0,0 +1,99 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_FILEREADSTREAM_H_
+#define RAPIDJSON_FILEREADSTREAM_H_
+
+#include "stream.h"
+#include <cstdio>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! File byte stream for input using fread().
+/*!
+ \note implements Stream concept
+*/
+class FileReadStream {
+public:
+ typedef char Ch; //!< Character type (byte).
+
+ //! Constructor.
+ /*!
+ \param fp File pointer opened for read.
+ \param buffer user-supplied buffer.
+ \param bufferSize size of buffer in bytes. Must >=4 bytes.
+ */
+ FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
+ RAPIDJSON_ASSERT(fp_ != 0);
+ RAPIDJSON_ASSERT(bufferSize >= 4);
+ Read();
+ }
+
+ Ch Peek() const { return *current_; }
+ Ch Take() { Ch c = *current_; Read(); return c; }
+ size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
+
+ // Not implemented
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ return (current_ + 4 <= bufferLast_) ? current_ : 0;
+ }
+
+private:
+ void Read() {
+ if (current_ < bufferLast_)
+ ++current_;
+ else if (!eof_) {
+ count_ += readCount_;
+ readCount_ = fread(buffer_, 1, bufferSize_, fp_);
+ bufferLast_ = buffer_ + readCount_ - 1;
+ current_ = buffer_;
+
+ if (readCount_ < bufferSize_) {
+ buffer_[readCount_] = '\0';
+ ++bufferLast_;
+ eof_ = true;
+ }
+ }
+ }
+
+ std::FILE* fp_;
+ Ch *buffer_;
+ size_t bufferSize_;
+ Ch *bufferLast_;
+ Ch *current_;
+ size_t readCount_;
+ size_t count_; //!< Number of characters read
+ bool eof_;
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/filewritestream.h b/ext/librethinkdbxx/src/rapidjson/filewritestream.h
new file mode 100644
index 00000000..6378dd60
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/filewritestream.h
@@ -0,0 +1,104 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_FILEWRITESTREAM_H_
+#define RAPIDJSON_FILEWRITESTREAM_H_
+
+#include "stream.h"
+#include <cstdio>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of C file stream for input using fread().
+/*!
+ \note implements Stream concept
+*/
+class FileWriteStream {
+public:
+ typedef char Ch; //!< Character type. Only support char.
+
+ FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
+ RAPIDJSON_ASSERT(fp_ != 0);
+ }
+
+ void Put(char c) {
+ if (current_ >= bufferEnd_)
+ Flush();
+
+ *current_++ = c;
+ }
+
+ void PutN(char c, size_t n) {
+ size_t avail = static_cast<size_t>(bufferEnd_ - current_);
+ while (n > avail) {
+ std::memset(current_, c, avail);
+ current_ += avail;
+ Flush();
+ n -= avail;
+ avail = static_cast<size_t>(bufferEnd_ - current_);
+ }
+
+ if (n > 0) {
+ std::memset(current_, c, n);
+ current_ += n;
+ }
+ }
+
+ void Flush() {
+ if (current_ != buffer_) {
+ size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
+ if (result < static_cast<size_t>(current_ - buffer_)) {
+ // failure deliberately ignored at this time
+ // added to avoid warn_unused_result build errors
+ }
+ current_ = buffer_;
+ }
+ }
+
+ // Not implemented
+ char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+ char Take() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ FileWriteStream(const FileWriteStream&);
+ FileWriteStream& operator=(const FileWriteStream&);
+
+ std::FILE* fp_;
+ char *buffer_;
+ char *bufferEnd_;
+ char *current_;
+};
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(FileWriteStream& stream, char c, size_t n) {
+ stream.PutN(c, n);
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_FILESTREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/fwd.h b/ext/librethinkdbxx/src/rapidjson/fwd.h
new file mode 100644
index 00000000..e8104e84
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/fwd.h
@@ -0,0 +1,151 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_FWD_H_
+#define RAPIDJSON_FWD_H_
+
+#include "rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+// encodings.h
+
+template<typename CharType> struct UTF8;
+template<typename CharType> struct UTF16;
+template<typename CharType> struct UTF16BE;
+template<typename CharType> struct UTF16LE;
+template<typename CharType> struct UTF32;
+template<typename CharType> struct UTF32BE;
+template<typename CharType> struct UTF32LE;
+template<typename CharType> struct ASCII;
+template<typename CharType> struct AutoUTF;
+
+template<typename SourceEncoding, typename TargetEncoding>
+struct Transcoder;
+
+// allocators.h
+
+class CrtAllocator;
+
+template <typename BaseAllocator>
+class MemoryPoolAllocator;
+
+// stream.h
+
+template <typename Encoding>
+struct GenericStringStream;
+
+typedef GenericStringStream<UTF8<char> > StringStream;
+
+template <typename Encoding>
+struct GenericInsituStringStream;
+
+typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
+
+// stringbuffer.h
+
+template <typename Encoding, typename Allocator>
+class GenericStringBuffer;
+
+typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
+
+// filereadstream.h
+
+class FileReadStream;
+
+// filewritestream.h
+
+class FileWriteStream;
+
+// memorybuffer.h
+
+template <typename Allocator>
+struct GenericMemoryBuffer;
+
+typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
+
+// memorystream.h
+
+struct MemoryStream;
+
+// reader.h
+
+template<typename Encoding, typename Derived>
+struct BaseReaderHandler;
+
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
+class GenericReader;
+
+typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
+
+// writer.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class Writer;
+
+// prettywriter.h
+
+template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
+class PrettyWriter;
+
+// document.h
+
+template <typename Encoding, typename Allocator>
+struct GenericMember;
+
+template <bool Const, typename Encoding, typename Allocator>
+class GenericMemberIterator;
+
+template<typename CharType>
+struct GenericStringRef;
+
+template <typename Encoding, typename Allocator>
+class GenericValue;
+
+typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+
+template <typename Encoding, typename Allocator, typename StackAllocator>
+class GenericDocument;
+
+typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
+
+// pointer.h
+
+template <typename ValueType, typename Allocator>
+class GenericPointer;
+
+typedef GenericPointer<Value, CrtAllocator> Pointer;
+
+// schema.h
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider;
+
+template <typename ValueT, typename Allocator>
+class GenericSchemaDocument;
+
+typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+template <
+ typename SchemaDocumentType,
+ typename OutputHandler,
+ typename StateAllocator>
+class GenericSchemaValidator;
+
+typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSONFWD_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/biginteger.h b/ext/librethinkdbxx/src/rapidjson/internal/biginteger.h
new file mode 100644
index 00000000..9d3e88c9
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/biginteger.h
@@ -0,0 +1,290 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_BIGINTEGER_H_
+#define RAPIDJSON_BIGINTEGER_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && defined(_M_AMD64)
+#include <intrin.h> // for _umul128
+#pragma intrinsic(_umul128)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+class BigInteger {
+public:
+ typedef uint64_t Type;
+
+ BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
+ std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
+ }
+
+ explicit BigInteger(uint64_t u) : count_(1) {
+ digits_[0] = u;
+ }
+
+ BigInteger(const char* decimals, size_t length) : count_(1) {
+ RAPIDJSON_ASSERT(length > 0);
+ digits_[0] = 0;
+ size_t i = 0;
+ const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
+ while (length >= kMaxDigitPerIteration) {
+ AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
+ length -= kMaxDigitPerIteration;
+ i += kMaxDigitPerIteration;
+ }
+
+ if (length > 0)
+ AppendDecimal64(decimals + i, decimals + i + length);
+ }
+
+ BigInteger& operator=(const BigInteger &rhs)
+ {
+ if (this != &rhs) {
+ count_ = rhs.count_;
+ std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
+ }
+ return *this;
+ }
+
+ BigInteger& operator=(uint64_t u) {
+ digits_[0] = u;
+ count_ = 1;
+ return *this;
+ }
+
+ BigInteger& operator+=(uint64_t u) {
+ Type backup = digits_[0];
+ digits_[0] += u;
+ for (size_t i = 0; i < count_ - 1; i++) {
+ if (digits_[i] >= backup)
+ return *this; // no carry
+ backup = digits_[i + 1];
+ digits_[i + 1] += 1;
+ }
+
+ // Last carry
+ if (digits_[count_ - 1] < backup)
+ PushBack(1);
+
+ return *this;
+ }
+
+ BigInteger& operator*=(uint64_t u) {
+ if (u == 0) return *this = 0;
+ if (u == 1) return *this;
+ if (*this == 1) return *this = u;
+
+ uint64_t k = 0;
+ for (size_t i = 0; i < count_; i++) {
+ uint64_t hi;
+ digits_[i] = MulAdd64(digits_[i], u, k, &hi);
+ k = hi;
+ }
+
+ if (k > 0)
+ PushBack(k);
+
+ return *this;
+ }
+
+ BigInteger& operator*=(uint32_t u) {
+ if (u == 0) return *this = 0;
+ if (u == 1) return *this;
+ if (*this == 1) return *this = u;
+
+ uint64_t k = 0;
+ for (size_t i = 0; i < count_; i++) {
+ const uint64_t c = digits_[i] >> 32;
+ const uint64_t d = digits_[i] & 0xFFFFFFFF;
+ const uint64_t uc = u * c;
+ const uint64_t ud = u * d;
+ const uint64_t p0 = ud + k;
+ const uint64_t p1 = uc + (p0 >> 32);
+ digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
+ k = p1 >> 32;
+ }
+
+ if (k > 0)
+ PushBack(k);
+
+ return *this;
+ }
+
+ BigInteger& operator<<=(size_t shift) {
+ if (IsZero() || shift == 0) return *this;
+
+ size_t offset = shift / kTypeBit;
+ size_t interShift = shift % kTypeBit;
+ RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
+
+ if (interShift == 0) {
+ std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
+ count_ += offset;
+ }
+ else {
+ digits_[count_] = 0;
+ for (size_t i = count_; i > 0; i--)
+ digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
+ digits_[offset] = digits_[0] << interShift;
+ count_ += offset;
+ if (digits_[count_])
+ count_++;
+ }
+
+ std::memset(digits_, 0, offset * sizeof(Type));
+
+ return *this;
+ }
+
+ bool operator==(const BigInteger& rhs) const {
+ return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
+ }
+
+ bool operator==(const Type rhs) const {
+ return count_ == 1 && digits_[0] == rhs;
+ }
+
+ BigInteger& MultiplyPow5(unsigned exp) {
+ static const uint32_t kPow5[12] = {
+ 5,
+ 5 * 5,
+ 5 * 5 * 5,
+ 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
+ };
+ if (exp == 0) return *this;
+ for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
+ for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
+ if (exp > 0) *this *= kPow5[exp - 1];
+ return *this;
+ }
+
+ // Compute absolute difference of this and rhs.
+ // Assume this != rhs
+ bool Difference(const BigInteger& rhs, BigInteger* out) const {
+ int cmp = Compare(rhs);
+ RAPIDJSON_ASSERT(cmp != 0);
+ const BigInteger *a, *b; // Makes a > b
+ bool ret;
+ if (cmp < 0) { a = &rhs; b = this; ret = true; }
+ else { a = this; b = &rhs; ret = false; }
+
+ Type borrow = 0;
+ for (size_t i = 0; i < a->count_; i++) {
+ Type d = a->digits_[i] - borrow;
+ if (i < b->count_)
+ d -= b->digits_[i];
+ borrow = (d > a->digits_[i]) ? 1 : 0;
+ out->digits_[i] = d;
+ if (d != 0)
+ out->count_ = i + 1;
+ }
+
+ return ret;
+ }
+
+ int Compare(const BigInteger& rhs) const {
+ if (count_ != rhs.count_)
+ return count_ < rhs.count_ ? -1 : 1;
+
+ for (size_t i = count_; i-- > 0;)
+ if (digits_[i] != rhs.digits_[i])
+ return digits_[i] < rhs.digits_[i] ? -1 : 1;
+
+ return 0;
+ }
+
+ size_t GetCount() const { return count_; }
+ Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
+ bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
+
+private:
+ void AppendDecimal64(const char* begin, const char* end) {
+ uint64_t u = ParseUint64(begin, end);
+ if (IsZero())
+ *this = u;
+ else {
+ unsigned exp = static_cast<unsigned>(end - begin);
+ (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
+ }
+ }
+
+ void PushBack(Type digit) {
+ RAPIDJSON_ASSERT(count_ < kCapacity);
+ digits_[count_++] = digit;
+ }
+
+ static uint64_t ParseUint64(const char* begin, const char* end) {
+ uint64_t r = 0;
+ for (const char* p = begin; p != end; ++p) {
+ RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
+ r = r * 10u + static_cast<unsigned>(*p - '0');
+ }
+ return r;
+ }
+
+ // Assume a * b + k < 2^128
+ static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+ uint64_t low = _umul128(a, b, outHigh) + k;
+ if (low < k)
+ (*outHigh)++;
+ return low;
+#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
+ __extension__ typedef unsigned __int128 uint128;
+ uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
+ p += k;
+ *outHigh = static_cast<uint64_t>(p >> 64);
+ return static_cast<uint64_t>(p);
+#else
+ const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
+ uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
+ x1 += (x0 >> 32); // can't give carry
+ x1 += x2;
+ if (x1 < x2)
+ x3 += (static_cast<uint64_t>(1) << 32);
+ uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
+ uint64_t hi = x3 + (x1 >> 32);
+
+ lo += k;
+ if (lo < k)
+ hi++;
+ *outHigh = hi;
+ return lo;
+#endif
+ }
+
+ static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
+ static const size_t kCapacity = kBitCount / sizeof(Type);
+ static const size_t kTypeBit = sizeof(Type) * 8;
+
+ Type digits_[kCapacity];
+ size_t count_;
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_BIGINTEGER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/diyfp.h b/ext/librethinkdbxx/src/rapidjson/internal/diyfp.h
new file mode 100644
index 00000000..c9fefdc6
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/diyfp.h
@@ -0,0 +1,258 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
+// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
+// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+
+#ifndef RAPIDJSON_DIYFP_H_
+#define RAPIDJSON_DIYFP_H_
+
+#include "../rapidjson.h"
+
+#if defined(_MSC_VER) && defined(_M_AMD64)
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse64)
+#pragma intrinsic(_umul128)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+struct DiyFp {
+ DiyFp() : f(), e() {}
+
+ DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
+
+ explicit DiyFp(double d) {
+ union {
+ double d;
+ uint64_t u64;
+ } u = { d };
+
+ int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
+ uint64_t significand = (u.u64 & kDpSignificandMask);
+ if (biased_e != 0) {
+ f = significand + kDpHiddenBit;
+ e = biased_e - kDpExponentBias;
+ }
+ else {
+ f = significand;
+ e = kDpMinExponent + 1;
+ }
+ }
+
+ DiyFp operator-(const DiyFp& rhs) const {
+ return DiyFp(f - rhs.f, e);
+ }
+
+ DiyFp operator*(const DiyFp& rhs) const {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+ uint64_t h;
+ uint64_t l = _umul128(f, rhs.f, &h);
+ if (l & (uint64_t(1) << 63)) // rounding
+ h++;
+ return DiyFp(h, e + rhs.e + 64);
+#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
+ __extension__ typedef unsigned __int128 uint128;
+ uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
+ uint64_t h = static_cast<uint64_t>(p >> 64);
+ uint64_t l = static_cast<uint64_t>(p);
+ if (l & (uint64_t(1) << 63)) // rounding
+ h++;
+ return DiyFp(h, e + rhs.e + 64);
+#else
+ const uint64_t M32 = 0xFFFFFFFF;
+ const uint64_t a = f >> 32;
+ const uint64_t b = f & M32;
+ const uint64_t c = rhs.f >> 32;
+ const uint64_t d = rhs.f & M32;
+ const uint64_t ac = a * c;
+ const uint64_t bc = b * c;
+ const uint64_t ad = a * d;
+ const uint64_t bd = b * d;
+ uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
+ tmp += 1U << 31; /// mult_round
+ return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
+#endif
+ }
+
+ DiyFp Normalize() const {
+#if defined(_MSC_VER) && defined(_M_AMD64)
+ unsigned long index;
+ _BitScanReverse64(&index, f);
+ return DiyFp(f << (63 - index), e - (63 - index));
+#elif defined(__GNUC__) && __GNUC__ >= 4
+ int s = __builtin_clzll(f);
+ return DiyFp(f << s, e - s);
+#else
+ DiyFp res = *this;
+ while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
+ res.f <<= 1;
+ res.e--;
+ }
+ return res;
+#endif
+ }
+
+ DiyFp NormalizeBoundary() const {
+ DiyFp res = *this;
+ while (!(res.f & (kDpHiddenBit << 1))) {
+ res.f <<= 1;
+ res.e--;
+ }
+ res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
+ res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
+ return res;
+ }
+
+ void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
+ DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
+ DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
+ mi.f <<= mi.e - pl.e;
+ mi.e = pl.e;
+ *plus = pl;
+ *minus = mi;
+ }
+
+ double ToDouble() const {
+ union {
+ double d;
+ uint64_t u64;
+ }u;
+ const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
+ static_cast<uint64_t>(e + kDpExponentBias);
+ u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
+ return u.d;
+ }
+
+ static const int kDiySignificandSize = 64;
+ static const int kDpSignificandSize = 52;
+ static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
+ static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
+ static const int kDpMinExponent = -kDpExponentBias;
+ static const int kDpDenormalExponent = -kDpExponentBias + 1;
+ static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+ static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+ static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+ uint64_t f;
+ int e;
+};
+
+inline DiyFp GetCachedPowerByIndex(size_t index) {
+ // 10^-348, 10^-340, ..., 10^340
+ static const uint64_t kCachedPowers_F[] = {
+ RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
+ RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
+ RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
+ RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
+ RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
+ RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
+ RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
+ RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
+ RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
+ RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
+ RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
+ RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
+ RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
+ RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
+ RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
+ RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
+ RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
+ RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
+ RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
+ RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
+ RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
+ RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
+ RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
+ RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
+ RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
+ RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
+ RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
+ RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
+ RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
+ RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
+ RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
+ RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
+ RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
+ RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
+ RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
+ RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
+ RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
+ RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
+ RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
+ RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
+ RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
+ RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
+ RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
+ RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
+ };
+ static const int16_t kCachedPowers_E[] = {
+ -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
+ -954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
+ -688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
+ -422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
+ -157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
+ 109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
+ 375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
+ 641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
+ 907, 933, 960, 986, 1013, 1039, 1066
+ };
+ return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
+}
+
+inline DiyFp GetCachedPower(int e, int* K) {
+
+ //int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
+ double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
+ int k = static_cast<int>(dk);
+ if (dk - k > 0.0)
+ k++;
+
+ unsigned index = static_cast<unsigned>((k >> 3) + 1);
+ *K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
+
+ return GetCachedPowerByIndex(index);
+}
+
+inline DiyFp GetCachedPower10(int exp, int *outExp) {
+ unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
+ *outExp = -348 + static_cast<int>(index) * 8;
+ return GetCachedPowerByIndex(index);
+ }
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_DIYFP_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/dtoa.h b/ext/librethinkdbxx/src/rapidjson/internal/dtoa.h
new file mode 100644
index 00000000..8d6350e6
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/dtoa.h
@@ -0,0 +1,245 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
+// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
+// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
+
+#ifndef RAPIDJSON_DTOA_
+#define RAPIDJSON_DTOA_
+
+#include "itoa.h" // GetDigitsLut()
+#include "diyfp.h"
+#include "ieee754.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
+#endif
+
+inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
+ while (rest < wp_w && delta - rest >= ten_kappa &&
+ (rest + ten_kappa < wp_w || /// closer
+ wp_w - rest > rest + ten_kappa - wp_w)) {
+ buffer[len - 1]--;
+ rest += ten_kappa;
+ }
+}
+
+inline unsigned CountDecimalDigit32(uint32_t n) {
+ // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
+ if (n < 10) return 1;
+ if (n < 100) return 2;
+ if (n < 1000) return 3;
+ if (n < 10000) return 4;
+ if (n < 100000) return 5;
+ if (n < 1000000) return 6;
+ if (n < 10000000) return 7;
+ if (n < 100000000) return 8;
+ // Will not reach 10 digits in DigitGen()
+ //if (n < 1000000000) return 9;
+ //return 10;
+ return 9;
+}
+
+inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
+ static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
+ const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
+ const DiyFp wp_w = Mp - W;
+ uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
+ uint64_t p2 = Mp.f & (one.f - 1);
+ unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
+ *len = 0;
+
+ while (kappa > 0) {
+ uint32_t d = 0;
+ switch (kappa) {
+ case 9: d = p1 / 100000000; p1 %= 100000000; break;
+ case 8: d = p1 / 10000000; p1 %= 10000000; break;
+ case 7: d = p1 / 1000000; p1 %= 1000000; break;
+ case 6: d = p1 / 100000; p1 %= 100000; break;
+ case 5: d = p1 / 10000; p1 %= 10000; break;
+ case 4: d = p1 / 1000; p1 %= 1000; break;
+ case 3: d = p1 / 100; p1 %= 100; break;
+ case 2: d = p1 / 10; p1 %= 10; break;
+ case 1: d = p1; p1 = 0; break;
+ default:;
+ }
+ if (d || *len)
+ buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
+ kappa--;
+ uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
+ if (tmp <= delta) {
+ *K += kappa;
+ GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
+ return;
+ }
+ }
+
+ // kappa = 0
+ for (;;) {
+ p2 *= 10;
+ delta *= 10;
+ char d = static_cast<char>(p2 >> -one.e);
+ if (d || *len)
+ buffer[(*len)++] = static_cast<char>('0' + d);
+ p2 &= one.f - 1;
+ kappa--;
+ if (p2 < delta) {
+ *K += kappa;
+ int index = -static_cast<int>(kappa);
+ GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
+ return;
+ }
+ }
+}
+
+inline void Grisu2(double value, char* buffer, int* length, int* K) {
+ const DiyFp v(value);
+ DiyFp w_m, w_p;
+ v.NormalizedBoundaries(&w_m, &w_p);
+
+ const DiyFp c_mk = GetCachedPower(w_p.e, K);
+ const DiyFp W = v.Normalize() * c_mk;
+ DiyFp Wp = w_p * c_mk;
+ DiyFp Wm = w_m * c_mk;
+ Wm.f++;
+ Wp.f--;
+ DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
+}
+
+inline char* WriteExponent(int K, char* buffer) {
+ if (K < 0) {
+ *buffer++ = '-';
+ K = -K;
+ }
+
+ if (K >= 100) {
+ *buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
+ K %= 100;
+ const char* d = GetDigitsLut() + K * 2;
+ *buffer++ = d[0];
+ *buffer++ = d[1];
+ }
+ else if (K >= 10) {
+ const char* d = GetDigitsLut() + K * 2;
+ *buffer++ = d[0];
+ *buffer++ = d[1];
+ }
+ else
+ *buffer++ = static_cast<char>('0' + static_cast<char>(K));
+
+ return buffer;
+}
+
+inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
+ const int kk = length + k; // 10^(kk-1) <= v < 10^kk
+
+ if (0 <= k && kk <= 21) {
+ // 1234e7 -> 12340000000
+ for (int i = length; i < kk; i++)
+ buffer[i] = '0';
+ buffer[kk] = '.';
+ buffer[kk + 1] = '0';
+ return &buffer[kk + 2];
+ }
+ else if (0 < kk && kk <= 21) {
+ // 1234e-2 -> 12.34
+ std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
+ buffer[kk] = '.';
+ if (0 > k + maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[kk + 2]; // Reserve one zero
+ }
+ else
+ return &buffer[length + 1];
+ }
+ else if (-6 < kk && kk <= 0) {
+ // 1234e-6 -> 0.001234
+ const int offset = 2 - kk;
+ std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
+ buffer[0] = '0';
+ buffer[1] = '.';
+ for (int i = 2; i < offset; i++)
+ buffer[i] = '0';
+ if (length - kk > maxDecimalPlaces) {
+ // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
+ // Remove extra trailing zeros (at least one) after truncation.
+ for (int i = maxDecimalPlaces + 1; i > 2; i--)
+ if (buffer[i] != '0')
+ return &buffer[i + 1];
+ return &buffer[3]; // Reserve one zero
+ }
+ else
+ return &buffer[length + offset];
+ }
+ else if (kk < -maxDecimalPlaces) {
+ // Truncate to zero
+ buffer[0] = '0';
+ buffer[1] = '.';
+ buffer[2] = '0';
+ return &buffer[3];
+ }
+ else if (length == 1) {
+ // 1e30
+ buffer[1] = 'e';
+ return WriteExponent(kk - 1, &buffer[2]);
+ }
+ else {
+ // 1234e30 -> 1.234e33
+ std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
+ buffer[1] = '.';
+ buffer[length + 1] = 'e';
+ return WriteExponent(kk - 1, &buffer[0 + length + 2]);
+ }
+}
+
+inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
+ RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
+ Double d(value);
+ if (d.IsZero()) {
+ if (d.Sign())
+ *buffer++ = '-'; // -0.0, Issue #289
+ buffer[0] = '0';
+ buffer[1] = '.';
+ buffer[2] = '0';
+ return &buffer[3];
+ }
+ else {
+ if (value < 0) {
+ *buffer++ = '-';
+ value = -value;
+ }
+ int length, K;
+ Grisu2(value, buffer, &length, &K);
+ return Prettify(buffer, length, K, maxDecimalPlaces);
+ }
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_DTOA_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/ieee754.h b/ext/librethinkdbxx/src/rapidjson/internal/ieee754.h
new file mode 100644
index 00000000..82bb0b99
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/ieee754.h
@@ -0,0 +1,78 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_IEEE754_
+#define RAPIDJSON_IEEE754_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+class Double {
+public:
+ Double() {}
+ Double(double d) : d_(d) {}
+ Double(uint64_t u) : u_(u) {}
+
+ double Value() const { return d_; }
+ uint64_t Uint64Value() const { return u_; }
+
+ double NextPositiveDouble() const {
+ RAPIDJSON_ASSERT(!Sign());
+ return Double(u_ + 1).Value();
+ }
+
+ bool Sign() const { return (u_ & kSignMask) != 0; }
+ uint64_t Significand() const { return u_ & kSignificandMask; }
+ int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
+
+ bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
+ bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
+ bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
+ bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
+ bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
+
+ uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
+ int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
+ uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
+
+ static unsigned EffectiveSignificandSize(int order) {
+ if (order >= -1021)
+ return 53;
+ else if (order <= -1074)
+ return 0;
+ else
+ return static_cast<unsigned>(order) + 1074;
+ }
+
+private:
+ static const int kSignificandSize = 52;
+ static const int kExponentBias = 0x3FF;
+ static const int kDenormalExponent = 1 - kExponentBias;
+ static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
+ static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
+ static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
+ static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
+
+ union {
+ double d_;
+ uint64_t u_;
+ };
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_IEEE754_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/itoa.h b/ext/librethinkdbxx/src/rapidjson/internal/itoa.h
new file mode 100644
index 00000000..01a4e7e7
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/itoa.h
@@ -0,0 +1,304 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ITOA_
+#define RAPIDJSON_ITOA_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline const char* GetDigitsLut() {
+ static const char cDigitsLut[200] = {
+ '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
+ '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
+ '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
+ '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
+ '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
+ '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
+ '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
+ '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
+ '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
+ '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
+ };
+ return cDigitsLut;
+}
+
+inline char* u32toa(uint32_t value, char* buffer) {
+ const char* cDigitsLut = GetDigitsLut();
+
+ if (value < 10000) {
+ const uint32_t d1 = (value / 100) << 1;
+ const uint32_t d2 = (value % 100) << 1;
+
+ if (value >= 1000)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= 100)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= 10)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ }
+ else if (value < 100000000) {
+ // value = bbbbcccc
+ const uint32_t b = value / 10000;
+ const uint32_t c = value % 10000;
+
+ const uint32_t d1 = (b / 100) << 1;
+ const uint32_t d2 = (b % 100) << 1;
+
+ const uint32_t d3 = (c / 100) << 1;
+ const uint32_t d4 = (c % 100) << 1;
+
+ if (value >= 10000000)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= 1000000)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= 100000)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ }
+ else {
+ // value = aabbbbcccc in decimal
+
+ const uint32_t a = value / 100000000; // 1 to 42
+ value %= 100000000;
+
+ if (a >= 10) {
+ const unsigned i = a << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ }
+ else
+ *buffer++ = static_cast<char>('0' + static_cast<char>(a));
+
+ const uint32_t b = value / 10000; // 0 to 9999
+ const uint32_t c = value % 10000; // 0 to 9999
+
+ const uint32_t d1 = (b / 100) << 1;
+ const uint32_t d2 = (b % 100) << 1;
+
+ const uint32_t d3 = (c / 100) << 1;
+ const uint32_t d4 = (c % 100) << 1;
+
+ *buffer++ = cDigitsLut[d1];
+ *buffer++ = cDigitsLut[d1 + 1];
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ }
+ return buffer;
+}
+
+inline char* i32toa(int32_t value, char* buffer) {
+ uint32_t u = static_cast<uint32_t>(value);
+ if (value < 0) {
+ *buffer++ = '-';
+ u = ~u + 1;
+ }
+
+ return u32toa(u, buffer);
+}
+
+inline char* u64toa(uint64_t value, char* buffer) {
+ const char* cDigitsLut = GetDigitsLut();
+ const uint64_t kTen8 = 100000000;
+ const uint64_t kTen9 = kTen8 * 10;
+ const uint64_t kTen10 = kTen8 * 100;
+ const uint64_t kTen11 = kTen8 * 1000;
+ const uint64_t kTen12 = kTen8 * 10000;
+ const uint64_t kTen13 = kTen8 * 100000;
+ const uint64_t kTen14 = kTen8 * 1000000;
+ const uint64_t kTen15 = kTen8 * 10000000;
+ const uint64_t kTen16 = kTen8 * kTen8;
+
+ if (value < kTen8) {
+ uint32_t v = static_cast<uint32_t>(value);
+ if (v < 10000) {
+ const uint32_t d1 = (v / 100) << 1;
+ const uint32_t d2 = (v % 100) << 1;
+
+ if (v >= 1000)
+ *buffer++ = cDigitsLut[d1];
+ if (v >= 100)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (v >= 10)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ }
+ else {
+ // value = bbbbcccc
+ const uint32_t b = v / 10000;
+ const uint32_t c = v % 10000;
+
+ const uint32_t d1 = (b / 100) << 1;
+ const uint32_t d2 = (b % 100) << 1;
+
+ const uint32_t d3 = (c / 100) << 1;
+ const uint32_t d4 = (c % 100) << 1;
+
+ if (value >= 10000000)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= 1000000)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= 100000)
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ }
+ }
+ else if (value < kTen16) {
+ const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
+ const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
+
+ const uint32_t b0 = v0 / 10000;
+ const uint32_t c0 = v0 % 10000;
+
+ const uint32_t d1 = (b0 / 100) << 1;
+ const uint32_t d2 = (b0 % 100) << 1;
+
+ const uint32_t d3 = (c0 / 100) << 1;
+ const uint32_t d4 = (c0 % 100) << 1;
+
+ const uint32_t b1 = v1 / 10000;
+ const uint32_t c1 = v1 % 10000;
+
+ const uint32_t d5 = (b1 / 100) << 1;
+ const uint32_t d6 = (b1 % 100) << 1;
+
+ const uint32_t d7 = (c1 / 100) << 1;
+ const uint32_t d8 = (c1 % 100) << 1;
+
+ if (value >= kTen15)
+ *buffer++ = cDigitsLut[d1];
+ if (value >= kTen14)
+ *buffer++ = cDigitsLut[d1 + 1];
+ if (value >= kTen13)
+ *buffer++ = cDigitsLut[d2];
+ if (value >= kTen12)
+ *buffer++ = cDigitsLut[d2 + 1];
+ if (value >= kTen11)
+ *buffer++ = cDigitsLut[d3];
+ if (value >= kTen10)
+ *buffer++ = cDigitsLut[d3 + 1];
+ if (value >= kTen9)
+ *buffer++ = cDigitsLut[d4];
+ if (value >= kTen8)
+ *buffer++ = cDigitsLut[d4 + 1];
+
+ *buffer++ = cDigitsLut[d5];
+ *buffer++ = cDigitsLut[d5 + 1];
+ *buffer++ = cDigitsLut[d6];
+ *buffer++ = cDigitsLut[d6 + 1];
+ *buffer++ = cDigitsLut[d7];
+ *buffer++ = cDigitsLut[d7 + 1];
+ *buffer++ = cDigitsLut[d8];
+ *buffer++ = cDigitsLut[d8 + 1];
+ }
+ else {
+ const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
+ value %= kTen16;
+
+ if (a < 10)
+ *buffer++ = static_cast<char>('0' + static_cast<char>(a));
+ else if (a < 100) {
+ const uint32_t i = a << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ }
+ else if (a < 1000) {
+ *buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
+
+ const uint32_t i = (a % 100) << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ }
+ else {
+ const uint32_t i = (a / 100) << 1;
+ const uint32_t j = (a % 100) << 1;
+ *buffer++ = cDigitsLut[i];
+ *buffer++ = cDigitsLut[i + 1];
+ *buffer++ = cDigitsLut[j];
+ *buffer++ = cDigitsLut[j + 1];
+ }
+
+ const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
+ const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
+
+ const uint32_t b0 = v0 / 10000;
+ const uint32_t c0 = v0 % 10000;
+
+ const uint32_t d1 = (b0 / 100) << 1;
+ const uint32_t d2 = (b0 % 100) << 1;
+
+ const uint32_t d3 = (c0 / 100) << 1;
+ const uint32_t d4 = (c0 % 100) << 1;
+
+ const uint32_t b1 = v1 / 10000;
+ const uint32_t c1 = v1 % 10000;
+
+ const uint32_t d5 = (b1 / 100) << 1;
+ const uint32_t d6 = (b1 % 100) << 1;
+
+ const uint32_t d7 = (c1 / 100) << 1;
+ const uint32_t d8 = (c1 % 100) << 1;
+
+ *buffer++ = cDigitsLut[d1];
+ *buffer++ = cDigitsLut[d1 + 1];
+ *buffer++ = cDigitsLut[d2];
+ *buffer++ = cDigitsLut[d2 + 1];
+ *buffer++ = cDigitsLut[d3];
+ *buffer++ = cDigitsLut[d3 + 1];
+ *buffer++ = cDigitsLut[d4];
+ *buffer++ = cDigitsLut[d4 + 1];
+ *buffer++ = cDigitsLut[d5];
+ *buffer++ = cDigitsLut[d5 + 1];
+ *buffer++ = cDigitsLut[d6];
+ *buffer++ = cDigitsLut[d6 + 1];
+ *buffer++ = cDigitsLut[d7];
+ *buffer++ = cDigitsLut[d7 + 1];
+ *buffer++ = cDigitsLut[d8];
+ *buffer++ = cDigitsLut[d8 + 1];
+ }
+
+ return buffer;
+}
+
+inline char* i64toa(int64_t value, char* buffer) {
+ uint64_t u = static_cast<uint64_t>(value);
+ if (value < 0) {
+ *buffer++ = '-';
+ u = ~u + 1;
+ }
+
+ return u64toa(u, buffer);
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ITOA_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/meta.h b/ext/librethinkdbxx/src/rapidjson/internal/meta.h
new file mode 100644
index 00000000..5a9aaa42
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/meta.h
@@ -0,0 +1,181 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_INTERNAL_META_H_
+#define RAPIDJSON_INTERNAL_META_H_
+
+#include "../rapidjson.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+#if defined(_MSC_VER)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(6334)
+#endif
+
+#if RAPIDJSON_HAS_CXX11_TYPETRAITS
+#include <type_traits>
+#endif
+
+//@cond RAPIDJSON_INTERNAL
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
+template <typename T> struct Void { typedef void Type; };
+
+///////////////////////////////////////////////////////////////////////////////
+// BoolType, TrueType, FalseType
+//
+template <bool Cond> struct BoolType {
+ static const bool Value = Cond;
+ typedef BoolType Type;
+};
+typedef BoolType<true> TrueType;
+typedef BoolType<false> FalseType;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
+//
+
+template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
+template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
+template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
+template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
+
+template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
+template <> struct AndExprCond<true, true> : TrueType {};
+template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
+template <> struct OrExprCond<false, false> : FalseType {};
+
+template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
+template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
+template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
+template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// AddConst, MaybeAddConst, RemoveConst
+template <typename T> struct AddConst { typedef const T Type; };
+template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
+template <typename T> struct RemoveConst { typedef T Type; };
+template <typename T> struct RemoveConst<const T> { typedef T Type; };
+
+
+///////////////////////////////////////////////////////////////////////////////
+// IsSame, IsConst, IsMoreConst, IsPointer
+//
+template <typename T, typename U> struct IsSame : FalseType {};
+template <typename T> struct IsSame<T, T> : TrueType {};
+
+template <typename T> struct IsConst : FalseType {};
+template <typename T> struct IsConst<const T> : TrueType {};
+
+template <typename CT, typename T>
+struct IsMoreConst
+ : AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
+ BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
+
+template <typename T> struct IsPointer : FalseType {};
+template <typename T> struct IsPointer<T*> : TrueType {};
+
+///////////////////////////////////////////////////////////////////////////////
+// IsBaseOf
+//
+#if RAPIDJSON_HAS_CXX11_TYPETRAITS
+
+template <typename B, typename D> struct IsBaseOf
+ : BoolType< ::std::is_base_of<B,D>::value> {};
+
+#else // simplified version adopted from Boost
+
+template<typename B, typename D> struct IsBaseOfImpl {
+ RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
+ RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
+
+ typedef char (&Yes)[1];
+ typedef char (&No) [2];
+
+ template <typename T>
+ static Yes Check(const D*, T);
+ static No Check(const B*, int);
+
+ struct Host {
+ operator const B*() const;
+ operator const D*();
+ };
+
+ enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
+};
+
+template <typename B, typename D> struct IsBaseOf
+ : OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
+
+#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
+
+
+//////////////////////////////////////////////////////////////////////////
+// EnableIf / DisableIf
+//
+template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
+template <typename T> struct EnableIfCond<false, T> { /* empty */ };
+
+template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
+template <typename T> struct DisableIfCond<true, T> { /* empty */ };
+
+template <typename Condition, typename T = void>
+struct EnableIf : EnableIfCond<Condition::Value, T> {};
+
+template <typename Condition, typename T = void>
+struct DisableIf : DisableIfCond<Condition::Value, T> {};
+
+// SFINAE helpers
+struct SfinaeTag {};
+template <typename T> struct RemoveSfinaeTag;
+template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
+
+#define RAPIDJSON_REMOVEFPTR_(type) \
+ typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
+ < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
+
+#define RAPIDJSON_ENABLEIF(cond) \
+ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
+
+#define RAPIDJSON_DISABLEIF(cond) \
+ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
+
+#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
+ typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond), \
+ RAPIDJSON_REMOVEFPTR_(returntype)>::Type
+
+#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
+ typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
+ <RAPIDJSON_REMOVEFPTR_(cond), \
+ RAPIDJSON_REMOVEFPTR_(returntype)>::Type
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+//@endcond
+
+#if defined(__GNUC__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_META_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/pow10.h b/ext/librethinkdbxx/src/rapidjson/internal/pow10.h
new file mode 100644
index 00000000..02f475d7
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/pow10.h
@@ -0,0 +1,55 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_POW10_
+#define RAPIDJSON_POW10_
+
+#include "../rapidjson.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Computes integer powers of 10 in double (10.0^n).
+/*! This function uses lookup table for fast and accurate results.
+ \param n non-negative exponent. Must <= 308.
+ \return 10.0^n
+*/
+inline double Pow10(int n) {
+ static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
+ 1e+0,
+ 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
+ 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
+ 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
+ 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
+ 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
+ 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
+ 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
+ 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
+ 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
+ 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
+ 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
+ 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
+ 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
+ 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
+ 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
+ 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
+ };
+ RAPIDJSON_ASSERT(n >= 0 && n <= 308);
+ return e[n];
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_POW10_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/regex.h b/ext/librethinkdbxx/src/rapidjson/internal/regex.h
new file mode 100644
index 00000000..422a5240
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/regex.h
@@ -0,0 +1,701 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_INTERNAL_REGEX_H_
+#define RAPIDJSON_INTERNAL_REGEX_H_
+
+#include "../allocators.h"
+#include "../stream.h"
+#include "stack.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(implicit-fallthrough)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+#ifndef RAPIDJSON_REGEX_VERBOSE
+#define RAPIDJSON_REGEX_VERBOSE 0
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericRegex
+
+static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
+static const SizeType kRegexInvalidRange = ~SizeType(0);
+
+//! Regular expression engine with subset of ECMAscript grammar.
+/*!
+ Supported regular expression syntax:
+ - \c ab Concatenation
+ - \c a|b Alternation
+ - \c a? Zero or one
+ - \c a* Zero or more
+ - \c a+ One or more
+ - \c a{3} Exactly 3 times
+ - \c a{3,} At least 3 times
+ - \c a{3,5} 3 to 5 times
+ - \c (ab) Grouping
+ - \c ^a At the beginning
+ - \c a$ At the end
+ - \c . Any character
+ - \c [abc] Character classes
+ - \c [a-c] Character class range
+ - \c [a-z0-9_] Character class combination
+ - \c [^abc] Negated character classes
+ - \c [^a-c] Negated character class range
+ - \c [\b] Backspace (U+0008)
+ - \c \\| \\\\ ... Escape characters
+ - \c \\f Form feed (U+000C)
+ - \c \\n Line feed (U+000A)
+ - \c \\r Carriage return (U+000D)
+ - \c \\t Tab (U+0009)
+ - \c \\v Vertical tab (U+000B)
+
+ \note This is a Thompson NFA engine, implemented with reference to
+ Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
+ https://swtch.com/~rsc/regexp/regexp1.html
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericRegex {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ GenericRegex(const Ch* source, Allocator* allocator = 0) :
+ states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
+ stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
+ {
+ GenericStringStream<Encoding> ss(source);
+ DecodedStream<GenericStringStream<Encoding> > ds(ss);
+ Parse(ds);
+ }
+
+ ~GenericRegex() {
+ Allocator::Free(stateSet_);
+ }
+
+ bool IsValid() const {
+ return root_ != kRegexInvalidState;
+ }
+
+ template <typename InputStream>
+ bool Match(InputStream& is) const {
+ return SearchWithAnchoring(is, true, true);
+ }
+
+ bool Match(const Ch* s) const {
+ GenericStringStream<Encoding> is(s);
+ return Match(is);
+ }
+
+ template <typename InputStream>
+ bool Search(InputStream& is) const {
+ return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
+ }
+
+ bool Search(const Ch* s) const {
+ GenericStringStream<Encoding> is(s);
+ return Search(is);
+ }
+
+private:
+ enum Operator {
+ kZeroOrOne,
+ kZeroOrMore,
+ kOneOrMore,
+ kConcatenation,
+ kAlternation,
+ kLeftParenthesis
+ };
+
+ static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
+ static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
+ static const unsigned kRangeNegationFlag = 0x80000000;
+
+ struct Range {
+ unsigned start; //
+ unsigned end;
+ SizeType next;
+ };
+
+ struct State {
+ SizeType out; //!< Equals to kInvalid for matching state
+ SizeType out1; //!< Equals to non-kInvalid for split
+ SizeType rangeStart;
+ unsigned codepoint;
+ };
+
+ struct Frag {
+ Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
+ SizeType start;
+ SizeType out; //!< link-list of all output states
+ SizeType minIndex;
+ };
+
+ template <typename SourceStream>
+ class DecodedStream {
+ public:
+ DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
+ unsigned Peek() { return codepoint_; }
+ unsigned Take() {
+ unsigned c = codepoint_;
+ if (c) // No further decoding when '\0'
+ Decode();
+ return c;
+ }
+
+ private:
+ void Decode() {
+ if (!Encoding::Decode(ss_, &codepoint_))
+ codepoint_ = 0;
+ }
+
+ SourceStream& ss_;
+ unsigned codepoint_;
+ };
+
+ State& GetState(SizeType index) {
+ RAPIDJSON_ASSERT(index < stateCount_);
+ return states_.template Bottom<State>()[index];
+ }
+
+ const State& GetState(SizeType index) const {
+ RAPIDJSON_ASSERT(index < stateCount_);
+ return states_.template Bottom<State>()[index];
+ }
+
+ Range& GetRange(SizeType index) {
+ RAPIDJSON_ASSERT(index < rangeCount_);
+ return ranges_.template Bottom<Range>()[index];
+ }
+
+ const Range& GetRange(SizeType index) const {
+ RAPIDJSON_ASSERT(index < rangeCount_);
+ return ranges_.template Bottom<Range>()[index];
+ }
+
+ template <typename InputStream>
+ void Parse(DecodedStream<InputStream>& ds) {
+ Allocator allocator;
+ Stack<Allocator> operandStack(&allocator, 256); // Frag
+ Stack<Allocator> operatorStack(&allocator, 256); // Operator
+ Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
+
+ *atomCountStack.template Push<unsigned>() = 0;
+
+ unsigned codepoint;
+ while (ds.Peek() != 0) {
+ switch (codepoint = ds.Take()) {
+ case '^':
+ anchorBegin_ = true;
+ break;
+
+ case '$':
+ anchorEnd_ = true;
+ break;
+
+ case '|':
+ while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+ *operatorStack.template Push<Operator>() = kAlternation;
+ *atomCountStack.template Top<unsigned>() = 0;
+ break;
+
+ case '(':
+ *operatorStack.template Push<Operator>() = kLeftParenthesis;
+ *atomCountStack.template Push<unsigned>() = 0;
+ break;
+
+ case ')':
+ while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+ if (operatorStack.Empty())
+ return;
+ operatorStack.template Pop<Operator>(1);
+ atomCountStack.template Pop<unsigned>(1);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '?':
+ if (!Eval(operandStack, kZeroOrOne))
+ return;
+ break;
+
+ case '*':
+ if (!Eval(operandStack, kZeroOrMore))
+ return;
+ break;
+
+ case '+':
+ if (!Eval(operandStack, kOneOrMore))
+ return;
+ break;
+
+ case '{':
+ {
+ unsigned n, m;
+ if (!ParseUnsigned(ds, &n))
+ return;
+
+ if (ds.Peek() == ',') {
+ ds.Take();
+ if (ds.Peek() == '}')
+ m = kInfinityQuantifier;
+ else if (!ParseUnsigned(ds, &m) || m < n)
+ return;
+ }
+ else
+ m = n;
+
+ if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
+ return;
+ ds.Take();
+ }
+ break;
+
+ case '.':
+ PushOperand(operandStack, kAnyCharacterClass);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '[':
+ {
+ SizeType range;
+ if (!ParseRange(ds, &range))
+ return;
+ SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
+ GetState(s).rangeStart = range;
+ *operandStack.template Push<Frag>() = Frag(s, s, s);
+ }
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ break;
+
+ case '\\': // Escape character
+ if (!CharacterEscape(ds, &codepoint))
+ return; // Unsupported escape character
+ // fall through to default
+
+ default: // Pattern character
+ PushOperand(operandStack, codepoint);
+ ImplicitConcatenation(atomCountStack, operatorStack);
+ }
+ }
+
+ while (!operatorStack.Empty())
+ if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
+ return;
+
+ // Link the operand to matching state.
+ if (operandStack.GetSize() == sizeof(Frag)) {
+ Frag* e = operandStack.template Pop<Frag>(1);
+ Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
+ root_ = e->start;
+
+#if RAPIDJSON_REGEX_VERBOSE
+ printf("root: %d\n", root_);
+ for (SizeType i = 0; i < stateCount_ ; i++) {
+ State& s = GetState(i);
+ printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
+ }
+ printf("\n");
+#endif
+ }
+
+ // Preallocate buffer for SearchWithAnchoring()
+ RAPIDJSON_ASSERT(stateSet_ == 0);
+ if (stateCount_ > 0) {
+ stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
+ state0_.template Reserve<SizeType>(stateCount_);
+ state1_.template Reserve<SizeType>(stateCount_);
+ }
+ }
+
+ SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
+ State* s = states_.template Push<State>();
+ s->out = out;
+ s->out1 = out1;
+ s->codepoint = codepoint;
+ s->rangeStart = kRegexInvalidRange;
+ return stateCount_++;
+ }
+
+ void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
+ SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
+ *operandStack.template Push<Frag>() = Frag(s, s, s);
+ }
+
+ void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
+ if (*atomCountStack.template Top<unsigned>())
+ *operatorStack.template Push<Operator>() = kConcatenation;
+ (*atomCountStack.template Top<unsigned>())++;
+ }
+
+ SizeType Append(SizeType l1, SizeType l2) {
+ SizeType old = l1;
+ while (GetState(l1).out != kRegexInvalidState)
+ l1 = GetState(l1).out;
+ GetState(l1).out = l2;
+ return old;
+ }
+
+ void Patch(SizeType l, SizeType s) {
+ for (SizeType next; l != kRegexInvalidState; l = next) {
+ next = GetState(l).out;
+ GetState(l).out = s;
+ }
+ }
+
+ bool Eval(Stack<Allocator>& operandStack, Operator op) {
+ switch (op) {
+ case kConcatenation:
+ RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
+ {
+ Frag e2 = *operandStack.template Pop<Frag>(1);
+ Frag e1 = *operandStack.template Pop<Frag>(1);
+ Patch(e1.out, e2.start);
+ *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
+ }
+ return true;
+
+ case kAlternation:
+ if (operandStack.GetSize() >= sizeof(Frag) * 2) {
+ Frag e2 = *operandStack.template Pop<Frag>(1);
+ Frag e1 = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(e1.start, e2.start, 0);
+ *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
+ return true;
+ }
+ return false;
+
+ case kZeroOrOne:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
+ return true;
+ }
+ return false;
+
+ case kZeroOrMore:
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ Patch(e.out, s);
+ *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
+ return true;
+ }
+ return false;
+
+ default:
+ RAPIDJSON_ASSERT(op == kOneOrMore);
+ if (operandStack.GetSize() >= sizeof(Frag)) {
+ Frag e = *operandStack.template Pop<Frag>(1);
+ SizeType s = NewState(kRegexInvalidState, e.start, 0);
+ Patch(e.out, s);
+ *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
+ RAPIDJSON_ASSERT(n <= m);
+ RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
+
+ if (n == 0) {
+ if (m == 0) // a{0} not support
+ return false;
+ else if (m == kInfinityQuantifier)
+ Eval(operandStack, kZeroOrMore); // a{0,} -> a*
+ else {
+ Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
+ for (unsigned i = 0; i < m - 1; i++)
+ CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
+ for (unsigned i = 0; i < m - 1; i++)
+ Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
+ }
+ return true;
+ }
+
+ for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
+ CloneTopOperand(operandStack);
+
+ if (m == kInfinityQuantifier)
+ Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
+ else if (m > n) {
+ CloneTopOperand(operandStack); // a{3,5} -> a a a a
+ Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
+ for (unsigned i = n; i < m - 1; i++)
+ CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
+ for (unsigned i = n; i < m; i++)
+ Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
+ }
+
+ for (unsigned i = 0; i < n - 1; i++)
+ Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
+
+ return true;
+ }
+
+ static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
+
+ void CloneTopOperand(Stack<Allocator>& operandStack) {
+ const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
+ SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
+ State* s = states_.template Push<State>(count);
+ memcpy(s, &GetState(src.minIndex), count * sizeof(State));
+ for (SizeType j = 0; j < count; j++) {
+ if (s[j].out != kRegexInvalidState)
+ s[j].out += count;
+ if (s[j].out1 != kRegexInvalidState)
+ s[j].out1 += count;
+ }
+ *operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
+ stateCount_ += count;
+ }
+
+ template <typename InputStream>
+ bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
+ unsigned r = 0;
+ if (ds.Peek() < '0' || ds.Peek() > '9')
+ return false;
+ while (ds.Peek() >= '0' && ds.Peek() <= '9') {
+ if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
+ return false; // overflow
+ r = r * 10 + (ds.Take() - '0');
+ }
+ *u = r;
+ return true;
+ }
+
+ template <typename InputStream>
+ bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
+ bool isBegin = true;
+ bool negate = false;
+ int step = 0;
+ SizeType start = kRegexInvalidRange;
+ SizeType current = kRegexInvalidRange;
+ unsigned codepoint;
+ while ((codepoint = ds.Take()) != 0) {
+ if (isBegin) {
+ isBegin = false;
+ if (codepoint == '^') {
+ negate = true;
+ continue;
+ }
+ }
+
+ switch (codepoint) {
+ case ']':
+ if (start == kRegexInvalidRange)
+ return false; // Error: nothing inside []
+ if (step == 2) { // Add trailing '-'
+ SizeType r = NewRange('-');
+ RAPIDJSON_ASSERT(current != kRegexInvalidRange);
+ GetRange(current).next = r;
+ }
+ if (negate)
+ GetRange(start).start |= kRangeNegationFlag;
+ *range = start;
+ return true;
+
+ case '\\':
+ if (ds.Peek() == 'b') {
+ ds.Take();
+ codepoint = 0x0008; // Escape backspace character
+ }
+ else if (!CharacterEscape(ds, &codepoint))
+ return false;
+ // fall through to default
+
+ default:
+ switch (step) {
+ case 1:
+ if (codepoint == '-') {
+ step++;
+ break;
+ }
+ // fall through to step 0 for other characters
+
+ case 0:
+ {
+ SizeType r = NewRange(codepoint);
+ if (current != kRegexInvalidRange)
+ GetRange(current).next = r;
+ if (start == kRegexInvalidRange)
+ start = r;
+ current = r;
+ }
+ step = 1;
+ break;
+
+ default:
+ RAPIDJSON_ASSERT(step == 2);
+ GetRange(current).end = codepoint;
+ step = 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ SizeType NewRange(unsigned codepoint) {
+ Range* r = ranges_.template Push<Range>();
+ r->start = r->end = codepoint;
+ r->next = kRegexInvalidRange;
+ return rangeCount_++;
+ }
+
+ template <typename InputStream>
+ bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
+ unsigned codepoint;
+ switch (codepoint = ds.Take()) {
+ case '^':
+ case '$':
+ case '|':
+ case '(':
+ case ')':
+ case '?':
+ case '*':
+ case '+':
+ case '.':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '\\':
+ *escapedCodepoint = codepoint; return true;
+ case 'f': *escapedCodepoint = 0x000C; return true;
+ case 'n': *escapedCodepoint = 0x000A; return true;
+ case 'r': *escapedCodepoint = 0x000D; return true;
+ case 't': *escapedCodepoint = 0x0009; return true;
+ case 'v': *escapedCodepoint = 0x000B; return true;
+ default:
+ return false; // Unsupported escape character
+ }
+ }
+
+ template <typename InputStream>
+ bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
+ RAPIDJSON_ASSERT(IsValid());
+ DecodedStream<InputStream> ds(is);
+
+ state0_.Clear();
+ Stack<Allocator> *current = &state0_, *next = &state1_;
+ const size_t stateSetSize = GetStateSetSize();
+ std::memset(stateSet_, 0, stateSetSize);
+
+ bool matched = AddState(*current, root_);
+ unsigned codepoint;
+ while (!current->Empty() && (codepoint = ds.Take()) != 0) {
+ std::memset(stateSet_, 0, stateSetSize);
+ next->Clear();
+ matched = false;
+ for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
+ const State& sr = GetState(*s);
+ if (sr.codepoint == codepoint ||
+ sr.codepoint == kAnyCharacterClass ||
+ (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
+ {
+ matched = AddState(*next, sr.out) || matched;
+ if (!anchorEnd && matched)
+ return true;
+ }
+ if (!anchorBegin)
+ AddState(*next, root_);
+ }
+ internal::Swap(current, next);
+ }
+
+ return matched;
+ }
+
+ size_t GetStateSetSize() const {
+ return (stateCount_ + 31) / 32 * 4;
+ }
+
+ // Return whether the added states is a match state
+ bool AddState(Stack<Allocator>& l, SizeType index) const {
+ RAPIDJSON_ASSERT(index != kRegexInvalidState);
+
+ const State& s = GetState(index);
+ if (s.out1 != kRegexInvalidState) { // Split
+ bool matched = AddState(l, s.out);
+ return AddState(l, s.out1) || matched;
+ }
+ else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) {
+ stateSet_[index >> 5] |= (1 << (index & 31));
+ *l.template PushUnsafe<SizeType>() = index;
+ }
+ return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
+ }
+
+ bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
+ bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
+ while (rangeIndex != kRegexInvalidRange) {
+ const Range& r = GetRange(rangeIndex);
+ if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
+ return yes;
+ rangeIndex = r.next;
+ }
+ return !yes;
+ }
+
+ Stack<Allocator> states_;
+ Stack<Allocator> ranges_;
+ SizeType root_;
+ SizeType stateCount_;
+ SizeType rangeCount_;
+
+ static const unsigned kInfinityQuantifier = ~0u;
+
+ // For SearchWithAnchoring()
+ uint32_t* stateSet_; // allocated by states_.GetAllocator()
+ mutable Stack<Allocator> state0_;
+ mutable Stack<Allocator> state1_;
+ bool anchorBegin_;
+ bool anchorEnd_;
+};
+
+typedef GenericRegex<UTF8<> > Regex;
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_REGEX_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/stack.h b/ext/librethinkdbxx/src/rapidjson/internal/stack.h
new file mode 100644
index 00000000..022c9aab
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/stack.h
@@ -0,0 +1,230 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_INTERNAL_STACK_H_
+#define RAPIDJSON_INTERNAL_STACK_H_
+
+#include "../allocators.h"
+#include "swap.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+///////////////////////////////////////////////////////////////////////////////
+// Stack
+
+//! A type-unsafe stack for storing different types of data.
+/*! \tparam Allocator Allocator for allocating stack memory.
+*/
+template <typename Allocator>
+class Stack {
+public:
+ // Optimization note: Do not allocate memory for stack_ in constructor.
+ // Do it lazily when first Push() -> Expand() -> Resize().
+ Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ Stack(Stack&& rhs)
+ : allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ stack_(rhs.stack_),
+ stackTop_(rhs.stackTop_),
+ stackEnd_(rhs.stackEnd_),
+ initialCapacity_(rhs.initialCapacity_)
+ {
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.stack_ = 0;
+ rhs.stackTop_ = 0;
+ rhs.stackEnd_ = 0;
+ rhs.initialCapacity_ = 0;
+ }
+#endif
+
+ ~Stack() {
+ Destroy();
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ Stack& operator=(Stack&& rhs) {
+ if (&rhs != this)
+ {
+ Destroy();
+
+ allocator_ = rhs.allocator_;
+ ownAllocator_ = rhs.ownAllocator_;
+ stack_ = rhs.stack_;
+ stackTop_ = rhs.stackTop_;
+ stackEnd_ = rhs.stackEnd_;
+ initialCapacity_ = rhs.initialCapacity_;
+
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ rhs.stack_ = 0;
+ rhs.stackTop_ = 0;
+ rhs.stackEnd_ = 0;
+ rhs.initialCapacity_ = 0;
+ }
+ return *this;
+ }
+#endif
+
+ void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
+ internal::Swap(allocator_, rhs.allocator_);
+ internal::Swap(ownAllocator_, rhs.ownAllocator_);
+ internal::Swap(stack_, rhs.stack_);
+ internal::Swap(stackTop_, rhs.stackTop_);
+ internal::Swap(stackEnd_, rhs.stackEnd_);
+ internal::Swap(initialCapacity_, rhs.initialCapacity_);
+ }
+
+ void Clear() { stackTop_ = stack_; }
+
+ void ShrinkToFit() {
+ if (Empty()) {
+ // If the stack is empty, completely deallocate the memory.
+ Allocator::Free(stack_);
+ stack_ = 0;
+ stackTop_ = 0;
+ stackEnd_ = 0;
+ }
+ else
+ Resize(GetSize());
+ }
+
+ // Optimization note: try to minimize the size of this function for force inline.
+ // Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
+ template<typename T>
+ RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
+ // Expand the stack if needed
+ if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
+ Expand<T>(count);
+ }
+
+ template<typename T>
+ RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
+ Reserve<T>(count);
+ return PushUnsafe<T>(count);
+ }
+
+ template<typename T>
+ RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
+ RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
+ T* ret = reinterpret_cast<T*>(stackTop_);
+ stackTop_ += sizeof(T) * count;
+ return ret;
+ }
+
+ template<typename T>
+ T* Pop(size_t count) {
+ RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
+ stackTop_ -= count * sizeof(T);
+ return reinterpret_cast<T*>(stackTop_);
+ }
+
+ template<typename T>
+ T* Top() {
+ RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+ return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+ }
+
+ template<typename T>
+ const T* Top() const {
+ RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
+ return reinterpret_cast<T*>(stackTop_ - sizeof(T));
+ }
+
+ template<typename T>
+ T* End() { return reinterpret_cast<T*>(stackTop_); }
+
+ template<typename T>
+ const T* End() const { return reinterpret_cast<T*>(stackTop_); }
+
+ template<typename T>
+ T* Bottom() { return reinterpret_cast<T*>(stack_); }
+
+ template<typename T>
+ const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
+
+ bool HasAllocator() const {
+ return allocator_ != 0;
+ }
+
+ Allocator& GetAllocator() {
+ RAPIDJSON_ASSERT(allocator_);
+ return *allocator_;
+ }
+
+ bool Empty() const { return stackTop_ == stack_; }
+ size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
+ size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
+
+private:
+ template<typename T>
+ void Expand(size_t count) {
+ // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
+ size_t newCapacity;
+ if (stack_ == 0) {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+ newCapacity = initialCapacity_;
+ } else {
+ newCapacity = GetCapacity();
+ newCapacity += (newCapacity + 1) / 2;
+ }
+ size_t newSize = GetSize() + sizeof(T) * count;
+ if (newCapacity < newSize)
+ newCapacity = newSize;
+
+ Resize(newCapacity);
+ }
+
+ void Resize(size_t newCapacity) {
+ const size_t size = GetSize(); // Backup the current size
+ stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
+ stackTop_ = stack_ + size;
+ stackEnd_ = stack_ + newCapacity;
+ }
+
+ void Destroy() {
+ Allocator::Free(stack_);
+ RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
+ }
+
+ // Prohibit copy constructor & assignment operator.
+ Stack(const Stack&);
+ Stack& operator=(const Stack&);
+
+ Allocator* allocator_;
+ Allocator* ownAllocator_;
+ char *stack_;
+ char *stackTop_;
+ char *stackEnd_;
+ size_t initialCapacity_;
+};
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_STACK_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/strfunc.h b/ext/librethinkdbxx/src/rapidjson/internal/strfunc.h
new file mode 100644
index 00000000..2edfae52
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/strfunc.h
@@ -0,0 +1,55 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
+#define RAPIDJSON_INTERNAL_STRFUNC_H_
+
+#include "../stream.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Custom strlen() which works on different character types.
+/*! \tparam Ch Character type (e.g. char, wchar_t, short)
+ \param s Null-terminated input string.
+ \return Number of characters in the string.
+ \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
+*/
+template <typename Ch>
+inline SizeType StrLen(const Ch* s) {
+ const Ch* p = s;
+ while (*p) ++p;
+ return SizeType(p - s);
+}
+
+//! Returns number of code points in a encoded string.
+template<typename Encoding>
+bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
+ GenericStringStream<Encoding> is(s);
+ const typename Encoding::Ch* end = s + length;
+ SizeType count = 0;
+ while (is.src_ < end) {
+ unsigned codepoint;
+ if (!Encoding::Decode(is, &codepoint))
+ return false;
+ count++;
+ }
+ *outCount = count;
+ return true;
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/strtod.h b/ext/librethinkdbxx/src/rapidjson/internal/strtod.h
new file mode 100644
index 00000000..289c413b
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/strtod.h
@@ -0,0 +1,269 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_STRTOD_
+#define RAPIDJSON_STRTOD_
+
+#include "ieee754.h"
+#include "biginteger.h"
+#include "diyfp.h"
+#include "pow10.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+inline double FastPath(double significand, int exp) {
+ if (exp < -308)
+ return 0.0;
+ else if (exp >= 0)
+ return significand * internal::Pow10(exp);
+ else
+ return significand / internal::Pow10(-exp);
+}
+
+inline double StrtodNormalPrecision(double d, int p) {
+ if (p < -308) {
+ // Prevent expSum < -308, making Pow10(p) = 0
+ d = FastPath(d, -308);
+ d = FastPath(d, p + 308);
+ }
+ else
+ d = FastPath(d, p);
+ return d;
+}
+
+template <typename T>
+inline T Min3(T a, T b, T c) {
+ T m = a;
+ if (m > b) m = b;
+ if (m > c) m = c;
+ return m;
+}
+
+inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
+ const Double db(b);
+ const uint64_t bInt = db.IntegerSignificand();
+ const int bExp = db.IntegerExponent();
+ const int hExp = bExp - 1;
+
+ int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
+
+ // Adjust for decimal exponent
+ if (dExp >= 0) {
+ dS_Exp2 += dExp;
+ dS_Exp5 += dExp;
+ }
+ else {
+ bS_Exp2 -= dExp;
+ bS_Exp5 -= dExp;
+ hS_Exp2 -= dExp;
+ hS_Exp5 -= dExp;
+ }
+
+ // Adjust for binary exponent
+ if (bExp >= 0)
+ bS_Exp2 += bExp;
+ else {
+ dS_Exp2 -= bExp;
+ hS_Exp2 -= bExp;
+ }
+
+ // Adjust for half ulp exponent
+ if (hExp >= 0)
+ hS_Exp2 += hExp;
+ else {
+ dS_Exp2 -= hExp;
+ bS_Exp2 -= hExp;
+ }
+
+ // Remove common power of two factor from all three scaled values
+ int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
+ dS_Exp2 -= common_Exp2;
+ bS_Exp2 -= common_Exp2;
+ hS_Exp2 -= common_Exp2;
+
+ BigInteger dS = d;
+ dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
+
+ BigInteger bS(bInt);
+ bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
+
+ BigInteger hS(1);
+ hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
+
+ BigInteger delta(0);
+ dS.Difference(bS, &delta);
+
+ return delta.Compare(hS);
+}
+
+inline bool StrtodFast(double d, int p, double* result) {
+ // Use fast path for string-to-double conversion if possible
+ // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
+ if (p > 22 && p < 22 + 16) {
+ // Fast Path Cases In Disguise
+ d *= internal::Pow10(p - 22);
+ p = 22;
+ }
+
+ if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
+ *result = FastPath(d, p);
+ return true;
+ }
+ else
+ return false;
+}
+
+// Compute an approximation and see if it is within 1/2 ULP
+inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
+ uint64_t significand = 0;
+ size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
+ for (; i < length; i++) {
+ if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
+ (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
+ break;
+ significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
+ }
+
+ if (i < length && decimals[i] >= '5') // Rounding
+ significand++;
+
+ size_t remaining = length - i;
+ const unsigned kUlpShift = 3;
+ const unsigned kUlp = 1 << kUlpShift;
+ int64_t error = (remaining == 0) ? 0 : kUlp / 2;
+
+ DiyFp v(significand, 0);
+ v = v.Normalize();
+ error <<= -v.e;
+
+ const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
+
+ int actualExp;
+ DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
+ if (actualExp != dExp) {
+ static const DiyFp kPow10[] = {
+ DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
+ DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
+ DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
+ DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
+ DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
+ DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
+ DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
+ };
+ int adjustment = dExp - actualExp - 1;
+ RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
+ v = v * kPow10[adjustment];
+ if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
+ error += kUlp / 2;
+ }
+
+ v = v * cachedPower;
+
+ error += kUlp + (error == 0 ? 0 : 1);
+
+ const int oldExp = v.e;
+ v = v.Normalize();
+ error <<= oldExp - v.e;
+
+ const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
+ unsigned precisionSize = 64 - effectiveSignificandSize;
+ if (precisionSize + kUlpShift >= 64) {
+ unsigned scaleExp = (precisionSize + kUlpShift) - 63;
+ v.f >>= scaleExp;
+ v.e += scaleExp;
+ error = (error >> scaleExp) + 1 + static_cast<int>(kUlp);
+ precisionSize -= scaleExp;
+ }
+
+ DiyFp rounded(v.f >> precisionSize, v.e + static_cast<int>(precisionSize));
+ const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
+ const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
+ if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
+ rounded.f++;
+ if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
+ rounded.f >>= 1;
+ rounded.e++;
+ }
+ }
+
+ *result = rounded.ToDouble();
+
+ return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
+}
+
+inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+ const BigInteger dInt(decimals, length);
+ const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
+ Double a(approx);
+ int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
+ if (cmp < 0)
+ return a.Value(); // within half ULP
+ else if (cmp == 0) {
+ // Round towards even
+ if (a.Significand() & 1)
+ return a.NextPositiveDouble();
+ else
+ return a.Value();
+ }
+ else // adjustment
+ return a.NextPositiveDouble();
+}
+
+inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
+ RAPIDJSON_ASSERT(d >= 0.0);
+ RAPIDJSON_ASSERT(length >= 1);
+
+ double result;
+ if (StrtodFast(d, p, &result))
+ return result;
+
+ // Trim leading zeros
+ while (*decimals == '0' && length > 1) {
+ length--;
+ decimals++;
+ decimalPosition--;
+ }
+
+ // Trim trailing zeros
+ while (decimals[length - 1] == '0' && length > 1) {
+ length--;
+ decimalPosition--;
+ exp++;
+ }
+
+ // Trim right-most digits
+ const int kMaxDecimalDigit = 780;
+ if (static_cast<int>(length) > kMaxDecimalDigit) {
+ int delta = (static_cast<int>(length) - kMaxDecimalDigit);
+ exp += delta;
+ decimalPosition -= static_cast<unsigned>(delta);
+ length = kMaxDecimalDigit;
+ }
+
+ // If too small, underflow to zero
+ if (int(length) + exp < -324)
+ return 0.0;
+
+ if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
+ return result;
+
+ // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
+ return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STRTOD_
diff --git a/ext/librethinkdbxx/src/rapidjson/internal/swap.h b/ext/librethinkdbxx/src/rapidjson/internal/swap.h
new file mode 100644
index 00000000..666e49f9
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/internal/swap.h
@@ -0,0 +1,46 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_INTERNAL_SWAP_H_
+#define RAPIDJSON_INTERNAL_SWAP_H_
+
+#include "../rapidjson.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+namespace internal {
+
+//! Custom swap() to avoid dependency on C++ <algorithm> header
+/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
+ \note This has the same semantics as std::swap().
+*/
+template <typename T>
+inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
+ T tmp = a;
+ a = b;
+ b = tmp;
+}
+
+} // namespace internal
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_INTERNAL_SWAP_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/istreamwrapper.h b/ext/librethinkdbxx/src/rapidjson/istreamwrapper.h
new file mode 100644
index 00000000..f5fe2897
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/istreamwrapper.h
@@ -0,0 +1,115 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_ISTREAMWRAPPER_H_
+#define RAPIDJSON_ISTREAMWRAPPER_H_
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
+/*!
+ The classes can be wrapped including but not limited to:
+
+ - \c std::istringstream
+ - \c std::stringstream
+ - \c std::wistringstream
+ - \c std::wstringstream
+ - \c std::ifstream
+ - \c std::fstream
+ - \c std::wifstream
+ - \c std::wfstream
+
+ \tparam StreamType Class derived from \c std::basic_istream.
+*/
+
+template <typename StreamType>
+class BasicIStreamWrapper {
+public:
+ typedef typename StreamType::char_type Ch;
+ BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {}
+
+ Ch Peek() const {
+ typename StreamType::int_type c = stream_.peek();
+ return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast<Ch>(c) : '\0';
+ }
+
+ Ch Take() {
+ typename StreamType::int_type c = stream_.get();
+ if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) {
+ count_++;
+ return static_cast<Ch>(c);
+ }
+ else
+ return '\0';
+ }
+
+ // tellg() may return -1 when failed. So we count by ourself.
+ size_t Tell() const { return count_; }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream.
+ int i;
+ bool hasError = false;
+ for (i = 0; i < 4; ++i) {
+ typename StreamType::int_type c = stream_.get();
+ if (c == StreamType::traits_type::eof()) {
+ hasError = true;
+ stream_.clear();
+ break;
+ }
+ peekBuffer_[i] = static_cast<Ch>(c);
+ }
+ for (--i; i >= 0; --i)
+ stream_.putback(peekBuffer_[i]);
+ return !hasError ? peekBuffer_ : 0;
+ }
+
+private:
+ BasicIStreamWrapper(const BasicIStreamWrapper&);
+ BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
+
+ StreamType& stream_;
+ size_t count_; //!< Number of characters read. Note:
+ mutable Ch peekBuffer_[4];
+};
+
+typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
+typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
+
+#if defined(__clang__) || defined(_MSC_VER)
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ISTREAMWRAPPER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/memorybuffer.h b/ext/librethinkdbxx/src/rapidjson/memorybuffer.h
new file mode 100644
index 00000000..39bee1de
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/memorybuffer.h
@@ -0,0 +1,70 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_MEMORYBUFFER_H_
+#define RAPIDJSON_MEMORYBUFFER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory output byte stream.
+/*!
+ This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
+
+ It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
+
+ Differences between MemoryBuffer and StringBuffer:
+ 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
+ 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
+
+ \tparam Allocator type for allocating memory buffer.
+ \note implements Stream concept
+*/
+template <typename Allocator = CrtAllocator>
+struct GenericMemoryBuffer {
+ typedef char Ch; // byte
+
+ GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
+
+ void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+ void Flush() {}
+
+ void Clear() { stack_.Clear(); }
+ void ShrinkToFit() { stack_.ShrinkToFit(); }
+ Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+ void Pop(size_t count) { stack_.template Pop<Ch>(count); }
+
+ const Ch* GetBuffer() const {
+ return stack_.template Bottom<Ch>();
+ }
+
+ size_t GetSize() const { return stack_.GetSize(); }
+
+ static const size_t kDefaultCapacity = 256;
+ mutable internal::Stack<Allocator> stack_;
+};
+
+typedef GenericMemoryBuffer<> MemoryBuffer;
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
+ std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_MEMORYBUFFER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/memorystream.h b/ext/librethinkdbxx/src/rapidjson/memorystream.h
new file mode 100644
index 00000000..1d71d8a4
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/memorystream.h
@@ -0,0 +1,71 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_MEMORYSTREAM_H_
+#define RAPIDJSON_MEMORYSTREAM_H_
+
+#include "stream.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(unreachable-code)
+RAPIDJSON_DIAG_OFF(missing-noreturn)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory input byte stream.
+/*!
+ This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
+
+ It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
+
+ Differences between MemoryStream and StringStream:
+ 1. StringStream has encoding but MemoryStream is a byte stream.
+ 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
+ 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
+ \note implements Stream concept
+*/
+struct MemoryStream {
+ typedef char Ch; // byte
+
+ MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
+
+ Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
+ Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
+ size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ // For encoding detection only.
+ const Ch* Peek4() const {
+ return Tell() + 4 <= size_ ? src_ : 0;
+ }
+
+ const Ch* src_; //!< Current read position.
+ const Ch* begin_; //!< Original head of the string.
+ const Ch* end_; //!< End of stream.
+ size_t size_; //!< Size of the stream.
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_MEMORYBUFFER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h b/ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h
new file mode 100644
index 00000000..18111286
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/msinttypes/inttypes.h
@@ -0,0 +1,316 @@
+// ISO C9x compliant inttypes.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2013 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the product nor the names of its contributors may
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// The above software in this distribution may have been modified by
+// THL A29 Limited ("Tencent Modifications").
+// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_INTTYPES_H_ // [
+#define _MSC_INTTYPES_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include "stdint.h"
+
+// miloyip: VC supports inttypes.h since VC2013
+#if _MSC_VER >= 1800
+#include <inttypes.h>
+#else
+
+// 7.8 Format conversion of integer types
+
+typedef struct {
+ intmax_t quot;
+ intmax_t rem;
+} imaxdiv_t;
+
+// 7.8.1 Macros for format specifiers
+
+#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
+
+// The fprintf macros for signed integers are:
+#define PRId8 "d"
+#define PRIi8 "i"
+#define PRIdLEAST8 "d"
+#define PRIiLEAST8 "i"
+#define PRIdFAST8 "d"
+#define PRIiFAST8 "i"
+
+#define PRId16 "hd"
+#define PRIi16 "hi"
+#define PRIdLEAST16 "hd"
+#define PRIiLEAST16 "hi"
+#define PRIdFAST16 "hd"
+#define PRIiFAST16 "hi"
+
+#define PRId32 "I32d"
+#define PRIi32 "I32i"
+#define PRIdLEAST32 "I32d"
+#define PRIiLEAST32 "I32i"
+#define PRIdFAST32 "I32d"
+#define PRIiFAST32 "I32i"
+
+#define PRId64 "I64d"
+#define PRIi64 "I64i"
+#define PRIdLEAST64 "I64d"
+#define PRIiLEAST64 "I64i"
+#define PRIdFAST64 "I64d"
+#define PRIiFAST64 "I64i"
+
+#define PRIdMAX "I64d"
+#define PRIiMAX "I64i"
+
+#define PRIdPTR "Id"
+#define PRIiPTR "Ii"
+
+// The fprintf macros for unsigned integers are:
+#define PRIo8 "o"
+#define PRIu8 "u"
+#define PRIx8 "x"
+#define PRIX8 "X"
+#define PRIoLEAST8 "o"
+#define PRIuLEAST8 "u"
+#define PRIxLEAST8 "x"
+#define PRIXLEAST8 "X"
+#define PRIoFAST8 "o"
+#define PRIuFAST8 "u"
+#define PRIxFAST8 "x"
+#define PRIXFAST8 "X"
+
+#define PRIo16 "ho"
+#define PRIu16 "hu"
+#define PRIx16 "hx"
+#define PRIX16 "hX"
+#define PRIoLEAST16 "ho"
+#define PRIuLEAST16 "hu"
+#define PRIxLEAST16 "hx"
+#define PRIXLEAST16 "hX"
+#define PRIoFAST16 "ho"
+#define PRIuFAST16 "hu"
+#define PRIxFAST16 "hx"
+#define PRIXFAST16 "hX"
+
+#define PRIo32 "I32o"
+#define PRIu32 "I32u"
+#define PRIx32 "I32x"
+#define PRIX32 "I32X"
+#define PRIoLEAST32 "I32o"
+#define PRIuLEAST32 "I32u"
+#define PRIxLEAST32 "I32x"
+#define PRIXLEAST32 "I32X"
+#define PRIoFAST32 "I32o"
+#define PRIuFAST32 "I32u"
+#define PRIxFAST32 "I32x"
+#define PRIXFAST32 "I32X"
+
+#define PRIo64 "I64o"
+#define PRIu64 "I64u"
+#define PRIx64 "I64x"
+#define PRIX64 "I64X"
+#define PRIoLEAST64 "I64o"
+#define PRIuLEAST64 "I64u"
+#define PRIxLEAST64 "I64x"
+#define PRIXLEAST64 "I64X"
+#define PRIoFAST64 "I64o"
+#define PRIuFAST64 "I64u"
+#define PRIxFAST64 "I64x"
+#define PRIXFAST64 "I64X"
+
+#define PRIoMAX "I64o"
+#define PRIuMAX "I64u"
+#define PRIxMAX "I64x"
+#define PRIXMAX "I64X"
+
+#define PRIoPTR "Io"
+#define PRIuPTR "Iu"
+#define PRIxPTR "Ix"
+#define PRIXPTR "IX"
+
+// The fscanf macros for signed integers are:
+#define SCNd8 "d"
+#define SCNi8 "i"
+#define SCNdLEAST8 "d"
+#define SCNiLEAST8 "i"
+#define SCNdFAST8 "d"
+#define SCNiFAST8 "i"
+
+#define SCNd16 "hd"
+#define SCNi16 "hi"
+#define SCNdLEAST16 "hd"
+#define SCNiLEAST16 "hi"
+#define SCNdFAST16 "hd"
+#define SCNiFAST16 "hi"
+
+#define SCNd32 "ld"
+#define SCNi32 "li"
+#define SCNdLEAST32 "ld"
+#define SCNiLEAST32 "li"
+#define SCNdFAST32 "ld"
+#define SCNiFAST32 "li"
+
+#define SCNd64 "I64d"
+#define SCNi64 "I64i"
+#define SCNdLEAST64 "I64d"
+#define SCNiLEAST64 "I64i"
+#define SCNdFAST64 "I64d"
+#define SCNiFAST64 "I64i"
+
+#define SCNdMAX "I64d"
+#define SCNiMAX "I64i"
+
+#ifdef _WIN64 // [
+# define SCNdPTR "I64d"
+# define SCNiPTR "I64i"
+#else // _WIN64 ][
+# define SCNdPTR "ld"
+# define SCNiPTR "li"
+#endif // _WIN64 ]
+
+// The fscanf macros for unsigned integers are:
+#define SCNo8 "o"
+#define SCNu8 "u"
+#define SCNx8 "x"
+#define SCNX8 "X"
+#define SCNoLEAST8 "o"
+#define SCNuLEAST8 "u"
+#define SCNxLEAST8 "x"
+#define SCNXLEAST8 "X"
+#define SCNoFAST8 "o"
+#define SCNuFAST8 "u"
+#define SCNxFAST8 "x"
+#define SCNXFAST8 "X"
+
+#define SCNo16 "ho"
+#define SCNu16 "hu"
+#define SCNx16 "hx"
+#define SCNX16 "hX"
+#define SCNoLEAST16 "ho"
+#define SCNuLEAST16 "hu"
+#define SCNxLEAST16 "hx"
+#define SCNXLEAST16 "hX"
+#define SCNoFAST16 "ho"
+#define SCNuFAST16 "hu"
+#define SCNxFAST16 "hx"
+#define SCNXFAST16 "hX"
+
+#define SCNo32 "lo"
+#define SCNu32 "lu"
+#define SCNx32 "lx"
+#define SCNX32 "lX"
+#define SCNoLEAST32 "lo"
+#define SCNuLEAST32 "lu"
+#define SCNxLEAST32 "lx"
+#define SCNXLEAST32 "lX"
+#define SCNoFAST32 "lo"
+#define SCNuFAST32 "lu"
+#define SCNxFAST32 "lx"
+#define SCNXFAST32 "lX"
+
+#define SCNo64 "I64o"
+#define SCNu64 "I64u"
+#define SCNx64 "I64x"
+#define SCNX64 "I64X"
+#define SCNoLEAST64 "I64o"
+#define SCNuLEAST64 "I64u"
+#define SCNxLEAST64 "I64x"
+#define SCNXLEAST64 "I64X"
+#define SCNoFAST64 "I64o"
+#define SCNuFAST64 "I64u"
+#define SCNxFAST64 "I64x"
+#define SCNXFAST64 "I64X"
+
+#define SCNoMAX "I64o"
+#define SCNuMAX "I64u"
+#define SCNxMAX "I64x"
+#define SCNXMAX "I64X"
+
+#ifdef _WIN64 // [
+# define SCNoPTR "I64o"
+# define SCNuPTR "I64u"
+# define SCNxPTR "I64x"
+# define SCNXPTR "I64X"
+#else // _WIN64 ][
+# define SCNoPTR "lo"
+# define SCNuPTR "lu"
+# define SCNxPTR "lx"
+# define SCNXPTR "lX"
+#endif // _WIN64 ]
+
+#endif // __STDC_FORMAT_MACROS ]
+
+// 7.8.2 Functions for greatest-width integer types
+
+// 7.8.2.1 The imaxabs function
+#define imaxabs _abs64
+
+// 7.8.2.2 The imaxdiv function
+
+// This is modified version of div() function from Microsoft's div.c found
+// in %MSVC.NET%\crt\src\div.c
+#ifdef STATIC_IMAXDIV // [
+static
+#else // STATIC_IMAXDIV ][
+_inline
+#endif // STATIC_IMAXDIV ]
+imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
+{
+ imaxdiv_t result;
+
+ result.quot = numer / denom;
+ result.rem = numer % denom;
+
+ if (numer < 0 && result.rem > 0) {
+ // did division wrong; must fix up
+ ++result.quot;
+ result.rem -= denom;
+ }
+
+ return result;
+}
+
+// 7.8.2.3 The strtoimax and strtoumax functions
+#define strtoimax _strtoi64
+#define strtoumax _strtoui64
+
+// 7.8.2.4 The wcstoimax and wcstoumax functions
+#define wcstoimax _wcstoi64
+#define wcstoumax _wcstoui64
+
+#endif // _MSC_VER >= 1800
+
+#endif // _MSC_INTTYPES_H_ ]
diff --git a/ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h b/ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h
new file mode 100644
index 00000000..3d4477b9
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/msinttypes/stdint.h
@@ -0,0 +1,300 @@
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2013 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the product nor the names of its contributors may
+// 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.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+// The above software in this distribution may have been modified by
+// THL A29 Limited ("Tencent Modifications").
+// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
+#if _MSC_VER >= 1600 // [
+#include <stdint.h>
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+#undef INT8_C
+#undef INT16_C
+#undef INT32_C
+#undef INT64_C
+#undef UINT8_C
+#undef UINT16_C
+#undef UINT32_C
+#undef UINT64_C
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C // [
+# define INTMAX_C INT64_C
+#endif // INTMAX_C ]
+#ifndef UINTMAX_C // [
+# define UINTMAX_C UINT64_C
+#endif // UINTMAX_C ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#else // ] _MSC_VER >= 1700 [
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler would give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#if defined(__cplusplus) && !defined(_M_ARM)
+extern "C" {
+#endif
+# include <wchar.h>
+#if defined(__cplusplus) && !defined(_M_ARM)
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
+// Check out Issue 9 for the details.
+#ifndef INTMAX_C // [
+# define INTMAX_C INT64_C
+#endif // INTMAX_C ]
+#ifndef UINTMAX_C // [
+# define UINTMAX_C UINT64_C
+#endif // UINTMAX_C ]
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+#endif // _MSC_VER >= 1600 ]
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h b/ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h
new file mode 100644
index 00000000..6f4667c0
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/ostreamwrapper.h
@@ -0,0 +1,81 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_OSTREAMWRAPPER_H_
+#define RAPIDJSON_OSTREAMWRAPPER_H_
+
+#include "stream.h"
+#include <iosfwd>
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
+/*!
+ The classes can be wrapped including but not limited to:
+
+ - \c std::ostringstream
+ - \c std::stringstream
+ - \c std::wpstringstream
+ - \c std::wstringstream
+ - \c std::ifstream
+ - \c std::fstream
+ - \c std::wofstream
+ - \c std::wfstream
+
+ \tparam StreamType Class derived from \c std::basic_ostream.
+*/
+
+template <typename StreamType>
+class BasicOStreamWrapper {
+public:
+ typedef typename StreamType::char_type Ch;
+ BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
+
+ void Put(Ch c) {
+ stream_.put(c);
+ }
+
+ void Flush() {
+ stream_.flush();
+ }
+
+ // Not implemented
+ char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
+ char Take() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
+ char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
+
+private:
+ BasicOStreamWrapper(const BasicOStreamWrapper&);
+ BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
+
+ StreamType& stream_;
+};
+
+typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
+typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_OSTREAMWRAPPER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/pointer.h b/ext/librethinkdbxx/src/rapidjson/pointer.h
new file mode 100644
index 00000000..0206ac1c
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/pointer.h
@@ -0,0 +1,1358 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_POINTER_H_
+#define RAPIDJSON_POINTER_H_
+
+#include "document.h"
+#include "internal/itoa.h"
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(switch-enum)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token
+
+//! Error code of parsing.
+/*! \ingroup RAPIDJSON_ERRORS
+ \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
+*/
+enum PointerParseErrorCode {
+ kPointerParseErrorNone = 0, //!< The parse is successful
+
+ kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
+ kPointerParseErrorInvalidEscape, //!< Invalid escape
+ kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
+ kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericPointer
+
+//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator.
+/*!
+ This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer"
+ (https://tools.ietf.org/html/rfc6901).
+
+ A JSON pointer is for identifying a specific value in a JSON document
+ (GenericDocument). It can simplify coding of DOM tree manipulation, because it
+ can access multiple-level depth of DOM tree with single API call.
+
+ After it parses a string representation (e.g. "/foo/0" or URI fragment
+ representation (e.g. "#/foo/0") into its internal representation (tokens),
+ it can be used to resolve a specific value in multiple documents, or sub-tree
+ of documents.
+
+ Contrary to GenericValue, Pointer can be copy constructed and copy assigned.
+ Apart from assignment, a Pointer cannot be modified after construction.
+
+ Although Pointer is very convenient, please aware that constructing Pointer
+ involves parsing and dynamic memory allocation. A special constructor with user-
+ supplied tokens eliminates these.
+
+ GenericPointer depends on GenericDocument and GenericValue.
+
+ \tparam ValueType The value type of the DOM tree. E.g. GenericValue<UTF8<> >
+ \tparam Allocator The allocator type for allocating memory for internal representation.
+
+ \note GenericPointer uses same encoding of ValueType.
+ However, Allocator of GenericPointer is independent of Allocator of Value.
+*/
+template <typename ValueType, typename Allocator = CrtAllocator>
+class GenericPointer {
+public:
+ typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value
+ typedef typename ValueType::Ch Ch; //!< Character type from Value
+
+ //! A token is the basic units of internal representation.
+ /*!
+ A JSON pointer string representation "/foo/123" is parsed to two tokens:
+ "foo" and 123. 123 will be represented in both numeric form and string form.
+ They are resolved according to the actual value type (object or array).
+
+ For token that are not numbers, or the numeric value is out of bound
+ (greater than limits of SizeType), they are only treated as string form
+ (i.e. the token's index will be equal to kPointerInvalidIndex).
+
+ This struct is public so that user can create a Pointer without parsing and
+ allocation, using a special constructor.
+ */
+ struct Token {
+ const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character.
+ SizeType length; //!< Length of the name.
+ SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex.
+ };
+
+ //!@name Constructors and destructor.
+ //@{
+
+ //! Default constructor.
+ GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+
+ //! Constructor that parses a string or URI fragment representation.
+ /*!
+ \param source A null-terminated, string or URI fragment representation of JSON pointer.
+ \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+ */
+ explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ Parse(source, internal::StrLen(source));
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Constructor that parses a string or URI fragment representation.
+ /*!
+ \param source A string or URI fragment representation of JSON pointer.
+ \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+ \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
+ */
+ explicit GenericPointer(const std::basic_string<Ch>& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ Parse(source.c_str(), source.size());
+ }
+#endif
+
+ //! Constructor that parses a string or URI fragment representation, with length of the source string.
+ /*!
+ \param source A string or URI fragment representation of JSON pointer.
+ \param length Length of source.
+ \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one.
+ \note Slightly faster than the overload without length.
+ */
+ GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ Parse(source, length);
+ }
+
+ //! Constructor with user-supplied tokens.
+ /*!
+ This constructor let user supplies const array of tokens.
+ This prevents the parsing process and eliminates allocation.
+ This is preferred for memory constrained environments.
+
+ \param tokens An constant array of tokens representing the JSON pointer.
+ \param tokenCount Number of tokens.
+
+ \b Example
+ \code
+ #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex }
+ #define INDEX(i) { #i, sizeof(#i) - 1, i }
+
+ static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) };
+ static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0]));
+ // Equivalent to static const Pointer p("/foo/123");
+
+ #undef NAME
+ #undef INDEX
+ \endcode
+ */
+ GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast<Token*>(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {}
+
+ //! Copy constructor.
+ GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {
+ *this = rhs;
+ }
+
+ //! Destructor.
+ ~GenericPointer() {
+ if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated.
+ Allocator::Free(tokens_);
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ //! Assignment operator.
+ GenericPointer& operator=(const GenericPointer& rhs) {
+ if (this != &rhs) {
+ // Do not delete ownAllcator
+ if (nameBuffer_)
+ Allocator::Free(tokens_);
+
+ tokenCount_ = rhs.tokenCount_;
+ parseErrorOffset_ = rhs.parseErrorOffset_;
+ parseErrorCode_ = rhs.parseErrorCode_;
+
+ if (rhs.nameBuffer_)
+ CopyFromRaw(rhs); // Normally parsed tokens.
+ else {
+ tokens_ = rhs.tokens_; // User supplied const tokens.
+ nameBuffer_ = 0;
+ }
+ }
+ return *this;
+ }
+
+ //@}
+
+ //!@name Append token
+ //@{
+
+ //! Append a token and return a new Pointer
+ /*!
+ \param token Token to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const Token& token, Allocator* allocator = 0) const {
+ GenericPointer r;
+ r.allocator_ = allocator;
+ Ch *p = r.CopyFromRaw(*this, 1, token.length + 1);
+ std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch));
+ r.tokens_[tokenCount_].name = p;
+ r.tokens_[tokenCount_].length = token.length;
+ r.tokens_[tokenCount_].index = token.index;
+ return r;
+ }
+
+ //! Append a name token with length, and return a new Pointer
+ /*!
+ \param name Name to be appended.
+ \param length Length of name.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const {
+ Token token = { name, length, kPointerInvalidIndex };
+ return Append(token, allocator);
+ }
+
+ //! Append a name token without length, and return a new Pointer
+ /*!
+ \param name Name (const Ch*) to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >), (GenericPointer))
+ Append(T* name, Allocator* allocator = 0) const {
+ return Append(name, StrLen(name), allocator);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Append a name token, and return a new Pointer
+ /*!
+ \param name Name to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const std::basic_string<Ch>& name, Allocator* allocator = 0) const {
+ return Append(name.c_str(), static_cast<SizeType>(name.size()), allocator);
+ }
+#endif
+
+ //! Append a index token, and return a new Pointer
+ /*!
+ \param index Index to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(SizeType index, Allocator* allocator = 0) const {
+ char buffer[21];
+ char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer);
+ SizeType length = static_cast<SizeType>(end - buffer);
+ buffer[length] = '\0';
+
+ if (sizeof(Ch) == 1) {
+ Token token = { reinterpret_cast<Ch*>(buffer), length, index };
+ return Append(token, allocator);
+ }
+ else {
+ Ch name[21];
+ for (size_t i = 0; i <= length; i++)
+ name[i] = buffer[i];
+ Token token = { name, length, index };
+ return Append(token, allocator);
+ }
+ }
+
+ //! Append a token by value, and return a new Pointer
+ /*!
+ \param token token to be appended.
+ \param allocator Allocator for the newly return Pointer.
+ \return A new Pointer with appended token.
+ */
+ GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const {
+ if (token.IsString())
+ return Append(token.GetString(), token.GetStringLength(), allocator);
+ else {
+ RAPIDJSON_ASSERT(token.IsUint64());
+ RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0));
+ return Append(static_cast<SizeType>(token.GetUint64()), allocator);
+ }
+ }
+
+ //!@name Handling Parse Error
+ //@{
+
+ //! Check whether this is a valid pointer.
+ bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; }
+
+ //! Get the parsing error offset in code unit.
+ size_t GetParseErrorOffset() const { return parseErrorOffset_; }
+
+ //! Get the parsing error code.
+ PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; }
+
+ //@}
+
+ //! Get the allocator of this pointer.
+ Allocator& GetAllocator() { return *allocator_; }
+
+ //!@name Tokens
+ //@{
+
+ //! Get the token array (const version only).
+ const Token* GetTokens() const { return tokens_; }
+
+ //! Get the number of tokens.
+ size_t GetTokenCount() const { return tokenCount_; }
+
+ //@}
+
+ //!@name Equality/inequality operators
+ //@{
+
+ //! Equality operator.
+ /*!
+ \note When any pointers are invalid, always returns false.
+ */
+ bool operator==(const GenericPointer& rhs) const {
+ if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_)
+ return false;
+
+ for (size_t i = 0; i < tokenCount_; i++) {
+ if (tokens_[i].index != rhs.tokens_[i].index ||
+ tokens_[i].length != rhs.tokens_[i].length ||
+ (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //! Inequality operator.
+ /*!
+ \note When any pointers are invalid, always returns true.
+ */
+ bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); }
+
+ //@}
+
+ //!@name Stringify
+ //@{
+
+ //! Stringify the pointer into string representation.
+ /*!
+ \tparam OutputStream Type of output stream.
+ \param os The output stream.
+ */
+ template<typename OutputStream>
+ bool Stringify(OutputStream& os) const {
+ return Stringify<false, OutputStream>(os);
+ }
+
+ //! Stringify the pointer into URI fragment representation.
+ /*!
+ \tparam OutputStream Type of output stream.
+ \param os The output stream.
+ */
+ template<typename OutputStream>
+ bool StringifyUriFragment(OutputStream& os) const {
+ return Stringify<true, OutputStream>(os);
+ }
+
+ //@}
+
+ //!@name Create value
+ //@{
+
+ //! Create a value in a subtree.
+ /*!
+ If the value is not exist, it creates all parent values and a JSON Null value.
+ So it always succeed and return the newly created or existing value.
+
+ Remind that it may change types of parents according to tokens, so it
+ potentially removes previously stored values. For example, if a document
+ was an array, and "/foo" is used to create a value, then the document
+ will be changed to an object, and all existing array elements are lost.
+
+ \param root Root value of a DOM subtree to be resolved. It can be any value other than document root.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \param alreadyExist If non-null, it stores whether the resolved value is already exist.
+ \return The resolved newly created (a JSON Null value), or already exists value.
+ */
+ ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const {
+ RAPIDJSON_ASSERT(IsValid());
+ ValueType* v = &root;
+ bool exist = true;
+ for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+ if (v->IsArray() && t->name[0] == '-' && t->length == 1) {
+ v->PushBack(ValueType().Move(), allocator);
+ v = &((*v)[v->Size() - 1]);
+ exist = false;
+ }
+ else {
+ if (t->index == kPointerInvalidIndex) { // must be object name
+ if (!v->IsObject())
+ v->SetObject(); // Change to Object
+ }
+ else { // object name or array index
+ if (!v->IsArray() && !v->IsObject())
+ v->SetArray(); // Change to Array
+ }
+
+ if (v->IsArray()) {
+ if (t->index >= v->Size()) {
+ v->Reserve(t->index + 1, allocator);
+ while (t->index >= v->Size())
+ v->PushBack(ValueType().Move(), allocator);
+ exist = false;
+ }
+ v = &((*v)[t->index]);
+ }
+ else {
+ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+ if (m == v->MemberEnd()) {
+ v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator);
+ v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end
+ exist = false;
+ }
+ else
+ v = &m->value;
+ }
+ }
+ }
+
+ if (alreadyExist)
+ *alreadyExist = exist;
+
+ return *v;
+ }
+
+ //! Creates a value in a document.
+ /*!
+ \param document A document to be resolved.
+ \param alreadyExist If non-null, it stores whether the resolved value is already exist.
+ \return The resolved newly created, or already exists value.
+ */
+ template <typename stackAllocator>
+ ValueType& Create(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, bool* alreadyExist = 0) const {
+ return Create(document, document.GetAllocator(), alreadyExist);
+ }
+
+ //@}
+
+ //!@name Query value
+ //@{
+
+ //! Query a value in a subtree.
+ /*!
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
+ \return Pointer to the value if it can be resolved. Otherwise null.
+
+ \note
+ There are only 3 situations when a value cannot be resolved:
+ 1. A value in the path is not an array nor object.
+ 2. An object value does not contain the token.
+ 3. A token is out of range of an array value.
+
+ Use unresolvedTokenIndex to retrieve the token index.
+ */
+ ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const {
+ RAPIDJSON_ASSERT(IsValid());
+ ValueType* v = &root;
+ for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+ switch (v->GetType()) {
+ case kObjectType:
+ {
+ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+ if (m == v->MemberEnd())
+ break;
+ v = &m->value;
+ }
+ continue;
+ case kArrayType:
+ if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+ break;
+ v = &((*v)[t->index]);
+ continue;
+ default:
+ break;
+ }
+
+ // Error: unresolved token
+ if (unresolvedTokenIndex)
+ *unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
+ return 0;
+ }
+ return v;
+ }
+
+ //! Query a const value in a const subtree.
+ /*!
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \return Pointer to the value if it can be resolved. Otherwise null.
+ */
+ const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const {
+ return Get(const_cast<ValueType&>(root), unresolvedTokenIndex);
+ }
+
+ //@}
+
+ //!@name Query a value with default
+ //@{
+
+ //! Query a value in a subtree with default value.
+ /*!
+ Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value.
+ So that this function always succeed.
+
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param defaultValue Default value to be cloned if the value was not exists.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \see Create()
+ */
+ ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const {
+ bool alreadyExist;
+ Value& v = Create(root, allocator, &alreadyExist);
+ return alreadyExist ? v : v.CopyFrom(defaultValue, allocator);
+ }
+
+ //! Query a value in a subtree with default null-terminated string.
+ ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const {
+ bool alreadyExist;
+ Value& v = Create(root, allocator, &alreadyExist);
+ return alreadyExist ? v : v.SetString(defaultValue, allocator);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Query a value in a subtree with default std::basic_string.
+ ValueType& GetWithDefault(ValueType& root, const std::basic_string<Ch>& defaultValue, typename ValueType::AllocatorType& allocator) const {
+ bool alreadyExist;
+ Value& v = Create(root, allocator, &alreadyExist);
+ return alreadyExist ? v : v.SetString(defaultValue, allocator);
+ }
+#endif
+
+ //! Query a value in a subtree with default primitive value.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const {
+ return GetWithDefault(root, ValueType(defaultValue).Move(), allocator);
+ }
+
+ //! Query a value in a document with default value.
+ template <typename stackAllocator>
+ ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+
+ //! Query a value in a document with default null-terminated string.
+ template <typename stackAllocator>
+ ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Query a value in a document with default std::basic_string.
+ template <typename stackAllocator>
+ ValueType& GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+#endif
+
+ //! Query a value in a document with default primitive value.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T, typename stackAllocator>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ GetWithDefault(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T defaultValue) const {
+ return GetWithDefault(document, defaultValue, document.GetAllocator());
+ }
+
+ //@}
+
+ //!@name Set a value
+ //@{
+
+ //! Set a value in a subtree, with move semantics.
+ /*!
+ It creates all parents if they are not exist or types are different to the tokens.
+ So this function always succeeds but potentially remove existing values.
+
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param value Value to be set.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \see Create()
+ */
+ ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = value;
+ }
+
+ //! Set a value in a subtree, with copy semantics.
+ ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator).CopyFrom(value, allocator);
+ }
+
+ //! Set a null-terminated string in a subtree.
+ ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = ValueType(value, allocator).Move();
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Set a std::basic_string in a subtree.
+ ValueType& Set(ValueType& root, const std::basic_string<Ch>& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = ValueType(value, allocator).Move();
+ }
+#endif
+
+ //! Set a primitive value in a subtree.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator) = ValueType(value).Move();
+ }
+
+ //! Set a value in a document, with move semantics.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const {
+ return Create(document) = value;
+ }
+
+ //! Set a value in a document, with copy semantics.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const ValueType& value) const {
+ return Create(document).CopyFrom(value, document.GetAllocator());
+ }
+
+ //! Set a null-terminated string in a document.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const Ch* value) const {
+ return Create(document) = ValueType(value, document.GetAllocator()).Move();
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ //! Sets a std::basic_string in a document.
+ template <typename stackAllocator>
+ ValueType& Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, const std::basic_string<Ch>& value) const {
+ return Create(document) = ValueType(value, document.GetAllocator()).Move();
+ }
+#endif
+
+ //! Set a primitive value in a document.
+ /*!
+ \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool
+ */
+ template <typename T, typename stackAllocator>
+ RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (ValueType&))
+ Set(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, T value) const {
+ return Create(document) = value;
+ }
+
+ //@}
+
+ //!@name Swap a value
+ //@{
+
+ //! Swap a value with a value in a subtree.
+ /*!
+ It creates all parents if they are not exist or types are different to the tokens.
+ So this function always succeeds but potentially remove existing values.
+
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \param value Value to be swapped.
+ \param allocator Allocator for creating the values if the specified value or its parents are not exist.
+ \see Create()
+ */
+ ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const {
+ return Create(root, allocator).Swap(value);
+ }
+
+ //! Swap a value with a value in a document.
+ template <typename stackAllocator>
+ ValueType& Swap(GenericDocument<EncodingType, typename ValueType::AllocatorType, stackAllocator>& document, ValueType& value) const {
+ return Create(document).Swap(value);
+ }
+
+ //@}
+
+ //! Erase a value in a subtree.
+ /*!
+ \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
+ \return Whether the resolved value is found and erased.
+
+ \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false.
+ */
+ bool Erase(ValueType& root) const {
+ RAPIDJSON_ASSERT(IsValid());
+ if (tokenCount_ == 0) // Cannot erase the root
+ return false;
+
+ ValueType* v = &root;
+ const Token* last = tokens_ + (tokenCount_ - 1);
+ for (const Token *t = tokens_; t != last; ++t) {
+ switch (v->GetType()) {
+ case kObjectType:
+ {
+ typename ValueType::MemberIterator m = v->FindMember(GenericStringRef<Ch>(t->name, t->length));
+ if (m == v->MemberEnd())
+ return false;
+ v = &m->value;
+ }
+ break;
+ case kArrayType:
+ if (t->index == kPointerInvalidIndex || t->index >= v->Size())
+ return false;
+ v = &((*v)[t->index]);
+ break;
+ default:
+ return false;
+ }
+ }
+
+ switch (v->GetType()) {
+ case kObjectType:
+ return v->EraseMember(GenericStringRef<Ch>(last->name, last->length));
+ case kArrayType:
+ if (last->index == kPointerInvalidIndex || last->index >= v->Size())
+ return false;
+ v->Erase(v->Begin() + last->index);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+private:
+ //! Clone the content from rhs to this.
+ /*!
+ \param rhs Source pointer.
+ \param extraToken Extra tokens to be allocated.
+ \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated.
+ \return Start of non-occupied name buffer, for storing extra names.
+ */
+ Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) {
+ if (!allocator_) // allocator is independently owned.
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens
+ for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t)
+ nameBufferSize += t->length;
+
+ tokenCount_ = rhs.tokenCount_ + extraToken;
+ tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch)));
+ nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
+ if (rhs.tokenCount_ > 0) {
+ std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token));
+ }
+ if (nameBufferSize > 0) {
+ std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch));
+ }
+
+ // Adjust pointers to name buffer
+ std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_;
+ for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t)
+ t->name += diff;
+
+ return nameBuffer_ + nameBufferSize;
+ }
+
+ //! Check whether a character should be percent-encoded.
+ /*!
+ According to RFC 3986 2.3 Unreserved Characters.
+ \param c The character (code unit) to be tested.
+ */
+ bool NeedPercentEncode(Ch c) const {
+ return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~');
+ }
+
+ //! Parse a JSON String or its URI fragment representation into tokens.
+#ifndef __clang__ // -Wdocumentation
+ /*!
+ \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated.
+ \param length Length of the source string.
+ \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped.
+ */
+#endif
+ void Parse(const Ch* source, size_t length) {
+ RAPIDJSON_ASSERT(source != NULL);
+ RAPIDJSON_ASSERT(nameBuffer_ == 0);
+ RAPIDJSON_ASSERT(tokens_ == 0);
+
+ // Create own allocator if user did not supply.
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ // Count number of '/' as tokenCount
+ tokenCount_ = 0;
+ for (const Ch* s = source; s != source + length; s++)
+ if (*s == '/')
+ tokenCount_++;
+
+ Token* token = tokens_ = static_cast<Token *>(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch)));
+ Ch* name = nameBuffer_ = reinterpret_cast<Ch *>(tokens_ + tokenCount_);
+ size_t i = 0;
+
+ // Detect if it is a URI fragment
+ bool uriFragment = false;
+ if (source[i] == '#') {
+ uriFragment = true;
+ i++;
+ }
+
+ if (i != length && source[i] != '/') {
+ parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus;
+ goto error;
+ }
+
+ while (i < length) {
+ RAPIDJSON_ASSERT(source[i] == '/');
+ i++; // consumes '/'
+
+ token->name = name;
+ bool isNumber = true;
+
+ while (i < length && source[i] != '/') {
+ Ch c = source[i];
+ if (uriFragment) {
+ // Decoding percent-encoding for URI fragment
+ if (c == '%') {
+ PercentDecodeStream is(&source[i], source + length);
+ GenericInsituStringStream<EncodingType> os(name);
+ Ch* begin = os.PutBegin();
+ if (!Transcoder<UTF8<>, EncodingType>().Validate(is, os) || !is.IsValid()) {
+ parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding;
+ goto error;
+ }
+ size_t len = os.PutEnd(begin);
+ i += is.Tell() - 1;
+ if (len == 1)
+ c = *name;
+ else {
+ name += len;
+ isNumber = false;
+ i++;
+ continue;
+ }
+ }
+ else if (NeedPercentEncode(c)) {
+ parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode;
+ goto error;
+ }
+ }
+
+ i++;
+
+ // Escaping "~0" -> '~', "~1" -> '/'
+ if (c == '~') {
+ if (i < length) {
+ c = source[i];
+ if (c == '0') c = '~';
+ else if (c == '1') c = '/';
+ else {
+ parseErrorCode_ = kPointerParseErrorInvalidEscape;
+ goto error;
+ }
+ i++;
+ }
+ else {
+ parseErrorCode_ = kPointerParseErrorInvalidEscape;
+ goto error;
+ }
+ }
+
+ // First check for index: all of characters are digit
+ if (c < '0' || c > '9')
+ isNumber = false;
+
+ *name++ = c;
+ }
+ token->length = static_cast<SizeType>(name - token->name);
+ if (token->length == 0)
+ isNumber = false;
+ *name++ = '\0'; // Null terminator
+
+ // Second check for index: more than one digit cannot have leading zero
+ if (isNumber && token->length > 1 && token->name[0] == '0')
+ isNumber = false;
+
+ // String to SizeType conversion
+ SizeType n = 0;
+ if (isNumber) {
+ for (size_t j = 0; j < token->length; j++) {
+ SizeType m = n * 10 + static_cast<SizeType>(token->name[j] - '0');
+ if (m < n) { // overflow detection
+ isNumber = false;
+ break;
+ }
+ n = m;
+ }
+ }
+
+ token->index = isNumber ? n : kPointerInvalidIndex;
+ token++;
+ }
+
+ RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer
+ parseErrorCode_ = kPointerParseErrorNone;
+ return;
+
+ error:
+ Allocator::Free(tokens_);
+ nameBuffer_ = 0;
+ tokens_ = 0;
+ tokenCount_ = 0;
+ parseErrorOffset_ = i;
+ return;
+ }
+
+ //! Stringify to string or URI fragment representation.
+ /*!
+ \tparam uriFragment True for stringifying to URI fragment representation. False for string representation.
+ \tparam OutputStream type of output stream.
+ \param os The output stream.
+ */
+ template<bool uriFragment, typename OutputStream>
+ bool Stringify(OutputStream& os) const {
+ RAPIDJSON_ASSERT(IsValid());
+
+ if (uriFragment)
+ os.Put('#');
+
+ for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
+ os.Put('/');
+ for (size_t j = 0; j < t->length; j++) {
+ Ch c = t->name[j];
+ if (c == '~') {
+ os.Put('~');
+ os.Put('0');
+ }
+ else if (c == '/') {
+ os.Put('~');
+ os.Put('1');
+ }
+ else if (uriFragment && NeedPercentEncode(c)) {
+ // Transcode to UTF8 sequence
+ GenericStringStream<typename ValueType::EncodingType> source(&t->name[j]);
+ PercentEncodeStream<OutputStream> target(os);
+ if (!Transcoder<EncodingType, UTF8<> >().Validate(source, target))
+ return false;
+ j += source.Tell() - 1;
+ }
+ else
+ os.Put(c);
+ }
+ }
+ return true;
+ }
+
+ //! A helper stream for decoding a percent-encoded sequence into code unit.
+ /*!
+ This stream decodes %XY triplet into code unit (0-255).
+ If it encounters invalid characters, it sets output code unit as 0 and
+ mark invalid, and to be checked by IsValid().
+ */
+ class PercentDecodeStream {
+ public:
+ typedef typename ValueType::Ch Ch;
+
+ //! Constructor
+ /*!
+ \param source Start of the stream
+ \param end Past-the-end of the stream.
+ */
+ PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {}
+
+ Ch Take() {
+ if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet
+ valid_ = false;
+ return 0;
+ }
+ src_++;
+ Ch c = 0;
+ for (int j = 0; j < 2; j++) {
+ c = static_cast<Ch>(c << 4);
+ Ch h = *src_;
+ if (h >= '0' && h <= '9') c = static_cast<Ch>(c + h - '0');
+ else if (h >= 'A' && h <= 'F') c = static_cast<Ch>(c + h - 'A' + 10);
+ else if (h >= 'a' && h <= 'f') c = static_cast<Ch>(c + h - 'a' + 10);
+ else {
+ valid_ = false;
+ return 0;
+ }
+ src_++;
+ }
+ return c;
+ }
+
+ size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+ bool IsValid() const { return valid_; }
+
+ private:
+ const Ch* src_; //!< Current read position.
+ const Ch* head_; //!< Original head of the string.
+ const Ch* end_; //!< Past-the-end position.
+ bool valid_; //!< Whether the parsing is valid.
+ };
+
+ //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence.
+ template <typename OutputStream>
+ class PercentEncodeStream {
+ public:
+ PercentEncodeStream(OutputStream& os) : os_(os) {}
+ void Put(char c) { // UTF-8 must be byte
+ unsigned char u = static_cast<unsigned char>(c);
+ static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ os_.Put('%');
+ os_.Put(hexDigits[u >> 4]);
+ os_.Put(hexDigits[u & 15]);
+ }
+ private:
+ OutputStream& os_;
+ };
+
+ Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
+ Allocator* ownAllocator_; //!< Allocator owned by this Pointer.
+ Ch* nameBuffer_; //!< A buffer containing all names in tokens.
+ Token* tokens_; //!< A list of tokens.
+ size_t tokenCount_; //!< Number of tokens in tokens_.
+ size_t parseErrorOffset_; //!< Offset in code unit when parsing fail.
+ PointerParseErrorCode parseErrorCode_; //!< Parsing error code.
+};
+
+//! GenericPointer for Value (UTF-8, default allocator).
+typedef GenericPointer<Value> Pointer;
+
+//!@name Helper functions for GenericPointer
+//@{
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::AllocatorType& a) {
+ return pointer.Create(root, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Create(root, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer) {
+ return pointer.Create(document);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Create(document);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType* GetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+ return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T>
+const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer<typename T::ValueType>& pointer, size_t* unresolvedTokenIndex = 0) {
+ return pointer.Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+template <typename T, typename CharType, size_t N>
+const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Get(root, unresolvedTokenIndex);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+#endif
+
+template <typename T, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+GetValueByPointerWithDefault(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 defaultValue, typename T::AllocatorType& a) {
+ return pointer.GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+#endif
+
+template <typename T, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).GetWithDefault(root, defaultValue, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+#endif
+
+template <typename DocumentType, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+GetValueByPointerWithDefault(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 defaultValue) {
+ return pointer.GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+#endif
+
+template <typename DocumentType, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).GetWithDefault(document, defaultValue);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const typename T::Ch* value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T>
+typename T::ValueType& SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+#endif
+
+template <typename T, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+SetValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, T2 value, typename T::AllocatorType& a) {
+ return pointer.Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string<typename T::Ch>& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+#endif
+
+template <typename T, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename T::ValueType&))
+SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Set(root, value, a);
+}
+
+// No allocator parameter
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) {
+ return pointer.Set(document, value);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::ValueType& value) {
+ return pointer.Set(document, value);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const typename DocumentType::Ch* value) {
+ return pointer.Set(document, value);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, const std::basic_string<typename DocumentType::Ch>& value) {
+ return pointer.Set(document, value);
+}
+#endif
+
+template <typename DocumentType, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+SetValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, T2 value) {
+ return pointer.Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+#if RAPIDJSON_HAS_STDSTRING
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string<typename DocumentType::Ch>& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+#endif
+
+template <typename DocumentType, typename CharType, size_t N, typename T2>
+RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T2>, internal::IsGenericValue<T2> >), (typename DocumentType::ValueType&))
+SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Set(document, value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer, typename T::ValueType& value, typename T::AllocatorType& a) {
+ return pointer.Swap(root, value, a);
+}
+
+template <typename T, typename CharType, size_t N>
+typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Swap(root, value, a);
+}
+
+template <typename DocumentType>
+typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer<typename DocumentType::ValueType>& pointer, typename DocumentType::ValueType& value) {
+ return pointer.Swap(document, value);
+}
+
+template <typename DocumentType, typename CharType, size_t N>
+typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) {
+ return GenericPointer<typename DocumentType::ValueType>(source, N - 1).Swap(document, value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+bool EraseValueByPointer(T& root, const GenericPointer<typename T::ValueType>& pointer) {
+ return pointer.Erase(root);
+}
+
+template <typename T, typename CharType, size_t N>
+bool EraseValueByPointer(T& root, const CharType(&source)[N]) {
+ return GenericPointer<typename T::ValueType>(source, N - 1).Erase(root);
+}
+
+//@}
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_POINTER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/prettywriter.h b/ext/librethinkdbxx/src/rapidjson/prettywriter.h
new file mode 100644
index 00000000..75dc474f
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/prettywriter.h
@@ -0,0 +1,249 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_PRETTYWRITER_H_
+#define RAPIDJSON_PRETTYWRITER_H_
+
+#include "writer.h"
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Combination of PrettyWriter format flags.
+/*! \see PrettyWriter::SetFormatOptions
+ */
+enum PrettyFormatOptions {
+ kFormatDefault = 0, //!< Default pretty formatting.
+ kFormatSingleLineArray = 1 //!< Format arrays on a single line.
+};
+
+//! Writer with indentation and spacing.
+/*!
+ \tparam OutputStream Type of ouptut os.
+ \tparam SourceEncoding Encoding of source string.
+ \tparam TargetEncoding Encoding of output stream.
+ \tparam StackAllocator Type of allocator for allocating memory of stack.
+*/
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
+public:
+ typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base;
+ typedef typename Base::Ch Ch;
+
+ //! Constructor
+ /*! \param os Output stream.
+ \param allocator User supplied allocator. If it is null, it will create a private one.
+ \param levelDepth Initial capacity of stack.
+ */
+ explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
+ Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
+
+
+ explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
+ Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
+
+ //! Set custom indentation.
+ /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
+ \param indentCharCount Number of indent characters for each indentation level.
+ \note The default indentation is 4 spaces.
+ */
+ PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
+ RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
+ indentChar_ = indentChar;
+ indentCharCount_ = indentCharCount;
+ return *this;
+ }
+
+ //! Set pretty writer formatting options.
+ /*! \param options Formatting options.
+ */
+ PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
+ formatOptions_ = options;
+ return *this;
+ }
+
+ /*! @name Implementation of Handler
+ \see Handler
+ */
+ //@{
+
+ bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
+ bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
+ bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
+ bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
+ bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
+ bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
+ bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
+
+ bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ PrettyPrefix(kNumberType);
+ return Base::WriteString(str, length);
+ }
+
+ bool String(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ PrettyPrefix(kStringType);
+ return Base::WriteString(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool String(const std::basic_string<Ch>& str) {
+ return String(str.data(), SizeType(str.size()));
+ }
+#endif
+
+ bool StartObject() {
+ PrettyPrefix(kObjectType);
+ new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
+ return Base::WriteStartObject();
+ }
+
+ bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
+
+ bool EndObject(SizeType memberCount = 0) {
+ (void)memberCount;
+ RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
+ RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
+ bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
+
+ if (!empty) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
+ bool ret = Base::WriteEndObject();
+ (void)ret;
+ RAPIDJSON_ASSERT(ret == true);
+ if (Base::level_stack_.Empty()) // end of json text
+ Base::os_->Flush();
+ return true;
+ }
+
+ bool StartArray() {
+ PrettyPrefix(kArrayType);
+ new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
+ return Base::WriteStartArray();
+ }
+
+ bool EndArray(SizeType memberCount = 0) {
+ (void)memberCount;
+ RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
+ RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
+ bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
+
+ if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
+ bool ret = Base::WriteEndArray();
+ (void)ret;
+ RAPIDJSON_ASSERT(ret == true);
+ if (Base::level_stack_.Empty()) // end of json text
+ Base::os_->Flush();
+ return true;
+ }
+
+ //@}
+
+ /*! @name Convenience extensions */
+ //@{
+
+ //! Simpler but slower overload.
+ bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
+ bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
+
+ //@}
+
+ //! Write a raw JSON value.
+ /*!
+ For user to write a stringified JSON as a value.
+
+ \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+ \param length Length of the json.
+ \param type Type of the root of json.
+ \note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
+ */
+ bool RawValue(const Ch* json, size_t length, Type type) { PrettyPrefix(type); return Base::WriteRawValue(json, length); }
+
+protected:
+ void PrettyPrefix(Type type) {
+ (void)type;
+ if (Base::level_stack_.GetSize() != 0) { // this value is not at root
+ typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
+
+ if (level->inArray) {
+ if (level->valueCount > 0) {
+ Base::os_->Put(','); // add comma if it is not the first element in array
+ if (formatOptions_ & kFormatSingleLineArray)
+ Base::os_->Put(' ');
+ }
+
+ if (!(formatOptions_ & kFormatSingleLineArray)) {
+ Base::os_->Put('\n');
+ WriteIndent();
+ }
+ }
+ else { // in object
+ if (level->valueCount > 0) {
+ if (level->valueCount % 2 == 0) {
+ Base::os_->Put(',');
+ Base::os_->Put('\n');
+ }
+ else {
+ Base::os_->Put(':');
+ Base::os_->Put(' ');
+ }
+ }
+ else
+ Base::os_->Put('\n');
+
+ if (level->valueCount % 2 == 0)
+ WriteIndent();
+ }
+ if (!level->inArray && level->valueCount % 2 == 0)
+ RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
+ level->valueCount++;
+ }
+ else {
+ RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
+ Base::hasRoot_ = true;
+ }
+ }
+
+ void WriteIndent() {
+ size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
+ PutN(*Base::os_, static_cast<typename TargetEncoding::Ch>(indentChar_), count);
+ }
+
+ Ch indentChar_;
+ unsigned indentCharCount_;
+ PrettyFormatOptions formatOptions_;
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ PrettyWriter(const PrettyWriter&);
+ PrettyWriter& operator=(const PrettyWriter&);
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/rapidjson.h b/ext/librethinkdbxx/src/rapidjson/rapidjson.h
new file mode 100644
index 00000000..d666f202
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/rapidjson.h
@@ -0,0 +1,615 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_RAPIDJSON_H_
+#define RAPIDJSON_RAPIDJSON_H_
+
+/*!\file rapidjson.h
+ \brief common definitions and configuration
+
+ \see RAPIDJSON_CONFIG
+ */
+
+/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
+ \brief Configuration macros for library features
+
+ Some RapidJSON features are configurable to adapt the library to a wide
+ variety of platforms, environments and usage scenarios. Most of the
+ features can be configured in terms of overriden or predefined
+ preprocessor macros at compile-time.
+
+ Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
+
+ \note These macros should be given on the compiler command-line
+ (where applicable) to avoid inconsistent values when compiling
+ different translation units of a single application.
+ */
+
+#include <cstdlib> // malloc(), realloc(), free(), size_t
+#include <cstring> // memset(), memcpy(), memmove(), memcmp()
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_VERSION_STRING
+//
+// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt.
+//
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+// token stringification
+#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
+#define RAPIDJSON_DO_STRINGIFY(x) #x
+//!@endcond
+
+/*! \def RAPIDJSON_MAJOR_VERSION
+ \ingroup RAPIDJSON_CONFIG
+ \brief Major version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_MINOR_VERSION
+ \ingroup RAPIDJSON_CONFIG
+ \brief Minor version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_PATCH_VERSION
+ \ingroup RAPIDJSON_CONFIG
+ \brief Patch version of RapidJSON in integer.
+*/
+/*! \def RAPIDJSON_VERSION_STRING
+ \ingroup RAPIDJSON_CONFIG
+ \brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
+*/
+#define RAPIDJSON_MAJOR_VERSION 1
+#define RAPIDJSON_MINOR_VERSION 0
+#define RAPIDJSON_PATCH_VERSION 2
+#define RAPIDJSON_VERSION_STRING \
+ RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION)
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NAMESPACE_(BEGIN|END)
+/*! \def RAPIDJSON_NAMESPACE
+ \ingroup RAPIDJSON_CONFIG
+ \brief provide custom rapidjson namespace
+
+ In order to avoid symbol clashes and/or "One Definition Rule" errors
+ between multiple inclusions of (different versions of) RapidJSON in
+ a single binary, users can customize the name of the main RapidJSON
+ namespace.
+
+ In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
+ to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
+ levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
+ RAPIDJSON_NAMESPACE_END need to be defined as well:
+
+ \code
+ // in some .cpp file
+ #define RAPIDJSON_NAMESPACE my::rapidjson
+ #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
+ #define RAPIDJSON_NAMESPACE_END } }
+ #include "rapidjson/..."
+ \endcode
+
+ \see rapidjson
+ */
+/*! \def RAPIDJSON_NAMESPACE_BEGIN
+ \ingroup RAPIDJSON_CONFIG
+ \brief provide custom rapidjson namespace (opening expression)
+ \see RAPIDJSON_NAMESPACE
+*/
+/*! \def RAPIDJSON_NAMESPACE_END
+ \ingroup RAPIDJSON_CONFIG
+ \brief provide custom rapidjson namespace (closing expression)
+ \see RAPIDJSON_NAMESPACE
+*/
+#ifndef RAPIDJSON_NAMESPACE
+#define RAPIDJSON_NAMESPACE rapidjson
+#endif
+#ifndef RAPIDJSON_NAMESPACE_BEGIN
+#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
+#endif
+#ifndef RAPIDJSON_NAMESPACE_END
+#define RAPIDJSON_NAMESPACE_END }
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_HAS_STDSTRING
+
+#ifndef RAPIDJSON_HAS_STDSTRING
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
+#else
+#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
+#endif
+/*! \def RAPIDJSON_HAS_STDSTRING
+ \ingroup RAPIDJSON_CONFIG
+ \brief Enable RapidJSON support for \c std::string
+
+ By defining this preprocessor symbol to \c 1, several convenience functions for using
+ \ref rapidjson::GenericValue with \c std::string are enabled, especially
+ for construction and comparison.
+
+ \hideinitializer
+*/
+#endif // !defined(RAPIDJSON_HAS_STDSTRING)
+
+#if RAPIDJSON_HAS_STDSTRING
+#include <string>
+#endif // RAPIDJSON_HAS_STDSTRING
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NO_INT64DEFINE
+
+/*! \def RAPIDJSON_NO_INT64DEFINE
+ \ingroup RAPIDJSON_CONFIG
+ \brief Use external 64-bit integer types.
+
+ RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
+ to be available at global scope.
+
+ If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
+ prevent RapidJSON from defining its own types.
+*/
+#ifndef RAPIDJSON_NO_INT64DEFINE
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
+#include "msinttypes/stdint.h"
+#include "msinttypes/inttypes.h"
+#else
+// Other compilers should have this.
+#include <stdint.h>
+#include <inttypes.h>
+#endif
+//!@endcond
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_NO_INT64DEFINE
+#endif
+#endif // RAPIDJSON_NO_INT64TYPEDEF
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_FORCEINLINE
+
+#ifndef RAPIDJSON_FORCEINLINE
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#if defined(_MSC_VER) && defined(NDEBUG)
+#define RAPIDJSON_FORCEINLINE __forceinline
+#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
+#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
+#else
+#define RAPIDJSON_FORCEINLINE
+#endif
+//!@endcond
+#endif // RAPIDJSON_FORCEINLINE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ENDIAN
+#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
+#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
+
+//! Endianness of the machine.
+/*!
+ \def RAPIDJSON_ENDIAN
+ \ingroup RAPIDJSON_CONFIG
+
+ GCC 4.6 provided macro for detecting endianness of the target machine. But other
+ compilers may not have this. User can define RAPIDJSON_ENDIAN to either
+ \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
+
+ Default detection implemented with reference to
+ \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
+ \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
+*/
+#ifndef RAPIDJSON_ENDIAN
+// Detect with GCC 4.6's macro
+# ifdef __BYTE_ORDER__
+# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+# else
+# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+# endif // __BYTE_ORDER__
+// Detect with GLIBC's endian.h
+# elif defined(__GLIBC__)
+# include <endian.h>
+# if (__BYTE_ORDER == __LITTLE_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif (__BYTE_ORDER == __BIG_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+# else
+# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+# endif // __GLIBC__
+// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
+# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+// Detect with architecture macros
+# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
+# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
+# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif defined(_MSC_VER) && defined(_M_ARM)
+# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
+# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
+# define RAPIDJSON_ENDIAN
+# else
+# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
+# endif
+#endif // RAPIDJSON_ENDIAN
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_64BIT
+
+//! Whether using 64-bit architecture
+#ifndef RAPIDJSON_64BIT
+#if defined(__LP64__) || defined(_WIN64) || defined(__EMSCRIPTEN__)
+#define RAPIDJSON_64BIT 1
+#else
+#define RAPIDJSON_64BIT 0
+#endif
+#endif // RAPIDJSON_64BIT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ALIGN
+
+//! Data alignment of the machine.
+/*! \ingroup RAPIDJSON_CONFIG
+ \param x pointer to align
+
+ Some machines require strict data alignment. Currently the default uses 4 bytes
+ alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms.
+ User can customize by defining the RAPIDJSON_ALIGN function macro.
+*/
+#ifndef RAPIDJSON_ALIGN
+#if RAPIDJSON_64BIT == 1
+#define RAPIDJSON_ALIGN(x) (((x) + static_cast<uint64_t>(7u)) & ~static_cast<uint64_t>(7u))
+#else
+#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_UINT64_C2
+
+//! Construct a 64-bit literal by a pair of 32-bit integer.
+/*!
+ 64-bit literal with or without ULL suffix is prone to compiler warnings.
+ UINT64_C() is C macro which cause compilation problems.
+ Use this macro to define 64-bit constants by a pair of 32-bit integer.
+*/
+#ifndef RAPIDJSON_UINT64_C2
+#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+//! Use only lower 48-bit address for some pointers.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+
+ This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
+ The higher 16-bit can be used for storing other data.
+ \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
+*/
+#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
+#else
+#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
+#endif
+#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
+
+#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
+#if RAPIDJSON_64BIT != 1
+#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
+#endif
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
+#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
+#else
+#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
+#define RAPIDJSON_GETPOINTER(type, p) (p)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD
+
+/*! \def RAPIDJSON_SIMD
+ \ingroup RAPIDJSON_CONFIG
+ \brief Enable SSE2/SSE4.2 optimization.
+
+ RapidJSON supports optimized implementations for some parsing operations
+ based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible
+ processors.
+
+ To enable these optimizations, two different symbols can be defined;
+ \code
+ // Enable SSE2 optimization.
+ #define RAPIDJSON_SSE2
+
+ // Enable SSE4.2 optimization.
+ #define RAPIDJSON_SSE42
+ \endcode
+
+ \c RAPIDJSON_SSE42 takes precedence, if both are defined.
+
+ If any of these symbols is defined, RapidJSON defines the macro
+ \c RAPIDJSON_SIMD to indicate the availability of the optimized code.
+*/
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
+ || defined(RAPIDJSON_DOXYGEN_RUNNING)
+#define RAPIDJSON_SIMD
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_NO_SIZETYPEDEFINE
+
+#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
+/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-provided \c SizeType definition.
+
+ In order to avoid using 32-bit size types for indexing strings and arrays,
+ define this preprocessor symbol and provide the type rapidjson::SizeType
+ before including RapidJSON:
+ \code
+ #define RAPIDJSON_NO_SIZETYPEDEFINE
+ namespace rapidjson { typedef ::std::size_t SizeType; }
+ #include "rapidjson/..."
+ \endcode
+
+ \see rapidjson::SizeType
+*/
+#ifdef RAPIDJSON_DOXYGEN_RUNNING
+#define RAPIDJSON_NO_SIZETYPEDEFINE
+#endif
+RAPIDJSON_NAMESPACE_BEGIN
+//! Size type (for string lengths, array sizes, etc.)
+/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
+ instead of using \c size_t. Users may override the SizeType by defining
+ \ref RAPIDJSON_NO_SIZETYPEDEFINE.
+*/
+typedef unsigned SizeType;
+RAPIDJSON_NAMESPACE_END
+#endif
+
+// always import std::size_t to rapidjson namespace
+RAPIDJSON_NAMESPACE_BEGIN
+using std::size_t;
+RAPIDJSON_NAMESPACE_END
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_ASSERT
+
+//! Assertion.
+/*! \ingroup RAPIDJSON_CONFIG
+ By default, rapidjson uses C \c assert() for internal assertions.
+ User can override it by defining RAPIDJSON_ASSERT(x) macro.
+
+ \note Parsing errors are handled and can be customized by the
+ \ref RAPIDJSON_ERRORS APIs.
+*/
+#ifndef RAPIDJSON_ASSERT
+#include <cassert>
+#define RAPIDJSON_ASSERT(x) assert(x)
+#endif // RAPIDJSON_ASSERT
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_STATIC_ASSERT
+
+// Adopt from boost
+#ifndef RAPIDJSON_STATIC_ASSERT
+#ifndef __clang__
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#endif
+RAPIDJSON_NAMESPACE_BEGIN
+template <bool x> struct STATIC_ASSERTION_FAILURE;
+template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
+template<int x> struct StaticAssertTest {};
+RAPIDJSON_NAMESPACE_END
+
+#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
+#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
+#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
+
+#if defined(__GNUC__)
+#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
+#else
+#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
+#endif
+#ifndef __clang__
+//!@endcond
+#endif
+
+/*! \def RAPIDJSON_STATIC_ASSERT
+ \brief (Internal) macro to check for conditions at compile-time
+ \param x compile-time condition
+ \hideinitializer
+ */
+#define RAPIDJSON_STATIC_ASSERT(x) \
+ typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
+ sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
+ RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
+
+//! Compiler branching hint for expression with high probability to be true.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+ \param x Boolean expression likely to be true.
+*/
+#ifndef RAPIDJSON_LIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define RAPIDJSON_LIKELY(x) (x)
+#endif
+#endif
+
+//! Compiler branching hint for expression with low probability to be true.
+/*!
+ \ingroup RAPIDJSON_CONFIG
+ \param x Boolean expression unlikely to be true.
+*/
+#ifndef RAPIDJSON_UNLIKELY
+#if defined(__GNUC__) || defined(__clang__)
+#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define RAPIDJSON_UNLIKELY(x) (x)
+#endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+
+#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
+#define RAPIDJSON_MULTILINEMACRO_END \
+} while((void)0, 0)
+
+// adopted from Boost
+#define RAPIDJSON_VERSION_CODE(x,y,z) \
+ (((x)*100000) + ((y)*100) + (z))
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
+
+#if defined(__GNUC__)
+#define RAPIDJSON_GNUC \
+ RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
+#endif
+
+#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
+
+#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
+#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
+#define RAPIDJSON_DIAG_OFF(x) \
+ RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
+
+// push/pop support in Clang and GCC>=4.6
+#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
+#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
+#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
+#else // GCC >= 4.2, < 4.6
+#define RAPIDJSON_DIAG_PUSH /* ignored */
+#define RAPIDJSON_DIAG_POP /* ignored */
+#endif
+
+#elif defined(_MSC_VER)
+
+// pragma (MSVC specific)
+#define RAPIDJSON_PRAGMA(x) __pragma(x)
+#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
+
+#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
+#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
+#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
+
+#else
+
+#define RAPIDJSON_DIAG_OFF(x) /* ignored */
+#define RAPIDJSON_DIAG_PUSH /* ignored */
+#define RAPIDJSON_DIAG_POP /* ignored */
+
+#endif // RAPIDJSON_DIAG_*
+
+///////////////////////////////////////////////////////////////////////////////
+// C++11 features
+
+#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#if defined(__clang__)
+#if __has_feature(cxx_rvalue_references) && \
+ (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#else
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
+#endif
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1600)
+
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
+#else
+#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
+
+#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
+// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
+#else
+#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
+#endif
+#endif
+#if RAPIDJSON_HAS_CXX11_NOEXCEPT
+#define RAPIDJSON_NOEXCEPT noexcept
+#else
+#define RAPIDJSON_NOEXCEPT /* noexcept */
+#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
+
+// no automatic detection, yet
+#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
+#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
+#endif
+
+#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
+#if defined(__clang__)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
+#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
+ (defined(_MSC_VER) && _MSC_VER >= 1700)
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
+#else
+#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
+#endif
+#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
+
+//!@endcond
+
+///////////////////////////////////////////////////////////////////////////////
+// new/delete
+
+#ifndef RAPIDJSON_NEW
+///! customization point for global \c new
+#define RAPIDJSON_NEW(x) new x
+#endif
+#ifndef RAPIDJSON_DELETE
+///! customization point for global \c delete
+#define RAPIDJSON_DELETE(x) delete x
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Type
+
+/*! \namespace rapidjson
+ \brief main RapidJSON namespace
+ \see RAPIDJSON_NAMESPACE
+*/
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Type of JSON value
+enum Type {
+ kNullType = 0, //!< null
+ kFalseType = 1, //!< false
+ kTrueType = 2, //!< true
+ kObjectType = 3, //!< object
+ kArrayType = 4, //!< array
+ kStringType = 5, //!< string
+ kNumberType = 6 //!< number
+};
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/reader.h b/ext/librethinkdbxx/src/rapidjson/reader.h
new file mode 100644
index 00000000..19f8849b
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/reader.h
@@ -0,0 +1,1879 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_READER_H_
+#define RAPIDJSON_READER_H_
+
+/*! \file reader.h */
+
+#include "allocators.h"
+#include "stream.h"
+#include "encodedstream.h"
+#include "internal/meta.h"
+#include "internal/stack.h"
+#include "internal/strtod.h"
+#include <limits>
+
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(old-style-cast)
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define RAPIDJSON_NOTHING /* deliberately empty */
+#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN
+#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \
+ RAPIDJSON_MULTILINEMACRO_BEGIN \
+ if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \
+ RAPIDJSON_MULTILINEMACRO_END
+#endif
+#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING)
+//!@endcond
+
+/*! \def RAPIDJSON_PARSE_ERROR_NORETURN
+ \ingroup RAPIDJSON_ERRORS
+ \brief Macro to indicate a parse error.
+ \param parseErrorCode \ref rapidjson::ParseErrorCode of the error
+ \param offset position of the error in JSON input (\c size_t)
+
+ This macros can be used as a customization point for the internal
+ error handling mechanism of RapidJSON.
+
+ A common usage model is to throw an exception instead of requiring the
+ caller to explicitly check the \ref rapidjson::GenericReader::Parse's
+ return value:
+
+ \code
+ #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \
+ throw ParseException(parseErrorCode, #parseErrorCode, offset)
+
+ #include <stdexcept> // std::runtime_error
+ #include "rapidjson/error/error.h" // rapidjson::ParseResult
+
+ struct ParseException : std::runtime_error, rapidjson::ParseResult {
+ ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset)
+ : std::runtime_error(msg), ParseResult(code, offset) {}
+ };
+
+ #include "rapidjson/reader.h"
+ \endcode
+
+ \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse
+ */
+#ifndef RAPIDJSON_PARSE_ERROR_NORETURN
+#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
+ RAPIDJSON_MULTILINEMACRO_BEGIN \
+ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \
+ SetParseError(parseErrorCode, offset); \
+ RAPIDJSON_MULTILINEMACRO_END
+#endif
+
+/*! \def RAPIDJSON_PARSE_ERROR
+ \ingroup RAPIDJSON_ERRORS
+ \brief (Internal) macro to indicate and handle a parse error.
+ \param parseErrorCode \ref rapidjson::ParseErrorCode of the error
+ \param offset position of the error in JSON input (\c size_t)
+
+ Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing.
+
+ \see RAPIDJSON_PARSE_ERROR_NORETURN
+ \hideinitializer
+ */
+#ifndef RAPIDJSON_PARSE_ERROR
+#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
+ RAPIDJSON_MULTILINEMACRO_BEGIN \
+ RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \
+ RAPIDJSON_MULTILINEMACRO_END
+#endif
+
+#include "error/error.h" // ParseErrorCode, ParseResult
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// ParseFlag
+
+/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kParseDefaultFlags definition.
+
+ User can define this as any \c ParseFlag combinations.
+*/
+#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS
+#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags
+#endif
+
+//! Combination of parseFlags
+/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream
+ */
+enum ParseFlag {
+ kParseNoFlags = 0, //!< No flags are set.
+ kParseInsituFlag = 1, //!< In-situ(destructive) parsing.
+ kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings.
+ kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing.
+ kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
+ kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower).
+ kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments.
+ kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
+ kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
+ kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles.
+ kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Handler
+
+/*! \class rapidjson::Handler
+ \brief Concept for receiving events from GenericReader upon parsing.
+ The functions return true if no error occurs. If they return false,
+ the event publisher should terminate the process.
+\code
+concept Handler {
+ typename Ch;
+
+ bool Null();
+ bool Bool(bool b);
+ bool Int(int i);
+ bool Uint(unsigned i);
+ bool Int64(int64_t i);
+ bool Uint64(uint64_t i);
+ bool Double(double d);
+ /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+ bool RawNumber(const Ch* str, SizeType length, bool copy);
+ bool String(const Ch* str, SizeType length, bool copy);
+ bool StartObject();
+ bool Key(const Ch* str, SizeType length, bool copy);
+ bool EndObject(SizeType memberCount);
+ bool StartArray();
+ bool EndArray(SizeType elementCount);
+};
+\endcode
+*/
+///////////////////////////////////////////////////////////////////////////////
+// BaseReaderHandler
+
+//! Default implementation of Handler.
+/*! This can be used as base class of any reader handler.
+ \note implements Handler concept
+*/
+template<typename Encoding = UTF8<>, typename Derived = void>
+struct BaseReaderHandler {
+ typedef typename Encoding::Ch Ch;
+
+ typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override;
+
+ bool Default() { return true; }
+ bool Null() { return static_cast<Override&>(*this).Default(); }
+ bool Bool(bool) { return static_cast<Override&>(*this).Default(); }
+ bool Int(int) { return static_cast<Override&>(*this).Default(); }
+ bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); }
+ bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
+ bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
+ bool Double(double) { return static_cast<Override&>(*this).Default(); }
+ /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length)
+ bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
+ bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
+ bool StartObject() { return static_cast<Override&>(*this).Default(); }
+ bool Key(const Ch* str, SizeType len, bool copy) { return static_cast<Override&>(*this).String(str, len, copy); }
+ bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); }
+ bool StartArray() { return static_cast<Override&>(*this).Default(); }
+ bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// StreamLocalCopy
+
+namespace internal {
+
+template<typename Stream, int = StreamTraits<Stream>::copyOptimization>
+class StreamLocalCopy;
+
+//! Do copy optimization.
+template<typename Stream>
+class StreamLocalCopy<Stream, 1> {
+public:
+ StreamLocalCopy(Stream& original) : s(original), original_(original) {}
+ ~StreamLocalCopy() { original_ = s; }
+
+ Stream s;
+
+private:
+ StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */;
+
+ Stream& original_;
+};
+
+//! Keep reference.
+template<typename Stream>
+class StreamLocalCopy<Stream, 0> {
+public:
+ StreamLocalCopy(Stream& original) : s(original) {}
+
+ Stream& s;
+
+private:
+ StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */;
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// SkipWhitespace
+
+//! Skip the JSON white spaces in a stream.
+/*! \param is A input stream for skipping white spaces.
+ \note This function has SSE2/SSE4.2 specialization.
+*/
+template<typename InputStream>
+void SkipWhitespace(InputStream& is) {
+ internal::StreamLocalCopy<InputStream> copy(is);
+ InputStream& s(copy.s);
+
+ typename InputStream::Ch c;
+ while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t')
+ s.Take();
+}
+
+inline const char* SkipWhitespace(const char* p, const char* end) {
+ while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ return p;
+}
+
+#ifdef RAPIDJSON_SSE42
+//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+ // Fast return for single non-whitespace
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // 16-byte align to the next boundary
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // The rest of string using SIMD
+ static const char whitespace[16] = " \n\r\t";
+ const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
+ if (r != 0) { // some of characters is non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+ // Fast return for single non-whitespace
+ if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ else
+ return p;
+
+ // The middle of string using SIMD
+ static const char whitespace[16] = " \n\r\t";
+ const __m128i w = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespace[0]));
+
+ for (; p <= end - 16; p += 16) {
+ const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY));
+ if (r != 0) { // some of characters is non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+
+ return SkipWhitespace(p, end);
+}
+
+#elif defined(RAPIDJSON_SSE2)
+
+//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once.
+inline const char *SkipWhitespace_SIMD(const char* p) {
+ // Fast return for single non-whitespace
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // 16-byte align to the next boundary
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')
+ ++p;
+ else
+ return p;
+
+ // The rest of string
+ #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+ static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+ #undef C16
+
+ const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+ const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+ const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+ const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ __m128i x = _mm_cmpeq_epi8(s, w0);
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+ unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+ if (r != 0) { // some of characters may be non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+}
+
+inline const char *SkipWhitespace_SIMD(const char* p, const char* end) {
+ // Fast return for single non-whitespace
+ if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
+ ++p;
+ else
+ return p;
+
+ // The rest of string
+ #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c }
+ static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') };
+ #undef C16
+
+ const __m128i w0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[0][0]));
+ const __m128i w1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[1][0]));
+ const __m128i w2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[2][0]));
+ const __m128i w3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&whitespaces[3][0]));
+
+ for (; p <= end - 16; p += 16) {
+ const __m128i s = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ __m128i x = _mm_cmpeq_epi8(s, w0);
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2));
+ x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3));
+ unsigned short r = static_cast<unsigned short>(~_mm_movemask_epi8(x));
+ if (r != 0) { // some of characters may be non-whitespace
+#ifdef _MSC_VER // Find the index of first non-whitespace
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ return p + offset;
+#else
+ return p + __builtin_ffs(r) - 1;
+#endif
+ }
+ }
+
+ return SkipWhitespace(p, end);
+}
+
+#endif // RAPIDJSON_SSE2
+
+#ifdef RAPIDJSON_SIMD
+//! Template function specialization for InsituStringStream
+template<> inline void SkipWhitespace(InsituStringStream& is) {
+ is.src_ = const_cast<char*>(SkipWhitespace_SIMD(is.src_));
+}
+
+//! Template function specialization for StringStream
+template<> inline void SkipWhitespace(StringStream& is) {
+ is.src_ = SkipWhitespace_SIMD(is.src_);
+}
+
+template<> inline void SkipWhitespace(EncodedInputStream<UTF8<>, MemoryStream>& is) {
+ is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_);
+}
+#endif // RAPIDJSON_SIMD
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericReader
+
+//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator.
+/*! GenericReader parses JSON text from a stream, and send events synchronously to an
+ object implementing Handler concept.
+
+ It needs to allocate a stack for storing a single decoded string during
+ non-destructive parsing.
+
+ For in-situ parsing, the decoded string is directly written to the source
+ text string, no temporary buffer is required.
+
+ A GenericReader object can be reused for parsing multiple JSON text.
+
+ \tparam SourceEncoding Encoding of the input stream.
+ \tparam TargetEncoding Encoding of the parse output.
+ \tparam StackAllocator Allocator type for stack.
+*/
+template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator = CrtAllocator>
+class GenericReader {
+public:
+ typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type
+
+ //! Constructor.
+ /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
+ \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
+ */
+ GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {}
+
+ //! Parse JSON text.
+ /*! \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept.
+ \tparam Handler Type of handler, implementing Handler concept.
+ \param is Input stream to be parsed.
+ \param handler The handler to receive events.
+ \return Whether the parsing is successful.
+ */
+ template <unsigned parseFlags, typename InputStream, typename Handler>
+ ParseResult Parse(InputStream& is, Handler& handler) {
+ if (parseFlags & kParseIterativeFlag)
+ return IterativeParse<parseFlags>(is, handler);
+
+ parseResult_.Clear();
+
+ ClearStackOnExit scope(*this);
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+ if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ }
+ else {
+ ParseValue<parseFlags>(is, handler);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+ if (!(parseFlags & kParseStopWhenDoneFlag)) {
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+
+ if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ }
+ }
+ }
+
+ return parseResult_;
+ }
+
+ //! Parse JSON text (with \ref kParseDefaultFlags)
+ /*! \tparam InputStream Type of input stream, implementing Stream concept
+ \tparam Handler Type of handler, implementing Handler concept.
+ \param is Input stream to be parsed.
+ \param handler The handler to receive events.
+ \return Whether the parsing is successful.
+ */
+ template <typename InputStream, typename Handler>
+ ParseResult Parse(InputStream& is, Handler& handler) {
+ return Parse<kParseDefaultFlags>(is, handler);
+ }
+
+ //! Whether a parse error has occured in the last parsing.
+ bool HasParseError() const { return parseResult_.IsError(); }
+
+ //! Get the \ref ParseErrorCode of last parsing.
+ ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); }
+
+ //! Get the position of last parsing error in input, 0 otherwise.
+ size_t GetErrorOffset() const { return parseResult_.Offset(); }
+
+protected:
+ void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); }
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ GenericReader(const GenericReader&);
+ GenericReader& operator=(const GenericReader&);
+
+ void ClearStack() { stack_.Clear(); }
+
+ // clear stack on any exit from ParseStream, e.g. due to exception
+ struct ClearStackOnExit {
+ explicit ClearStackOnExit(GenericReader& r) : r_(r) {}
+ ~ClearStackOnExit() { r_.ClearStack(); }
+ private:
+ GenericReader& r_;
+ ClearStackOnExit(const ClearStackOnExit&);
+ ClearStackOnExit& operator=(const ClearStackOnExit&);
+ };
+
+ template<unsigned parseFlags, typename InputStream>
+ void SkipWhitespaceAndComments(InputStream& is) {
+ SkipWhitespace(is);
+
+ if (parseFlags & kParseCommentsFlag) {
+ while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) {
+ if (Consume(is, '*')) {
+ while (true) {
+ if (RAPIDJSON_UNLIKELY(is.Peek() == '\0'))
+ RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
+ else if (Consume(is, '*')) {
+ if (Consume(is, '/'))
+ break;
+ }
+ else
+ is.Take();
+ }
+ }
+ else if (RAPIDJSON_LIKELY(Consume(is, '/')))
+ while (is.Peek() != '\0' && is.Take() != '\n');
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
+
+ SkipWhitespace(is);
+ }
+ }
+ }
+
+ // Parse object: { string : value, ... }
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseObject(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == '{');
+ is.Take(); // Skip '{'
+
+ if (RAPIDJSON_UNLIKELY(!handler.StartObject()))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (Consume(is, '}')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+
+ for (SizeType memberCount = 0;;) {
+ if (RAPIDJSON_UNLIKELY(is.Peek() != '"'))
+ RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
+
+ ParseString<parseFlags>(is, handler, true);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (RAPIDJSON_UNLIKELY(!Consume(is, ':')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ ParseValue<parseFlags>(is, handler);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ ++memberCount;
+
+ switch (is.Peek()) {
+ case ',':
+ is.Take();
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ break;
+ case '}':
+ is.Take();
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ default:
+ RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy
+ }
+
+ if (parseFlags & kParseTrailingCommasFlag) {
+ if (is.Peek() == '}') {
+ if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ is.Take();
+ return;
+ }
+ }
+ }
+ }
+
+ // Parse array: [ value, ... ]
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseArray(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == '[');
+ is.Take(); // Skip '['
+
+ if (RAPIDJSON_UNLIKELY(!handler.StartArray()))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (Consume(is, ']')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+
+ for (SizeType elementCount = 0;;) {
+ ParseValue<parseFlags>(is, handler);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ ++elementCount;
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+
+ if (Consume(is, ',')) {
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ }
+ else if (Consume(is, ']')) {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ return;
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
+
+ if (parseFlags & kParseTrailingCommasFlag) {
+ if (is.Peek() == ']') {
+ if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ is.Take();
+ return;
+ }
+ }
+ }
+ }
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseNull(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == 'n');
+ is.Take();
+
+ if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Null()))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseTrue(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == 't');
+ is.Take();
+
+ if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Bool(true)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseFalse(InputStream& is, Handler& handler) {
+ RAPIDJSON_ASSERT(is.Peek() == 'f');
+ is.Take();
+
+ if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) {
+ if (RAPIDJSON_UNLIKELY(!handler.Bool(false)))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell());
+ }
+
+ template<typename InputStream>
+ RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) {
+ if (RAPIDJSON_LIKELY(is.Peek() == expect)) {
+ is.Take();
+ return true;
+ }
+ else
+ return false;
+ }
+
+ // Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
+ template<typename InputStream>
+ unsigned ParseHex4(InputStream& is, size_t escapeOffset) {
+ unsigned codepoint = 0;
+ for (int i = 0; i < 4; i++) {
+ Ch c = is.Peek();
+ codepoint <<= 4;
+ codepoint += static_cast<unsigned>(c);
+ if (c >= '0' && c <= '9')
+ codepoint -= '0';
+ else if (c >= 'A' && c <= 'F')
+ codepoint -= 'A' - 10;
+ else if (c >= 'a' && c <= 'f')
+ codepoint -= 'a' - 10;
+ else {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
+ }
+ is.Take();
+ }
+ return codepoint;
+ }
+
+ template <typename CharType>
+ class StackStream {
+ public:
+ typedef CharType Ch;
+
+ StackStream(internal::Stack<StackAllocator>& stack) : stack_(stack), length_(0) {}
+ RAPIDJSON_FORCEINLINE void Put(Ch c) {
+ *stack_.template Push<Ch>() = c;
+ ++length_;
+ }
+
+ RAPIDJSON_FORCEINLINE void* Push(SizeType count) {
+ length_ += count;
+ return stack_.template Push<Ch>(count);
+ }
+
+ size_t Length() const { return length_; }
+
+ Ch* Pop() {
+ return stack_.template Pop<Ch>(length_);
+ }
+
+ private:
+ StackStream(const StackStream&);
+ StackStream& operator=(const StackStream&);
+
+ internal::Stack<StackAllocator>& stack_;
+ SizeType length_;
+ };
+
+ // Parse string and generate String event. Different code paths for kParseInsituFlag.
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseString(InputStream& is, Handler& handler, bool isKey = false) {
+ internal::StreamLocalCopy<InputStream> copy(is);
+ InputStream& s(copy.s);
+
+ RAPIDJSON_ASSERT(s.Peek() == '\"');
+ s.Take(); // Skip '\"'
+
+ bool success = false;
+ if (parseFlags & kParseInsituFlag) {
+ typename InputStream::Ch *head = s.PutBegin();
+ ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ size_t length = s.PutEnd(head) - 1;
+ RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+ const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+ success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false));
+ }
+ else {
+ StackStream<typename TargetEncoding::Ch> stackStream(stack_);
+ ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ SizeType length = static_cast<SizeType>(stackStream.Length()) - 1;
+ const typename TargetEncoding::Ch* const str = stackStream.Pop();
+ success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true));
+ }
+ if (RAPIDJSON_UNLIKELY(!success))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
+ }
+
+ // Parse string to an output is
+ // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation.
+ template<unsigned parseFlags, typename SEncoding, typename TEncoding, typename InputStream, typename OutputStream>
+ RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) {
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ static const char escape[256] = {
+ Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/',
+ Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0,
+ 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0,
+ 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16
+ };
+#undef Z16
+//!@endcond
+
+ for (;;) {
+ // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation.
+ if (!(parseFlags & kParseValidateEncodingFlag))
+ ScanCopyUnescapedString(is, os);
+
+ Ch c = is.Peek();
+ if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape
+ size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset
+ is.Take();
+ Ch e = is.Peek();
+ if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast<unsigned char>(e)])) {
+ is.Take();
+ os.Put(static_cast<typename TEncoding::Ch>(escape[static_cast<unsigned char>(e)]));
+ }
+ else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode
+ is.Take();
+ unsigned codepoint = ParseHex4(is, escapeOffset);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) {
+ // Handle UTF-16 surrogate pair
+ if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+ unsigned codepoint2 = ParseHex4(is, escapeOffset);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
+ if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset);
+ codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000;
+ }
+ TEncoding::Encode(os, codepoint);
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset);
+ }
+ else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote
+ is.Take();
+ os.Put('\0'); // null-terminate the string
+ return;
+ }
+ else if (RAPIDJSON_UNLIKELY(static_cast<unsigned>(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ if (c == '\0')
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell());
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell());
+ }
+ else {
+ size_t offset = is.Tell();
+ if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ?
+ !Transcoder<SEncoding, TEncoding>::Validate(is, os) :
+ !Transcoder<SEncoding, TEncoding>::Transcode(is, os))))
+ RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset);
+ }
+ }
+ }
+
+ template<typename InputStream, typename OutputStream>
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) {
+ // Do nothing for generic version
+ }
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+ // StringStream -> StackStream<char>
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream<char>& os) {
+ const char* p = is.src_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = p;
+ return;
+ }
+ else
+ os.Put(*p++);
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ SizeType length;
+ #ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+ #else
+ length = static_cast<SizeType>(__builtin_ffs(r) - 1);
+ #endif
+ char* q = reinterpret_cast<char*>(os.Push(length));
+ for (size_t i = 0; i < length; i++)
+ q[i] = p[i];
+
+ p += length;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s);
+ }
+
+ is.src_ = p;
+ }
+
+ // InsituStringStream -> InsituStringStream
+ static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) {
+ RAPIDJSON_ASSERT(&is == &os);
+ (void)os;
+
+ if (is.src_ == is.dst_) {
+ SkipUnescapedString(is);
+ return;
+ }
+
+ char* p = is.src_;
+ char *q = is.dst_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ while (p != nextAligned)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = p;
+ is.dst_ = q;
+ return;
+ }
+ else
+ *q++ = *p++;
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16, q += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ size_t length;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+#else
+ length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+ for (const char* pend = p + length; p != pend; )
+ *q++ = *p++;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s);
+ }
+
+ is.src_ = p;
+ is.dst_ = q;
+ }
+
+ // When read/write pointers are the same for insitu stream, just skip unescaped characters
+ static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) {
+ RAPIDJSON_ASSERT(is.src_ == is.dst_);
+ char* p = is.src_;
+
+ // Scan one by one until alignment (unaligned load may cross page boundary and cause crash)
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ for (; p != nextAligned; p++)
+ if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast<unsigned>(*p) < 0x20)) {
+ is.src_ = is.dst_ = p;
+ return;
+ }
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (;; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ size_t length;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ length = offset;
+#else
+ length = static_cast<size_t>(__builtin_ffs(r) - 1);
+#endif
+ p += length;
+ break;
+ }
+ }
+
+ is.src_ = is.dst_ = p;
+ }
+#endif
+
+ template<typename InputStream, bool backup, bool pushOnTake>
+ class NumberStream;
+
+ template<typename InputStream>
+ class NumberStream<InputStream, false, false> {
+ public:
+ typedef typename InputStream::Ch Ch;
+
+ NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; }
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); }
+ RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); }
+ RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); }
+ RAPIDJSON_FORCEINLINE void Push(char) {}
+
+ size_t Tell() { return is.Tell(); }
+ size_t Length() { return 0; }
+ const char* Pop() { return 0; }
+
+ protected:
+ NumberStream& operator=(const NumberStream&);
+
+ InputStream& is;
+ };
+
+ template<typename InputStream>
+ class NumberStream<InputStream, true, false> : public NumberStream<InputStream, false, false> {
+ typedef NumberStream<InputStream, false, false> Base;
+ public:
+ NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {}
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch TakePush() {
+ stackStream.Put(static_cast<char>(Base::is.Peek()));
+ return Base::is.Take();
+ }
+
+ RAPIDJSON_FORCEINLINE void Push(char c) {
+ stackStream.Put(c);
+ }
+
+ size_t Length() { return stackStream.Length(); }
+
+ const char* Pop() {
+ stackStream.Put('\0');
+ return stackStream.Pop();
+ }
+
+ private:
+ StackStream<char> stackStream;
+ };
+
+ template<typename InputStream>
+ class NumberStream<InputStream, true, true> : public NumberStream<InputStream, true, false> {
+ typedef NumberStream<InputStream, true, false> Base;
+ public:
+ NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {}
+ ~NumberStream() {}
+
+ RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); }
+ };
+
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseNumber(InputStream& is, Handler& handler) {
+ internal::StreamLocalCopy<InputStream> copy(is);
+ NumberStream<InputStream,
+ ((parseFlags & kParseNumbersAsStringsFlag) != 0) ?
+ ((parseFlags & kParseInsituFlag) == 0) :
+ ((parseFlags & kParseFullPrecisionFlag) != 0),
+ (parseFlags & kParseNumbersAsStringsFlag) != 0 &&
+ (parseFlags & kParseInsituFlag) == 0> s(*this, copy.s);
+
+ size_t startOffset = s.Tell();
+ double d = 0.0;
+ bool useNanOrInf = false;
+
+ // Parse minus
+ bool minus = Consume(s, '-');
+
+ // Parse int: zero / ( digit1-9 *DIGIT )
+ unsigned i = 0;
+ uint64_t i64 = 0;
+ bool use64bit = false;
+ int significandDigit = 0;
+ if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) {
+ i = 0;
+ s.TakePush();
+ }
+ else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) {
+ i = static_cast<unsigned>(s.TakePush() - '0');
+
+ if (minus)
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648
+ if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) {
+ i64 = i;
+ use64bit = true;
+ break;
+ }
+ }
+ i = i * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ else
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295
+ if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) {
+ i64 = i;
+ use64bit = true;
+ break;
+ }
+ }
+ i = i * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ }
+ // Parse NaN or Infinity here
+ else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) {
+ useNanOrInf = true;
+ if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) {
+ d = std::numeric_limits<double>::quiet_NaN();
+ }
+ else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) {
+ d = (minus ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity());
+ if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n')
+ && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y'))))
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell());
+
+ // Parse 64bit int
+ bool useDouble = false;
+ if (use64bit) {
+ if (minus)
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808
+ if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) {
+ d = static_cast<double>(i64);
+ useDouble = true;
+ break;
+ }
+ i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ else
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615
+ if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) {
+ d = static_cast<double>(i64);
+ useDouble = true;
+ break;
+ }
+ i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ significandDigit++;
+ }
+ }
+
+ // Force double for big integer
+ if (useDouble) {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
+ d = d * 10 + (s.TakePush() - '0');
+ }
+ }
+
+ // Parse frac = decimal-point 1*DIGIT
+ int expFrac = 0;
+ size_t decimalPosition;
+ if (Consume(s, '.')) {
+ decimalPosition = s.Length();
+
+ if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9')))
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell());
+
+ if (!useDouble) {
+#if RAPIDJSON_64BIT
+ // Use i64 to store significand in 64-bit architecture
+ if (!use64bit)
+ i64 = i;
+
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path
+ break;
+ else {
+ i64 = i64 * 10 + static_cast<unsigned>(s.TakePush() - '0');
+ --expFrac;
+ if (i64 != 0)
+ significandDigit++;
+ }
+ }
+
+ d = static_cast<double>(i64);
+#else
+ // Use double to store significand in 32-bit architecture
+ d = static_cast<double>(use64bit ? i64 : i);
+#endif
+ useDouble = true;
+ }
+
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ if (significandDigit < 17) {
+ d = d * 10.0 + (s.TakePush() - '0');
+ --expFrac;
+ if (RAPIDJSON_LIKELY(d > 0.0))
+ significandDigit++;
+ }
+ else
+ s.TakePush();
+ }
+ }
+ else
+ decimalPosition = s.Length(); // decimal position at the end of integer.
+
+ // Parse exp = e [ minus / plus ] 1*DIGIT
+ int exp = 0;
+ if (Consume(s, 'e') || Consume(s, 'E')) {
+ if (!useDouble) {
+ d = static_cast<double>(use64bit ? i64 : i);
+ useDouble = true;
+ }
+
+ bool expMinus = false;
+ if (Consume(s, '+'))
+ ;
+ else if (Consume(s, '-'))
+ expMinus = true;
+
+ if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ exp = static_cast<int>(s.Take() - '0');
+ if (expMinus) {
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ exp = exp * 10 + static_cast<int>(s.Take() - '0');
+ if (exp >= 214748364) { // Issue #313: prevent overflow exponent
+ while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent
+ s.Take();
+ }
+ }
+ }
+ else { // positive exp
+ int maxExp = 308 - expFrac;
+ while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) {
+ exp = exp * 10 + static_cast<int>(s.Take() - '0');
+ if (RAPIDJSON_UNLIKELY(exp > maxExp))
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset);
+ }
+ }
+ }
+ else
+ RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell());
+
+ if (expMinus)
+ exp = -exp;
+ }
+
+ // Finish parsing, call event according to the type of number.
+ bool cont = true;
+
+ if (parseFlags & kParseNumbersAsStringsFlag) {
+ if (parseFlags & kParseInsituFlag) {
+ s.Pop(); // Pop stack no matter if it will be used or not.
+ typename InputStream::Ch* head = is.PutBegin();
+ const size_t length = s.Tell() - startOffset;
+ RAPIDJSON_ASSERT(length <= 0xFFFFFFFF);
+ // unable to insert the \0 character here, it will erase the comma after this number
+ const typename TargetEncoding::Ch* const str = reinterpret_cast<typename TargetEncoding::Ch*>(head);
+ cont = handler.RawNumber(str, SizeType(length), false);
+ }
+ else {
+ SizeType numCharsToCopy = static_cast<SizeType>(s.Length());
+ StringStream srcStream(s.Pop());
+ StackStream<typename TargetEncoding::Ch> dstStream(stack_);
+ while (numCharsToCopy--) {
+ Transcoder<UTF8<>, TargetEncoding>::Transcode(srcStream, dstStream);
+ }
+ dstStream.Put('\0');
+ const typename TargetEncoding::Ch* str = dstStream.Pop();
+ const SizeType length = static_cast<SizeType>(dstStream.Length()) - 1;
+ cont = handler.RawNumber(str, SizeType(length), true);
+ }
+ }
+ else {
+ size_t length = s.Length();
+ const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not.
+
+ if (useDouble) {
+ int p = exp + expFrac;
+ if (parseFlags & kParseFullPrecisionFlag)
+ d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp);
+ else
+ d = internal::StrtodNormalPrecision(d, p);
+
+ cont = handler.Double(minus ? -d : d);
+ }
+ else if (useNanOrInf) {
+ cont = handler.Double(d);
+ }
+ else {
+ if (use64bit) {
+ if (minus)
+ cont = handler.Int64(static_cast<int64_t>(~i64 + 1));
+ else
+ cont = handler.Uint64(i64);
+ }
+ else {
+ if (minus)
+ cont = handler.Int(static_cast<int32_t>(~i + 1));
+ else
+ cont = handler.Uint(i);
+ }
+ }
+ }
+ if (RAPIDJSON_UNLIKELY(!cont))
+ RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset);
+ }
+
+ // Parse any JSON value
+ template<unsigned parseFlags, typename InputStream, typename Handler>
+ void ParseValue(InputStream& is, Handler& handler) {
+ switch (is.Peek()) {
+ case 'n': ParseNull <parseFlags>(is, handler); break;
+ case 't': ParseTrue <parseFlags>(is, handler); break;
+ case 'f': ParseFalse <parseFlags>(is, handler); break;
+ case '"': ParseString<parseFlags>(is, handler); break;
+ case '{': ParseObject<parseFlags>(is, handler); break;
+ case '[': ParseArray <parseFlags>(is, handler); break;
+ default :
+ ParseNumber<parseFlags>(is, handler);
+ break;
+
+ }
+ }
+
+ // Iterative Parsing
+
+ // States
+ enum IterativeParsingState {
+ IterativeParsingStartState = 0,
+ IterativeParsingFinishState,
+ IterativeParsingErrorState,
+
+ // Object states
+ IterativeParsingObjectInitialState,
+ IterativeParsingMemberKeyState,
+ IterativeParsingKeyValueDelimiterState,
+ IterativeParsingMemberValueState,
+ IterativeParsingMemberDelimiterState,
+ IterativeParsingObjectFinishState,
+
+ // Array states
+ IterativeParsingArrayInitialState,
+ IterativeParsingElementState,
+ IterativeParsingElementDelimiterState,
+ IterativeParsingArrayFinishState,
+
+ // Single value state
+ IterativeParsingValueState
+ };
+
+ enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 };
+
+ // Tokens
+ enum Token {
+ LeftBracketToken = 0,
+ RightBracketToken,
+
+ LeftCurlyBracketToken,
+ RightCurlyBracketToken,
+
+ CommaToken,
+ ColonToken,
+
+ StringToken,
+ FalseToken,
+ TrueToken,
+ NullToken,
+ NumberToken,
+
+ kTokenCount
+ };
+
+ RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) {
+
+//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
+#define N NumberToken
+#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N
+ // Maps from ASCII to Token
+ static const unsigned char tokenMap[256] = {
+ N16, // 00~0F
+ N16, // 10~1F
+ N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F
+ N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F
+ N16, // 40~4F
+ N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F
+ N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F
+ N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F
+ N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF
+ };
+#undef N
+#undef N16
+//!@endcond
+
+ if (sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256)
+ return static_cast<Token>(tokenMap[static_cast<unsigned char>(c)]);
+ else
+ return NumberToken;
+ }
+
+ RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) {
+ // current state x one lookahead token -> new state
+ static const char G[cIterativeParsingStateCount][kTokenCount] = {
+ // Start
+ {
+ IterativeParsingArrayInitialState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingValueState, // String
+ IterativeParsingValueState, // False
+ IterativeParsingValueState, // True
+ IterativeParsingValueState, // Null
+ IterativeParsingValueState // Number
+ },
+ // Finish(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // Error(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // ObjectInitial
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingMemberKeyState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // MemberKey
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingKeyValueDelimiterState, // Colon
+ IterativeParsingErrorState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // KeyValueDelimiter
+ {
+ IterativeParsingArrayInitialState, // Left bracket(push MemberValue state)
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state)
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingMemberValueState, // String
+ IterativeParsingMemberValueState, // False
+ IterativeParsingMemberValueState, // True
+ IterativeParsingMemberValueState, // Null
+ IterativeParsingMemberValueState // Number
+ },
+ // MemberValue
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
+ IterativeParsingMemberDelimiterState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingErrorState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // MemberDelimiter
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingErrorState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingObjectFinishState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingMemberKeyState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // ObjectFinish(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // ArrayInitial
+ {
+ IterativeParsingArrayInitialState, // Left bracket(push Element state)
+ IterativeParsingArrayFinishState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket(push Element state)
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingElementState, // String
+ IterativeParsingElementState, // False
+ IterativeParsingElementState, // True
+ IterativeParsingElementState, // Null
+ IterativeParsingElementState // Number
+ },
+ // Element
+ {
+ IterativeParsingErrorState, // Left bracket
+ IterativeParsingArrayFinishState, // Right bracket
+ IterativeParsingErrorState, // Left curly bracket
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingElementDelimiterState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingErrorState, // String
+ IterativeParsingErrorState, // False
+ IterativeParsingErrorState, // True
+ IterativeParsingErrorState, // Null
+ IterativeParsingErrorState // Number
+ },
+ // ElementDelimiter
+ {
+ IterativeParsingArrayInitialState, // Left bracket(push Element state)
+ IterativeParsingArrayFinishState, // Right bracket
+ IterativeParsingObjectInitialState, // Left curly bracket(push Element state)
+ IterativeParsingErrorState, // Right curly bracket
+ IterativeParsingErrorState, // Comma
+ IterativeParsingErrorState, // Colon
+ IterativeParsingElementState, // String
+ IterativeParsingElementState, // False
+ IterativeParsingElementState, // True
+ IterativeParsingElementState, // Null
+ IterativeParsingElementState // Number
+ },
+ // ArrayFinish(sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ },
+ // Single Value (sink state)
+ {
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
+ IterativeParsingErrorState
+ }
+ }; // End of G
+
+ return static_cast<IterativeParsingState>(G[state][token]);
+ }
+
+ // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit().
+ // May return a new state on state pop.
+ template <unsigned parseFlags, typename InputStream, typename Handler>
+ RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
+ (void)token;
+
+ switch (dst) {
+ case IterativeParsingErrorState:
+ return dst;
+
+ case IterativeParsingObjectInitialState:
+ case IterativeParsingArrayInitialState:
+ {
+ // Push the state(Element or MemeberValue) if we are nested in another array or value of member.
+ // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop.
+ IterativeParsingState n = src;
+ if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState)
+ n = IterativeParsingElementState;
+ else if (src == IterativeParsingKeyValueDelimiterState)
+ n = IterativeParsingMemberValueState;
+ // Push current state.
+ *stack_.template Push<SizeType>(1) = n;
+ // Initialize and push the member/element count.
+ *stack_.template Push<SizeType>(1) = 0;
+ // Call handler
+ bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray();
+ // On handler short circuits the parsing.
+ if (!hr) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ else {
+ is.Take();
+ return dst;
+ }
+ }
+
+ case IterativeParsingMemberKeyState:
+ ParseString<parseFlags>(is, handler, true);
+ if (HasParseError())
+ return IterativeParsingErrorState;
+ else
+ return dst;
+
+ case IterativeParsingKeyValueDelimiterState:
+ RAPIDJSON_ASSERT(token == ColonToken);
+ is.Take();
+ return dst;
+
+ case IterativeParsingMemberValueState:
+ // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+ ParseValue<parseFlags>(is, handler);
+ if (HasParseError()) {
+ return IterativeParsingErrorState;
+ }
+ return dst;
+
+ case IterativeParsingElementState:
+ // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+ ParseValue<parseFlags>(is, handler);
+ if (HasParseError()) {
+ return IterativeParsingErrorState;
+ }
+ return dst;
+
+ case IterativeParsingMemberDelimiterState:
+ case IterativeParsingElementDelimiterState:
+ is.Take();
+ // Update member/element count.
+ *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1;
+ return dst;
+
+ case IterativeParsingObjectFinishState:
+ {
+ // Transit from delimiter is only allowed when trailing commas are enabled
+ if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ // Get member count.
+ SizeType c = *stack_.template Pop<SizeType>(1);
+ // If the object is not empty, count the last member.
+ if (src == IterativeParsingMemberValueState)
+ ++c;
+ // Restore the state.
+ IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
+ // Transit to Finish state if this is the topmost scope.
+ if (n == IterativeParsingStartState)
+ n = IterativeParsingFinishState;
+ // Call handler
+ bool hr = handler.EndObject(c);
+ // On handler short circuits the parsing.
+ if (!hr) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ else {
+ is.Take();
+ return n;
+ }
+ }
+
+ case IterativeParsingArrayFinishState:
+ {
+ // Transit from delimiter is only allowed when trailing commas are enabled
+ if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ // Get element count.
+ SizeType c = *stack_.template Pop<SizeType>(1);
+ // If the array is not empty, count the last element.
+ if (src == IterativeParsingElementState)
+ ++c;
+ // Restore the state.
+ IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
+ // Transit to Finish state if this is the topmost scope.
+ if (n == IterativeParsingStartState)
+ n = IterativeParsingFinishState;
+ // Call handler
+ bool hr = handler.EndArray(c);
+ // On handler short circuits the parsing.
+ if (!hr) {
+ RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
+ return IterativeParsingErrorState;
+ }
+ else {
+ is.Take();
+ return n;
+ }
+ }
+
+ default:
+ // This branch is for IterativeParsingValueState actually.
+ // Use `default:` rather than
+ // `case IterativeParsingValueState:` is for code coverage.
+
+ // The IterativeParsingStartState is not enumerated in this switch-case.
+ // It is impossible for that case. And it can be caught by following assertion.
+
+ // The IterativeParsingFinishState is not enumerated in this switch-case either.
+ // It is a "derivative" state which cannot triggered from Predict() directly.
+ // Therefore it cannot happen here. And it can be caught by following assertion.
+ RAPIDJSON_ASSERT(dst == IterativeParsingValueState);
+
+ // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
+ ParseValue<parseFlags>(is, handler);
+ if (HasParseError()) {
+ return IterativeParsingErrorState;
+ }
+ return IterativeParsingFinishState;
+ }
+ }
+
+ template <typename InputStream>
+ void HandleError(IterativeParsingState src, InputStream& is) {
+ if (HasParseError()) {
+ // Error flag has been set.
+ return;
+ }
+
+ switch (src) {
+ case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return;
+ case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return;
+ case IterativeParsingObjectInitialState:
+ case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return;
+ case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return;
+ case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return;
+ case IterativeParsingKeyValueDelimiterState:
+ case IterativeParsingArrayInitialState:
+ case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return;
+ default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return;
+ }
+ }
+
+ template <unsigned parseFlags, typename InputStream, typename Handler>
+ ParseResult IterativeParse(InputStream& is, Handler& handler) {
+ parseResult_.Clear();
+ ClearStackOnExit scope(*this);
+ IterativeParsingState state = IterativeParsingStartState;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ while (is.Peek() != '\0') {
+ Token t = Tokenize(is.Peek());
+ IterativeParsingState n = Predict(state, t);
+ IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler);
+
+ if (d == IterativeParsingErrorState) {
+ HandleError(state, is);
+ break;
+ }
+
+ state = d;
+
+ // Do not further consume streams if a root JSON has been parsed.
+ if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState)
+ break;
+
+ SkipWhitespaceAndComments<parseFlags>(is);
+ RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
+ }
+
+ // Handle the end of file.
+ if (state != IterativeParsingFinishState)
+ HandleError(state, is);
+
+ return parseResult_;
+ }
+
+ static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
+ internal::Stack<StackAllocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
+ ParseResult parseResult_;
+}; // class GenericReader
+
+//! Reader with UTF8 encoding and default allocator.
+typedef GenericReader<UTF8<>, UTF8<> > Reader;
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_READER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/schema.h b/ext/librethinkdbxx/src/rapidjson/schema.h
new file mode 100644
index 00000000..b182aa27
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/schema.h
@@ -0,0 +1,2006 @@
+// Tencent is pleased to support the open source community by making RapidJSON available->
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License-> You may obtain a copy of the License at
+//
+// http://opensource->org/licenses/MIT
+//
+// 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->
+
+#ifndef RAPIDJSON_SCHEMA_H_
+#define RAPIDJSON_SCHEMA_H_
+
+#include "document.h"
+#include "pointer.h"
+#include <cmath> // abs, floor
+
+#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
+#endif
+
+#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 1
+#else
+#define RAPIDJSON_SCHEMA_USE_STDREGEX 0
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+#include "internal/regex.h"
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+#include <regex>
+#endif
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
+#define RAPIDJSON_SCHEMA_HAS_REGEX 1
+#else
+#define RAPIDJSON_SCHEMA_HAS_REGEX 0
+#endif
+
+#ifndef RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_VERBOSE 0
+#endif
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#include "stringbuffer.h"
+#endif
+
+RAPIDJSON_DIAG_PUSH
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(weak-vtables)
+RAPIDJSON_DIAG_OFF(exit-time-destructors)
+RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
+RAPIDJSON_DIAG_OFF(variadic-macros)
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Verbose Utilities
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+
+namespace internal {
+
+inline void PrintInvalidKeyword(const char* keyword) {
+ printf("Fail keyword: %s\n", keyword);
+}
+
+inline void PrintInvalidKeyword(const wchar_t* keyword) {
+ wprintf(L"Fail keyword: %ls\n", keyword);
+}
+
+inline void PrintInvalidDocument(const char* document) {
+ printf("Fail document: %s\n\n", document);
+}
+
+inline void PrintInvalidDocument(const wchar_t* document) {
+ wprintf(L"Fail document: %ls\n\n", document);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
+ printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
+}
+
+inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
+ wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
+}
+
+} // namespace internal
+
+#endif // RAPIDJSON_SCHEMA_VERBOSE
+
+///////////////////////////////////////////////////////////////////////////////
+// RAPIDJSON_INVALID_KEYWORD_RETURN
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
+#else
+#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
+#endif
+
+#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+ context.invalidKeyword = keyword.GetString();\
+ RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
+ return false;\
+RAPIDJSON_MULTILINEMACRO_END
+
+///////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+
+template <typename ValueType, typename Allocator>
+class GenericSchemaDocument;
+
+namespace internal {
+
+template <typename SchemaDocumentType>
+class Schema;
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaValidator
+
+class ISchemaValidator {
+public:
+ virtual ~ISchemaValidator() {}
+ virtual bool IsValid() const = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// ISchemaStateFactory
+
+template <typename SchemaType>
+class ISchemaStateFactory {
+public:
+ virtual ~ISchemaStateFactory() {}
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
+ virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
+ virtual void* CreateHasher() = 0;
+ virtual uint64_t GetHashCode(void* hasher) = 0;
+ virtual void DestroryHasher(void* hasher) = 0;
+ virtual void* MallocState(size_t size) = 0;
+ virtual void FreeState(void* p) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// For comparison of compound value
+template<typename Encoding, typename Allocator>
+class Hasher {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
+
+ bool Null() { return WriteType(kNullType); }
+ bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
+ bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+ bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+ bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
+ bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
+ bool Double(double d) {
+ Number n;
+ if (d < 0) n.u.i = static_cast<int64_t>(d);
+ else n.u.u = static_cast<uint64_t>(d);
+ n.d = d;
+ return WriteNumber(n);
+ }
+
+ bool RawNumber(const Ch* str, SizeType len, bool) {
+ WriteBuffer(kNumberType, str, len * sizeof(Ch));
+ return true;
+ }
+
+ bool String(const Ch* str, SizeType len, bool) {
+ WriteBuffer(kStringType, str, len * sizeof(Ch));
+ return true;
+ }
+
+ bool StartObject() { return true; }
+ bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
+ bool EndObject(SizeType memberCount) {
+ uint64_t h = Hash(0, kObjectType);
+ uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
+ for (SizeType i = 0; i < memberCount; i++)
+ h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ bool StartArray() { return true; }
+ bool EndArray(SizeType elementCount) {
+ uint64_t h = Hash(0, kArrayType);
+ uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
+ for (SizeType i = 0; i < elementCount; i++)
+ h = Hash(h, e[i]); // Use hash to achieve element order sensitive
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
+
+ uint64_t GetHashCode() const {
+ RAPIDJSON_ASSERT(IsValid());
+ return *stack_.template Top<uint64_t>();
+ }
+
+private:
+ static const size_t kDefaultSize = 256;
+ struct Number {
+ union U {
+ uint64_t u;
+ int64_t i;
+ }u;
+ double d;
+ };
+
+ bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
+
+ bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
+
+ bool WriteBuffer(Type type, const void* data, size_t len) {
+ // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
+ uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
+ const unsigned char* d = static_cast<const unsigned char*>(data);
+ for (size_t i = 0; i < len; i++)
+ h = Hash(h, d[i]);
+ *stack_.template Push<uint64_t>() = h;
+ return true;
+ }
+
+ static uint64_t Hash(uint64_t h, uint64_t d) {
+ static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
+ h ^= d;
+ h *= kPrime;
+ return h;
+ }
+
+ Stack<Allocator> stack_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidationContext
+
+template <typename SchemaDocumentType>
+struct SchemaValidationContext {
+ typedef Schema<SchemaDocumentType> SchemaType;
+ typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
+ typedef typename SchemaType::ValueType ValueType;
+ typedef typename ValueType::Ch Ch;
+
+ enum PatternValidatorType {
+ kPatternValidatorOnly,
+ kPatternValidatorWithProperty,
+ kPatternValidatorWithAdditionalProperty
+ };
+
+ SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
+ factory(f),
+ schema(s),
+ valueSchema(),
+ invalidKeyword(),
+ hasher(),
+ arrayElementHashCodes(),
+ validators(),
+ validatorCount(),
+ patternPropertiesValidators(),
+ patternPropertiesValidatorCount(),
+ patternPropertiesSchemas(),
+ patternPropertiesSchemaCount(),
+ valuePatternValidatorType(kPatternValidatorOnly),
+ propertyExist(),
+ inArray(false),
+ valueUniqueness(false),
+ arrayUniqueness(false)
+ {
+ }
+
+ ~SchemaValidationContext() {
+ if (hasher)
+ factory.DestroryHasher(hasher);
+ if (validators) {
+ for (SizeType i = 0; i < validatorCount; i++)
+ factory.DestroySchemaValidator(validators[i]);
+ factory.FreeState(validators);
+ }
+ if (patternPropertiesValidators) {
+ for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
+ factory.DestroySchemaValidator(patternPropertiesValidators[i]);
+ factory.FreeState(patternPropertiesValidators);
+ }
+ if (patternPropertiesSchemas)
+ factory.FreeState(patternPropertiesSchemas);
+ if (propertyExist)
+ factory.FreeState(propertyExist);
+ }
+
+ SchemaValidatorFactoryType& factory;
+ const SchemaType* schema;
+ const SchemaType* valueSchema;
+ const Ch* invalidKeyword;
+ void* hasher; // Only validator access
+ void* arrayElementHashCodes; // Only validator access this
+ ISchemaValidator** validators;
+ SizeType validatorCount;
+ ISchemaValidator** patternPropertiesValidators;
+ SizeType patternPropertiesValidatorCount;
+ const SchemaType** patternPropertiesSchemas;
+ SizeType patternPropertiesSchemaCount;
+ PatternValidatorType valuePatternValidatorType;
+ PatternValidatorType objectPatternValidatorType;
+ SizeType arrayElementIndex;
+ bool* propertyExist;
+ bool inArray;
+ bool valueUniqueness;
+ bool arrayUniqueness;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Schema
+
+template <typename SchemaDocumentType>
+class Schema {
+public:
+ typedef typename SchemaDocumentType::ValueType ValueType;
+ typedef typename SchemaDocumentType::AllocatorType AllocatorType;
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+ typedef SchemaValidationContext<SchemaDocumentType> Context;
+ typedef Schema<SchemaDocumentType> SchemaType;
+ typedef GenericValue<EncodingType, AllocatorType> SValue;
+ friend class GenericSchemaDocument<ValueType, AllocatorType>;
+
+ Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
+ allocator_(allocator),
+ enum_(),
+ enumCount_(),
+ not_(),
+ type_((1 << kTotalSchemaType) - 1), // typeless
+ validatorCount_(),
+ properties_(),
+ additionalPropertiesSchema_(),
+ patternProperties_(),
+ patternPropertyCount_(),
+ propertyCount_(),
+ minProperties_(),
+ maxProperties_(SizeType(~0)),
+ additionalProperties_(true),
+ hasDependencies_(),
+ hasRequired_(),
+ hasSchemaDependencies_(),
+ additionalItemsSchema_(),
+ itemsList_(),
+ itemsTuple_(),
+ itemsTupleCount_(),
+ minItems_(),
+ maxItems_(SizeType(~0)),
+ additionalItems_(true),
+ uniqueItems_(false),
+ pattern_(),
+ minLength_(0),
+ maxLength_(~SizeType(0)),
+ exclusiveMinimum_(false),
+ exclusiveMaximum_(false)
+ {
+ typedef typename SchemaDocumentType::ValueType ValueType;
+ typedef typename ValueType::ConstValueIterator ConstValueIterator;
+ typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
+
+ if (!value.IsObject())
+ return;
+
+ if (const ValueType* v = GetMember(value, GetTypeString())) {
+ type_ = 0;
+ if (v->IsString())
+ AddType(*v);
+ else if (v->IsArray())
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
+ AddType(*itr);
+ }
+
+ if (const ValueType* v = GetMember(value, GetEnumString()))
+ if (v->IsArray() && v->Size() > 0) {
+ enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
+ typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
+ char buffer[256 + 24];
+ MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
+ EnumHasherType h(&hasherAllocator, 256);
+ itr->Accept(h);
+ enum_[enumCount_++] = h.GetHashCode();
+ }
+ }
+
+ if (schemaDocument) {
+ AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
+ AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
+ AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
+ }
+
+ if (const ValueType* v = GetMember(value, GetNotString())) {
+ schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
+ notValidatorIndex_ = validatorCount_;
+ validatorCount_++;
+ }
+
+ // Object
+
+ const ValueType* properties = GetMember(value, GetPropertiesString());
+ const ValueType* required = GetMember(value, GetRequiredString());
+ const ValueType* dependencies = GetMember(value, GetDependenciesString());
+ {
+ // Gather properties from properties/required/dependencies
+ SValue allProperties(kArrayType);
+
+ if (properties && properties->IsObject())
+ for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
+ AddUniqueElement(allProperties, itr->name);
+
+ if (required && required->IsArray())
+ for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+ if (itr->IsString())
+ AddUniqueElement(allProperties, *itr);
+
+ if (dependencies && dependencies->IsObject())
+ for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+ AddUniqueElement(allProperties, itr->name);
+ if (itr->value.IsArray())
+ for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
+ if (i->IsString())
+ AddUniqueElement(allProperties, *i);
+ }
+
+ if (allProperties.Size() > 0) {
+ propertyCount_ = allProperties.Size();
+ properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
+ for (SizeType i = 0; i < propertyCount_; i++) {
+ new (&properties_[i]) Property();
+ properties_[i].name = allProperties[i];
+ properties_[i].schema = GetTypeless();
+ }
+ }
+ }
+
+ if (properties && properties->IsObject()) {
+ PointerType q = p.Append(GetPropertiesString(), allocator_);
+ for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
+ SizeType index;
+ if (FindPropertyIndex(itr->name, &index))
+ schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
+ }
+ }
+
+ if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
+ PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
+ patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
+ patternPropertyCount_ = 0;
+
+ for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
+ new (&patternProperties_[patternPropertyCount_]) PatternProperty();
+ patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
+ schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
+ patternPropertyCount_++;
+ }
+ }
+
+ if (required && required->IsArray())
+ for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
+ if (itr->IsString()) {
+ SizeType index;
+ if (FindPropertyIndex(*itr, &index)) {
+ properties_[index].required = true;
+ hasRequired_ = true;
+ }
+ }
+
+ if (dependencies && dependencies->IsObject()) {
+ PointerType q = p.Append(GetDependenciesString(), allocator_);
+ hasDependencies_ = true;
+ for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
+ SizeType sourceIndex;
+ if (FindPropertyIndex(itr->name, &sourceIndex)) {
+ if (itr->value.IsArray()) {
+ properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
+ std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
+ for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
+ SizeType targetIndex;
+ if (FindPropertyIndex(*targetItr, &targetIndex))
+ properties_[sourceIndex].dependencies[targetIndex] = true;
+ }
+ }
+ else if (itr->value.IsObject()) {
+ hasSchemaDependencies_ = true;
+ schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
+ properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
+ validatorCount_++;
+ }
+ }
+ }
+ }
+
+ if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
+ if (v->IsBool())
+ additionalProperties_ = v->GetBool();
+ else if (v->IsObject())
+ schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
+ }
+
+ AssignIfExist(minProperties_, value, GetMinPropertiesString());
+ AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
+
+ // Array
+ if (const ValueType* v = GetMember(value, GetItemsString())) {
+ PointerType q = p.Append(GetItemsString(), allocator_);
+ if (v->IsObject()) // List validation
+ schemaDocument->CreateSchema(&itemsList_, q, *v, document);
+ else if (v->IsArray()) { // Tuple validation
+ itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
+ SizeType index = 0;
+ for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
+ schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
+ }
+ }
+
+ AssignIfExist(minItems_, value, GetMinItemsString());
+ AssignIfExist(maxItems_, value, GetMaxItemsString());
+
+ if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
+ if (v->IsBool())
+ additionalItems_ = v->GetBool();
+ else if (v->IsObject())
+ schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
+ }
+
+ AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
+
+ // String
+ AssignIfExist(minLength_, value, GetMinLengthString());
+ AssignIfExist(maxLength_, value, GetMaxLengthString());
+
+ if (const ValueType* v = GetMember(value, GetPatternString()))
+ pattern_ = CreatePattern(*v);
+
+ // Number
+ if (const ValueType* v = GetMember(value, GetMinimumString()))
+ if (v->IsNumber())
+ minimum_.CopyFrom(*v, *allocator_);
+
+ if (const ValueType* v = GetMember(value, GetMaximumString()))
+ if (v->IsNumber())
+ maximum_.CopyFrom(*v, *allocator_);
+
+ AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
+ AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
+
+ if (const ValueType* v = GetMember(value, GetMultipleOfString()))
+ if (v->IsNumber() && v->GetDouble() > 0.0)
+ multipleOf_.CopyFrom(*v, *allocator_);
+ }
+
+ ~Schema() {
+ if (allocator_) {
+ allocator_->Free(enum_);
+ }
+ if (properties_) {
+ for (SizeType i = 0; i < propertyCount_; i++)
+ properties_[i].~Property();
+ AllocatorType::Free(properties_);
+ }
+ if (patternProperties_) {
+ for (SizeType i = 0; i < patternPropertyCount_; i++)
+ patternProperties_[i].~PatternProperty();
+ AllocatorType::Free(patternProperties_);
+ }
+ AllocatorType::Free(itemsTuple_);
+#if RAPIDJSON_SCHEMA_HAS_REGEX
+ if (pattern_) {
+ pattern_->~RegexType();
+ allocator_->Free(pattern_);
+ }
+#endif
+ }
+
+ bool BeginValue(Context& context) const {
+ if (context.inArray) {
+ if (uniqueItems_)
+ context.valueUniqueness = true;
+
+ if (itemsList_)
+ context.valueSchema = itemsList_;
+ else if (itemsTuple_) {
+ if (context.arrayElementIndex < itemsTupleCount_)
+ context.valueSchema = itemsTuple_[context.arrayElementIndex];
+ else if (additionalItemsSchema_)
+ context.valueSchema = additionalItemsSchema_;
+ else if (additionalItems_)
+ context.valueSchema = GetTypeless();
+ else
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
+ }
+ else
+ context.valueSchema = GetTypeless();
+
+ context.arrayElementIndex++;
+ }
+ return true;
+ }
+
+ RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
+ if (context.patternPropertiesValidatorCount > 0) {
+ bool otherValid = false;
+ SizeType count = context.patternPropertiesValidatorCount;
+ if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
+ otherValid = context.patternPropertiesValidators[--count]->IsValid();
+
+ bool patternValid = true;
+ for (SizeType i = 0; i < count; i++)
+ if (!context.patternPropertiesValidators[i]->IsValid()) {
+ patternValid = false;
+ break;
+ }
+
+ if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
+ if (!patternValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+ else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
+ if (!patternValid || !otherValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+ else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
+ }
+
+ if (enum_) {
+ const uint64_t h = context.factory.GetHashCode(context.hasher);
+ for (SizeType i = 0; i < enumCount_; i++)
+ if (enum_[i] == h)
+ goto foundEnum;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
+ foundEnum:;
+ }
+
+ if (allOf_.schemas)
+ for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
+ if (!context.validators[i]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
+
+ if (anyOf_.schemas) {
+ for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
+ if (context.validators[i]->IsValid())
+ goto foundAny;
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
+ foundAny:;
+ }
+
+ if (oneOf_.schemas) {
+ bool oneValid = false;
+ for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
+ if (context.validators[i]->IsValid()) {
+ if (oneValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+ else
+ oneValid = true;
+ }
+ if (!oneValid)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
+ }
+
+ if (not_ && context.validators[notValidatorIndex_]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
+
+ return true;
+ }
+
+ bool Null(Context& context) const {
+ if (!(type_ & (1 << kNullSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ return CreateParallelValidator(context);
+ }
+
+ bool Bool(Context& context, bool) const {
+ if (!(type_ & (1 << kBooleanSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+ return CreateParallelValidator(context);
+ }
+
+ bool Int(Context& context, int i) const {
+ if (!CheckInt(context, i))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Uint(Context& context, unsigned u) const {
+ if (!CheckUint(context, u))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Int64(Context& context, int64_t i) const {
+ if (!CheckInt(context, i))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Uint64(Context& context, uint64_t u) const {
+ if (!CheckUint(context, u))
+ return false;
+ return CreateParallelValidator(context);
+ }
+
+ bool Double(Context& context, double d) const {
+ if (!(type_ & (1 << kNumberSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
+ return false;
+
+ if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
+ return false;
+
+ if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
+ return false;
+
+ return CreateParallelValidator(context);
+ }
+
+ bool String(Context& context, const Ch* str, SizeType length, bool) const {
+ if (!(type_ & (1 << kStringSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
+ SizeType count;
+ if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
+ if (count < minLength_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
+ if (count > maxLength_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
+ }
+ }
+
+ if (pattern_ && !IsPatternMatch(pattern_, str, length))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
+
+ return CreateParallelValidator(context);
+ }
+
+ bool StartObject(Context& context) const {
+ if (!(type_ & (1 << kObjectSchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (hasDependencies_ || hasRequired_) {
+ context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
+ std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
+ }
+
+ if (patternProperties_) { // pre-allocate schema array
+ SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
+ context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
+ context.patternPropertiesSchemaCount = 0;
+ std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
+ }
+
+ return CreateParallelValidator(context);
+ }
+
+ bool Key(Context& context, const Ch* str, SizeType len, bool) const {
+ if (patternProperties_) {
+ context.patternPropertiesSchemaCount = 0;
+ for (SizeType i = 0; i < patternPropertyCount_; i++)
+ if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
+ }
+
+ SizeType index;
+ if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
+ if (context.patternPropertiesSchemaCount > 0) {
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
+ context.valueSchema = GetTypeless();
+ context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
+ }
+ else
+ context.valueSchema = properties_[index].schema;
+
+ if (context.propertyExist)
+ context.propertyExist[index] = true;
+
+ return true;
+ }
+
+ if (additionalPropertiesSchema_) {
+ if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
+ context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
+ context.valueSchema = GetTypeless();
+ context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
+ }
+ else
+ context.valueSchema = additionalPropertiesSchema_;
+ return true;
+ }
+ else if (additionalProperties_) {
+ context.valueSchema = GetTypeless();
+ return true;
+ }
+
+ if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
+
+ return true;
+ }
+
+ bool EndObject(Context& context, SizeType memberCount) const {
+ if (hasRequired_)
+ for (SizeType index = 0; index < propertyCount_; index++)
+ if (properties_[index].required)
+ if (!context.propertyExist[index])
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
+
+ if (memberCount < minProperties_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
+
+ if (memberCount > maxProperties_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
+
+ if (hasDependencies_) {
+ for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
+ if (context.propertyExist[sourceIndex]) {
+ if (properties_[sourceIndex].dependencies) {
+ for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
+ if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ }
+ else if (properties_[sourceIndex].dependenciesSchema)
+ if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
+ }
+ }
+
+ return true;
+ }
+
+ bool StartArray(Context& context) const {
+ if (!(type_ & (1 << kArraySchemaType)))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ context.arrayElementIndex = 0;
+ context.inArray = true;
+
+ return CreateParallelValidator(context);
+ }
+
+ bool EndArray(Context& context, SizeType elementCount) const {
+ context.inArray = false;
+
+ if (elementCount < minItems_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
+
+ if (elementCount > maxItems_)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
+
+ return true;
+ }
+
+ // Generate functions for string literal according to Ch
+#define RAPIDJSON_STRING_(name, ...) \
+ static const ValueType& Get##name##String() {\
+ static const Ch s[] = { __VA_ARGS__, '\0' };\
+ static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
+ return v;\
+ }
+
+ RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
+ RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
+ RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
+ RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
+ RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
+ RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
+ RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
+ RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
+ RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
+ RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
+ RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
+ RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
+ RAPIDJSON_STRING_(Not, 'n', 'o', 't')
+ RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
+ RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
+ RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
+ RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
+ RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
+ RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
+ RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
+ RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
+ RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
+
+#undef RAPIDJSON_STRING_
+
+private:
+ enum SchemaValueType {
+ kNullSchemaType,
+ kBooleanSchemaType,
+ kObjectSchemaType,
+ kArraySchemaType,
+ kStringSchemaType,
+ kNumberSchemaType,
+ kIntegerSchemaType,
+ kTotalSchemaType
+ };
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+ typedef internal::GenericRegex<EncodingType> RegexType;
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+ typedef std::basic_regex<Ch> RegexType;
+#else
+ typedef char RegexType;
+#endif
+
+ struct SchemaArray {
+ SchemaArray() : schemas(), count() {}
+ ~SchemaArray() { AllocatorType::Free(schemas); }
+ const SchemaType** schemas;
+ SizeType begin; // begin index of context.validators
+ SizeType count;
+ };
+
+ static const SchemaType* GetTypeless() {
+ static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
+ return &typeless;
+ }
+
+ template <typename V1, typename V2>
+ void AddUniqueElement(V1& a, const V2& v) {
+ for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
+ if (*itr == v)
+ return;
+ V1 c(v, *allocator_);
+ a.PushBack(c, *allocator_);
+ }
+
+ static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
+ typename ValueType::ConstMemberIterator itr = value.FindMember(name);
+ return itr != value.MemberEnd() ? &(itr->value) : 0;
+ }
+
+ static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
+ if (const ValueType* v = GetMember(value, name))
+ if (v->IsBool())
+ out = v->GetBool();
+ }
+
+ static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
+ if (const ValueType* v = GetMember(value, name))
+ if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
+ out = static_cast<SizeType>(v->GetUint64());
+ }
+
+ void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
+ if (const ValueType* v = GetMember(value, name)) {
+ if (v->IsArray() && v->Size() > 0) {
+ PointerType q = p.Append(name, allocator_);
+ out.count = v->Size();
+ out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
+ memset(out.schemas, 0, sizeof(Schema*)* out.count);
+ for (SizeType i = 0; i < out.count; i++)
+ schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
+ out.begin = validatorCount_;
+ validatorCount_ += out.count;
+ }
+ }
+ }
+
+#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType& value) {
+ if (value.IsString()) {
+ RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
+ if (!r->IsValid()) {
+ r->~RegexType();
+ AllocatorType::Free(r);
+ r = 0;
+ }
+ return r;
+ }
+ return 0;
+ }
+
+ static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
+ return pattern->Search(str);
+ }
+#elif RAPIDJSON_SCHEMA_USE_STDREGEX
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType& value) {
+ if (value.IsString())
+ try {
+ return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
+ }
+ catch (const std::regex_error&) {
+ }
+ return 0;
+ }
+
+ static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
+ std::match_results<const Ch*> r;
+ return std::regex_search(str, str + length, r, *pattern);
+ }
+#else
+ template <typename ValueType>
+ RegexType* CreatePattern(const ValueType&) { return 0; }
+
+ static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
+#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
+
+ void AddType(const ValueType& type) {
+ if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
+ else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
+ else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
+ else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
+ else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
+ else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
+ else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
+ }
+
+ bool CreateParallelValidator(Context& context) const {
+ if (enum_ || context.arrayUniqueness)
+ context.hasher = context.factory.CreateHasher();
+
+ if (validatorCount_) {
+ RAPIDJSON_ASSERT(context.validators == 0);
+ context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
+ context.validatorCount = validatorCount_;
+
+ if (allOf_.schemas)
+ CreateSchemaValidators(context, allOf_);
+
+ if (anyOf_.schemas)
+ CreateSchemaValidators(context, anyOf_);
+
+ if (oneOf_.schemas)
+ CreateSchemaValidators(context, oneOf_);
+
+ if (not_)
+ context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
+
+ if (hasSchemaDependencies_) {
+ for (SizeType i = 0; i < propertyCount_; i++)
+ if (properties_[i].dependenciesSchema)
+ context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
+ }
+ }
+
+ return true;
+ }
+
+ void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
+ for (SizeType i = 0; i < schemas.count; i++)
+ context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
+ }
+
+ // O(n)
+ bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
+ SizeType len = name.GetStringLength();
+ const Ch* str = name.GetString();
+ for (SizeType index = 0; index < propertyCount_; index++)
+ if (properties_[index].name.GetStringLength() == len &&
+ (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
+ {
+ *outIndex = index;
+ return true;
+ }
+ return false;
+ }
+
+ bool CheckInt(Context& context, int64_t i) const {
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull()) {
+ if (minimum_.IsInt64()) {
+ if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ }
+ else if (minimum_.IsUint64()) {
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
+ }
+ else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!maximum_.IsNull()) {
+ if (maximum_.IsInt64()) {
+ if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ }
+ else if (maximum_.IsUint64())
+ /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64()
+ else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!multipleOf_.IsNull()) {
+ if (multipleOf_.IsUint64()) {
+ if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ }
+ else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckUint(Context& context, uint64_t i) const {
+ if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
+
+ if (!minimum_.IsNull()) {
+ if (minimum_.IsUint64()) {
+ if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ }
+ else if (minimum_.IsInt64())
+ /* do nothing */; // i >= 0 > minimum.Getint64()
+ else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!maximum_.IsNull()) {
+ if (maximum_.IsUint64()) {
+ if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ }
+ else if (maximum_.IsInt64())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
+ else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
+ return false;
+ }
+
+ if (!multipleOf_.IsNull()) {
+ if (multipleOf_.IsUint64()) {
+ if (i % multipleOf_.GetUint64() != 0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ }
+ else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckDoubleMinimum(Context& context, double d) const {
+ if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
+ return true;
+ }
+
+ bool CheckDoubleMaximum(Context& context, double d) const {
+ if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
+ return true;
+ }
+
+ bool CheckDoubleMultipleOf(Context& context, double d) const {
+ double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
+ double q = std::floor(a / b);
+ double r = a - q * b;
+ if (r > 0.0)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
+ return true;
+ }
+
+ struct Property {
+ Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
+ ~Property() { AllocatorType::Free(dependencies); }
+ SValue name;
+ const SchemaType* schema;
+ const SchemaType* dependenciesSchema;
+ SizeType dependenciesValidatorIndex;
+ bool* dependencies;
+ bool required;
+ };
+
+ struct PatternProperty {
+ PatternProperty() : schema(), pattern() {}
+ ~PatternProperty() {
+ if (pattern) {
+ pattern->~RegexType();
+ AllocatorType::Free(pattern);
+ }
+ }
+ const SchemaType* schema;
+ RegexType* pattern;
+ };
+
+ AllocatorType* allocator_;
+ uint64_t* enum_;
+ SizeType enumCount_;
+ SchemaArray allOf_;
+ SchemaArray anyOf_;
+ SchemaArray oneOf_;
+ const SchemaType* not_;
+ unsigned type_; // bitmask of kSchemaType
+ SizeType validatorCount_;
+ SizeType notValidatorIndex_;
+
+ Property* properties_;
+ const SchemaType* additionalPropertiesSchema_;
+ PatternProperty* patternProperties_;
+ SizeType patternPropertyCount_;
+ SizeType propertyCount_;
+ SizeType minProperties_;
+ SizeType maxProperties_;
+ bool additionalProperties_;
+ bool hasDependencies_;
+ bool hasRequired_;
+ bool hasSchemaDependencies_;
+
+ const SchemaType* additionalItemsSchema_;
+ const SchemaType* itemsList_;
+ const SchemaType** itemsTuple_;
+ SizeType itemsTupleCount_;
+ SizeType minItems_;
+ SizeType maxItems_;
+ bool additionalItems_;
+ bool uniqueItems_;
+
+ RegexType* pattern_;
+ SizeType minLength_;
+ SizeType maxLength_;
+
+ SValue minimum_;
+ SValue maximum_;
+ SValue multipleOf_;
+ bool exclusiveMinimum_;
+ bool exclusiveMaximum_;
+};
+
+template<typename Stack, typename Ch>
+struct TokenHelper {
+ RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+ *documentStack.template Push<Ch>() = '/';
+ char buffer[21];
+ size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
+ for (size_t i = 0; i < length; i++)
+ *documentStack.template Push<Ch>() = buffer[i];
+ }
+};
+
+// Partial specialized version for char to prevent buffer copying.
+template <typename Stack>
+struct TokenHelper<Stack, char> {
+ RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
+ if (sizeof(SizeType) == 4) {
+ char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
+ *buffer++ = '/';
+ const char* end = internal::u32toa(index, buffer);
+ documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
+ }
+ else {
+ char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
+ *buffer++ = '/';
+ const char* end = internal::u64toa(index, buffer);
+ documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
+ }
+ }
+};
+
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+// IGenericRemoteSchemaDocumentProvider
+
+template <typename SchemaDocumentType>
+class IGenericRemoteSchemaDocumentProvider {
+public:
+ typedef typename SchemaDocumentType::Ch Ch;
+
+ virtual ~IGenericRemoteSchemaDocumentProvider() {}
+ virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaDocument
+
+//! JSON schema document.
+/*!
+ A JSON schema document is a compiled version of a JSON schema.
+ It is basically a tree of internal::Schema.
+
+ \note This is an immutable class (i.e. its instance cannot be modified after construction).
+ \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
+ \tparam Allocator Allocator type for allocating memory of this document.
+*/
+template <typename ValueT, typename Allocator = CrtAllocator>
+class GenericSchemaDocument {
+public:
+ typedef ValueT ValueType;
+ typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
+ typedef Allocator AllocatorType;
+ typedef typename ValueType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+ typedef internal::Schema<GenericSchemaDocument> SchemaType;
+ typedef GenericPointer<ValueType, Allocator> PointerType;
+ friend class internal::Schema<GenericSchemaDocument>;
+ template <typename, typename, typename>
+ friend class GenericSchemaValidator;
+
+ //! Constructor.
+ /*!
+ Compile a JSON document into schema document.
+
+ \param document A JSON document as source.
+ \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
+ \param allocator An optional allocator instance for allocating memory. Can be null.
+ */
+ explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
+ remoteProvider_(remoteProvider),
+ allocator_(allocator),
+ ownAllocator_(),
+ root_(),
+ schemaMap_(allocator, kInitialSchemaMapSize),
+ schemaRef_(allocator, kInitialSchemaRefSize)
+ {
+ if (!allocator_)
+ ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
+
+ // Generate root schema, it will call CreateSchema() to create sub-schemas,
+ // And call AddRefSchema() if there are $ref.
+ CreateSchemaRecursive(&root_, PointerType(), document, document);
+
+ // Resolve $ref
+ while (!schemaRef_.Empty()) {
+ SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
+ if (const SchemaType* s = GetSchema(refEntry->target)) {
+ if (refEntry->schema)
+ *refEntry->schema = s;
+
+ // Create entry in map if not exist
+ if (!GetSchema(refEntry->source)) {
+ new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
+ }
+ }
+ refEntry->~SchemaRefEntry();
+ }
+
+ RAPIDJSON_ASSERT(root_ != 0);
+
+ schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ //! Move constructor in C++11
+ GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
+ remoteProvider_(rhs.remoteProvider_),
+ allocator_(rhs.allocator_),
+ ownAllocator_(rhs.ownAllocator_),
+ root_(rhs.root_),
+ schemaMap_(std::move(rhs.schemaMap_)),
+ schemaRef_(std::move(rhs.schemaRef_))
+ {
+ rhs.remoteProvider_ = 0;
+ rhs.allocator_ = 0;
+ rhs.ownAllocator_ = 0;
+ }
+#endif
+
+ //! Destructor
+ ~GenericSchemaDocument() {
+ while (!schemaMap_.Empty())
+ schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
+
+ RAPIDJSON_DELETE(ownAllocator_);
+ }
+
+ //! Get the root schema.
+ const SchemaType& GetRoot() const { return *root_; }
+
+private:
+ //! Prohibit copying
+ GenericSchemaDocument(const GenericSchemaDocument&);
+ //! Prohibit assignment
+ GenericSchemaDocument& operator=(const GenericSchemaDocument&);
+
+ struct SchemaRefEntry {
+ SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
+ PointerType source;
+ PointerType target;
+ const SchemaType** schema;
+ };
+
+ struct SchemaEntry {
+ SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
+ ~SchemaEntry() {
+ if (owned) {
+ schema->~SchemaType();
+ Allocator::Free(schema);
+ }
+ }
+ PointerType pointer;
+ SchemaType* schema;
+ bool owned;
+ };
+
+ void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+ if (schema)
+ *schema = SchemaType::GetTypeless();
+
+ if (v.GetType() == kObjectType) {
+ const SchemaType* s = GetSchema(pointer);
+ if (!s)
+ CreateSchema(schema, pointer, v, document);
+
+ for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
+ CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
+ }
+ else if (v.GetType() == kArrayType)
+ for (SizeType i = 0; i < v.Size(); i++)
+ CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
+ }
+
+ void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
+ RAPIDJSON_ASSERT(pointer.IsValid());
+ if (v.IsObject()) {
+ if (!HandleRefSchema(pointer, schema, v, document)) {
+ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
+ new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
+ if (schema)
+ *schema = s;
+ }
+ }
+ }
+
+ bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
+ static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
+ static const ValueType kRefValue(kRefString, 4);
+
+ typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
+ if (itr == v.MemberEnd())
+ return false;
+
+ if (itr->value.IsString()) {
+ SizeType len = itr->value.GetStringLength();
+ if (len > 0) {
+ const Ch* s = itr->value.GetString();
+ SizeType i = 0;
+ while (i < len && s[i] != '#') // Find the first #
+ i++;
+
+ if (i > 0) { // Remote reference, resolve immediately
+ if (remoteProvider_) {
+ if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
+ PointerType pointer(&s[i], len - i, allocator_);
+ if (pointer.IsValid()) {
+ if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
+ if (schema)
+ *schema = sc;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ else if (s[i] == '#') { // Local reference, defer resolution
+ PointerType pointer(&s[i], len - i, allocator_);
+ if (pointer.IsValid()) {
+ if (const ValueType* nv = pointer.Get(document))
+ if (HandleRefSchema(source, schema, *nv, document))
+ return true;
+
+ new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ const SchemaType* GetSchema(const PointerType& pointer) const {
+ for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+ if (pointer == target->pointer)
+ return target->schema;
+ return 0;
+ }
+
+ PointerType GetPointer(const SchemaType* schema) const {
+ for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
+ if (schema == target->schema)
+ return target->pointer;
+ return PointerType();
+ }
+
+ static const size_t kInitialSchemaMapSize = 64;
+ static const size_t kInitialSchemaRefSize = 64;
+
+ IRemoteSchemaDocumentProviderType* remoteProvider_;
+ Allocator *allocator_;
+ Allocator *ownAllocator_;
+ const SchemaType* root_; //!< Root schema.
+ internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
+ internal::Stack<Allocator> schemaRef_; // Stores Pointer from $ref and schema which holds the $ref
+};
+
+//! GenericSchemaDocument using Value type.
+typedef GenericSchemaDocument<Value> SchemaDocument;
+//! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
+typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
+
+///////////////////////////////////////////////////////////////////////////////
+// GenericSchemaValidator
+
+//! JSON Schema Validator.
+/*!
+ A SAX style JSON schema validator.
+ It uses a \c GenericSchemaDocument to validate SAX events.
+ It delegates the incoming SAX events to an output handler.
+ The default output handler does nothing.
+ It can be reused multiple times by calling \c Reset().
+
+ \tparam SchemaDocumentType Type of schema document.
+ \tparam OutputHandler Type of output handler. Default handler does nothing.
+ \tparam StateAllocator Allocator for storing the internal validation states.
+*/
+template <
+ typename SchemaDocumentType,
+ typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
+ typename StateAllocator = CrtAllocator>
+class GenericSchemaValidator :
+ public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
+ public internal::ISchemaValidator
+{
+public:
+ typedef typename SchemaDocumentType::SchemaType SchemaType;
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename SchemaType::EncodingType EncodingType;
+ typedef typename EncodingType::Ch Ch;
+
+ //! Constructor without output handler.
+ /*!
+ \param schemaDocument The schema document to conform to.
+ \param allocator Optional allocator for storing internal validation states.
+ \param schemaStackCapacity Optional initial capacity of schema path stack.
+ \param documentStackCapacity Optional initial capacity of document path stack.
+ */
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(schemaDocument.GetRoot()),
+ outputHandler_(GetNullHandler()),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(0)
+#endif
+ {
+ }
+
+ //! Constructor with output handler.
+ /*!
+ \param schemaDocument The schema document to conform to.
+ \param allocator Optional allocator for storing internal validation states.
+ \param schemaStackCapacity Optional initial capacity of schema path stack.
+ \param documentStackCapacity Optional initial capacity of document path stack.
+ */
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ OutputHandler& outputHandler,
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(schemaDocument.GetRoot()),
+ outputHandler_(outputHandler),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(0)
+#endif
+ {
+ }
+
+ //! Destructor.
+ ~GenericSchemaValidator() {
+ Reset();
+ RAPIDJSON_DELETE(ownStateAllocator_);
+ }
+
+ //! Reset the internal states.
+ void Reset() {
+ while (!schemaStack_.Empty())
+ PopSchema();
+ documentStack_.Clear();
+ valid_ = true;
+ }
+
+ //! Checks whether the current state is valid.
+ // Implementation of ISchemaValidator
+ virtual bool IsValid() const { return valid_; }
+
+ //! Gets the JSON pointer pointed to the invalid schema.
+ PointerType GetInvalidSchemaPointer() const {
+ return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
+ }
+
+ //! Gets the keyword of invalid schema.
+ const Ch* GetInvalidSchemaKeyword() const {
+ return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
+ }
+
+ //! Gets the JSON pointer pointed to the invalid value.
+ PointerType GetInvalidDocumentPointer() const {
+ return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
+ }
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
+RAPIDJSON_MULTILINEMACRO_BEGIN\
+ *documentStack_.template Push<Ch>() = '\0';\
+ documentStack_.template Pop<Ch>(1);\
+ internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
+RAPIDJSON_MULTILINEMACRO_END
+#else
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
+#endif
+
+#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
+ if (!valid_) return false; \
+ if (!BeginValue() || !CurrentSchema().method arg1) {\
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
+ return valid_ = false;\
+ }
+
+#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
+ for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
+ if (context->hasher)\
+ static_cast<HasherType*>(context->hasher)->method arg2;\
+ if (context->validators)\
+ for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
+ static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
+ if (context->patternPropertiesValidators)\
+ for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
+ static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
+ }
+
+#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
+ return valid_ = EndValue() && outputHandler_.method arg2
+
+#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
+ RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
+
+ bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); }
+ bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
+ bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
+ bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
+ bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
+ bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
+ bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
+ bool RawNumber(const Ch* str, SizeType length, bool copy)
+ { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+ bool String(const Ch* str, SizeType length, bool copy)
+ { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
+
+ bool StartObject() {
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
+ return valid_ = outputHandler_.StartObject();
+ }
+
+ bool Key(const Ch* str, SizeType len, bool copy) {
+ if (!valid_) return false;
+ AppendToken(str, len);
+ if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
+ return valid_ = outputHandler_.Key(str, len, copy);
+ }
+
+ bool EndObject(SizeType memberCount) {
+ if (!valid_) return false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
+ if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
+ }
+
+ bool StartArray() {
+ RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
+ return valid_ = outputHandler_.StartArray();
+ }
+
+ bool EndArray(SizeType elementCount) {
+ if (!valid_) return false;
+ RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
+ if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
+ RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
+ }
+
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
+#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
+#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
+#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
+
+ // Implementation of ISchemaStateFactory<SchemaType>
+ virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
+ return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+ depth_ + 1,
+#endif
+ &GetStateAllocator());
+ }
+
+ virtual void DestroySchemaValidator(ISchemaValidator* validator) {
+ GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
+ v->~GenericSchemaValidator();
+ StateAllocator::Free(v);
+ }
+
+ virtual void* CreateHasher() {
+ return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
+ }
+
+ virtual uint64_t GetHashCode(void* hasher) {
+ return static_cast<HasherType*>(hasher)->GetHashCode();
+ }
+
+ virtual void DestroryHasher(void* hasher) {
+ HasherType* h = static_cast<HasherType*>(hasher);
+ h->~HasherType();
+ StateAllocator::Free(h);
+ }
+
+ virtual void* MallocState(size_t size) {
+ return GetStateAllocator().Malloc(size);
+ }
+
+ virtual void FreeState(void* p) {
+ return StateAllocator::Free(p);
+ }
+
+private:
+ typedef typename SchemaType::Context Context;
+ typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
+ typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
+
+ GenericSchemaValidator(
+ const SchemaDocumentType& schemaDocument,
+ const SchemaType& root,
+#if RAPIDJSON_SCHEMA_VERBOSE
+ unsigned depth,
+#endif
+ StateAllocator* allocator = 0,
+ size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
+ size_t documentStackCapacity = kDefaultDocumentStackCapacity)
+ :
+ schemaDocument_(&schemaDocument),
+ root_(root),
+ outputHandler_(GetNullHandler()),
+ stateAllocator_(allocator),
+ ownStateAllocator_(0),
+ schemaStack_(allocator, schemaStackCapacity),
+ documentStack_(allocator, documentStackCapacity),
+ valid_(true)
+#if RAPIDJSON_SCHEMA_VERBOSE
+ , depth_(depth)
+#endif
+ {
+ }
+
+ StateAllocator& GetStateAllocator() {
+ if (!stateAllocator_)
+ stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator());
+ return *stateAllocator_;
+ }
+
+ bool BeginValue() {
+ if (schemaStack_.Empty())
+ PushSchema(root_);
+ else {
+ if (CurrentContext().inArray)
+ internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
+
+ if (!CurrentSchema().BeginValue(CurrentContext()))
+ return false;
+
+ SizeType count = CurrentContext().patternPropertiesSchemaCount;
+ const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
+ typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
+ bool valueUniqueness = CurrentContext().valueUniqueness;
+ if (CurrentContext().valueSchema)
+ PushSchema(*CurrentContext().valueSchema);
+
+ if (count > 0) {
+ CurrentContext().objectPatternValidatorType = patternValidatorType;
+ ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
+ SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
+ va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
+ for (SizeType i = 0; i < count; i++)
+ va[validatorCount++] = CreateSchemaValidator(*sa[i]);
+ }
+
+ CurrentContext().arrayUniqueness = valueUniqueness;
+ }
+ return true;
+ }
+
+ bool EndValue() {
+ if (!CurrentSchema().EndValue(CurrentContext()))
+ return false;
+
+#if RAPIDJSON_SCHEMA_VERBOSE
+ GenericStringBuffer<EncodingType> sb;
+ schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
+
+ *documentStack_.template Push<Ch>() = '\0';
+ documentStack_.template Pop<Ch>(1);
+ internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
+#endif
+
+ uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
+
+ PopSchema();
+
+ if (!schemaStack_.Empty()) {
+ Context& context = CurrentContext();
+ if (context.valueUniqueness) {
+ HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
+ if (!a)
+ CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
+ for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
+ if (itr->GetUint64() == h)
+ RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
+ a->PushBack(h, GetStateAllocator());
+ }
+ }
+
+ // Remove the last token of document pointer
+ while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
+ ;
+
+ return true;
+ }
+
+ void AppendToken(const Ch* str, SizeType len) {
+ documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
+ *documentStack_.template PushUnsafe<Ch>() = '/';
+ for (SizeType i = 0; i < len; i++) {
+ if (str[i] == '~') {
+ *documentStack_.template PushUnsafe<Ch>() = '~';
+ *documentStack_.template PushUnsafe<Ch>() = '0';
+ }
+ else if (str[i] == '/') {
+ *documentStack_.template PushUnsafe<Ch>() = '~';
+ *documentStack_.template PushUnsafe<Ch>() = '1';
+ }
+ else
+ *documentStack_.template PushUnsafe<Ch>() = str[i];
+ }
+ }
+
+ RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
+
+ RAPIDJSON_FORCEINLINE void PopSchema() {
+ Context* c = schemaStack_.template Pop<Context>(1);
+ if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
+ a->~HashCodeArray();
+ StateAllocator::Free(a);
+ }
+ c->~Context();
+ }
+
+ const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
+ Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
+ const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
+
+ static OutputHandler& GetNullHandler() {
+ static OutputHandler nullHandler;
+ return nullHandler;
+ }
+
+ static const size_t kDefaultSchemaStackCapacity = 1024;
+ static const size_t kDefaultDocumentStackCapacity = 256;
+ const SchemaDocumentType* schemaDocument_;
+ const SchemaType& root_;
+ OutputHandler& outputHandler_;
+ StateAllocator* stateAllocator_;
+ StateAllocator* ownStateAllocator_;
+ internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
+ internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
+ bool valid_;
+#if RAPIDJSON_SCHEMA_VERBOSE
+ unsigned depth_;
+#endif
+};
+
+typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
+
+///////////////////////////////////////////////////////////////////////////////
+// SchemaValidatingReader
+
+//! A helper class for parsing with validation.
+/*!
+ This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
+
+ \tparam parseFlags Combination of \ref ParseFlag.
+ \tparam InputStream Type of input stream, implementing Stream concept.
+ \tparam SourceEncoding Encoding of the input stream.
+ \tparam SchemaDocumentType Type of schema document.
+ \tparam StackAllocator Allocator type for stack.
+*/
+template <
+ unsigned parseFlags,
+ typename InputStream,
+ typename SourceEncoding,
+ typename SchemaDocumentType = SchemaDocument,
+ typename StackAllocator = CrtAllocator>
+class SchemaValidatingReader {
+public:
+ typedef typename SchemaDocumentType::PointerType PointerType;
+ typedef typename InputStream::Ch Ch;
+
+ //! Constructor
+ /*!
+ \param is Input stream.
+ \param sd Schema document.
+ */
+ SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
+
+ template <typename Handler>
+ bool operator()(Handler& handler) {
+ GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
+ GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
+ parseResult_ = reader.template Parse<parseFlags>(is_, validator);
+
+ isValid_ = validator.IsValid();
+ if (isValid_) {
+ invalidSchemaPointer_ = PointerType();
+ invalidSchemaKeyword_ = 0;
+ invalidDocumentPointer_ = PointerType();
+ }
+ else {
+ invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
+ invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
+ invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
+ }
+
+ return parseResult_;
+ }
+
+ const ParseResult& GetParseResult() const { return parseResult_; }
+ bool IsValid() const { return isValid_; }
+ const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
+ const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
+ const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
+
+private:
+ InputStream& is_;
+ const SchemaDocumentType& sd_;
+
+ ParseResult parseResult_;
+ PointerType invalidSchemaPointer_;
+ const Ch* invalidSchemaKeyword_;
+ PointerType invalidDocumentPointer_;
+ bool isValid_;
+};
+
+RAPIDJSON_NAMESPACE_END
+RAPIDJSON_DIAG_POP
+
+#endif // RAPIDJSON_SCHEMA_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/stream.h b/ext/librethinkdbxx/src/rapidjson/stream.h
new file mode 100644
index 00000000..fef82c25
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/stream.h
@@ -0,0 +1,179 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#include "rapidjson.h"
+
+#ifndef RAPIDJSON_STREAM_H_
+#define RAPIDJSON_STREAM_H_
+
+#include "encodings.h"
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Stream
+
+/*! \class rapidjson::Stream
+ \brief Concept for reading and writing characters.
+
+ For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
+
+ For write-only stream, only need to implement Put() and Flush().
+
+\code
+concept Stream {
+ typename Ch; //!< Character type of the stream.
+
+ //! Read the current character from stream without moving the read cursor.
+ Ch Peek() const;
+
+ //! Read the current character from stream and moving the read cursor to next character.
+ Ch Take();
+
+ //! Get the current read cursor.
+ //! \return Number of characters read from start.
+ size_t Tell();
+
+ //! Begin writing operation at the current read pointer.
+ //! \return The begin writer pointer.
+ Ch* PutBegin();
+
+ //! Write a character.
+ void Put(Ch c);
+
+ //! Flush the buffer.
+ void Flush();
+
+ //! End the writing operation.
+ //! \param begin The begin write pointer returned by PutBegin().
+ //! \return Number of characters written.
+ size_t PutEnd(Ch* begin);
+}
+\endcode
+*/
+
+//! Provides additional information for stream.
+/*!
+ By using traits pattern, this type provides a default configuration for stream.
+ For custom stream, this type can be specialized for other configuration.
+ See TEST(Reader, CustomStringStream) in readertest.cpp for example.
+*/
+template<typename Stream>
+struct StreamTraits {
+ //! Whether to make local copy of stream for optimization during parsing.
+ /*!
+ By default, for safety, streams do not use local copy optimization.
+ Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
+ */
+ enum { copyOptimization = 0 };
+};
+
+//! Reserve n characters for writing to a stream.
+template<typename Stream>
+inline void PutReserve(Stream& stream, size_t count) {
+ (void)stream;
+ (void)count;
+}
+
+//! Write character to a stream, presuming buffer is reserved.
+template<typename Stream>
+inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
+ stream.Put(c);
+}
+
+//! Put N copies of a character to a stream.
+template<typename Stream, typename Ch>
+inline void PutN(Stream& stream, Ch c, size_t n) {
+ PutReserve(stream, n);
+ for (size_t i = 0; i < n; i++)
+ PutUnsafe(stream, c);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// StringStream
+
+//! Read-only string stream.
+/*! \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericStringStream {
+ typedef typename Encoding::Ch Ch;
+
+ GenericStringStream(const Ch *src) : src_(src), head_(src) {}
+
+ Ch Peek() const { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() const { return static_cast<size_t>(src_ - head_); }
+
+ Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
+ void Put(Ch) { RAPIDJSON_ASSERT(false); }
+ void Flush() { RAPIDJSON_ASSERT(false); }
+ size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
+
+ const Ch* src_; //!< Current read position.
+ const Ch* head_; //!< Original head of the string.
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+//! String stream with UTF8 encoding.
+typedef GenericStringStream<UTF8<> > StringStream;
+
+///////////////////////////////////////////////////////////////////////////////
+// InsituStringStream
+
+//! A read-write string stream.
+/*! This string stream is particularly designed for in-situ parsing.
+ \note implements Stream concept
+*/
+template <typename Encoding>
+struct GenericInsituStringStream {
+ typedef typename Encoding::Ch Ch;
+
+ GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
+
+ // Read
+ Ch Peek() { return *src_; }
+ Ch Take() { return *src_++; }
+ size_t Tell() { return static_cast<size_t>(src_ - head_); }
+
+ // Write
+ void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
+
+ Ch* PutBegin() { return dst_ = src_; }
+ size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
+ void Flush() {}
+
+ Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
+ void Pop(size_t count) { dst_ -= count; }
+
+ Ch* src_;
+ Ch* dst_;
+ Ch* head_;
+};
+
+template <typename Encoding>
+struct StreamTraits<GenericInsituStringStream<Encoding> > {
+ enum { copyOptimization = 1 };
+};
+
+//! Insitu string stream with UTF8 encoding.
+typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_STREAM_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/stringbuffer.h b/ext/librethinkdbxx/src/rapidjson/stringbuffer.h
new file mode 100644
index 00000000..78f34d20
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/stringbuffer.h
@@ -0,0 +1,117 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_STRINGBUFFER_H_
+#define RAPIDJSON_STRINGBUFFER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+#include <utility> // std::move
+#endif
+
+#include "internal/stack.h"
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+//! Represents an in-memory output stream.
+/*!
+ \tparam Encoding Encoding of the stream.
+ \tparam Allocator type for allocating memory buffer.
+ \note implements Stream concept
+*/
+template <typename Encoding, typename Allocator = CrtAllocator>
+class GenericStringBuffer {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
+ GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
+ if (&rhs != this)
+ stack_ = std::move(rhs.stack_);
+ return *this;
+ }
+#endif
+
+ void Put(Ch c) { *stack_.template Push<Ch>() = c; }
+ void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
+ void Flush() {}
+
+ void Clear() { stack_.Clear(); }
+ void ShrinkToFit() {
+ // Push and pop a null terminator. This is safe.
+ *stack_.template Push<Ch>() = '\0';
+ stack_.ShrinkToFit();
+ stack_.template Pop<Ch>(1);
+ }
+
+ void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
+ Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
+ Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
+ void Pop(size_t count) { stack_.template Pop<Ch>(count); }
+
+ const Ch* GetString() const {
+ // Push and pop a null terminator. This is safe.
+ *stack_.template Push<Ch>() = '\0';
+ stack_.template Pop<Ch>(1);
+
+ return stack_.template Bottom<Ch>();
+ }
+
+ size_t GetSize() const { return stack_.GetSize(); }
+
+ static const size_t kDefaultCapacity = 256;
+ mutable internal::Stack<Allocator> stack_;
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ GenericStringBuffer(const GenericStringBuffer&);
+ GenericStringBuffer& operator=(const GenericStringBuffer&);
+};
+
+//! String buffer with UTF8 encoding
+typedef GenericStringBuffer<UTF8<> > StringBuffer;
+
+template<typename Encoding, typename Allocator>
+inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
+ stream.Reserve(count);
+}
+
+template<typename Encoding, typename Allocator>
+inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
+ stream.PutUnsafe(c);
+}
+
+//! Implement specialized version of PutN() with memset() for better performance.
+template<>
+inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
+ std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
+}
+
+RAPIDJSON_NAMESPACE_END
+
+#if defined(__clang__)
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_STRINGBUFFER_H_
diff --git a/ext/librethinkdbxx/src/rapidjson/writer.h b/ext/librethinkdbxx/src/rapidjson/writer.h
new file mode 100644
index 00000000..7d0610eb
--- /dev/null
+++ b/ext/librethinkdbxx/src/rapidjson/writer.h
@@ -0,0 +1,609 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// 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.
+
+#ifndef RAPIDJSON_WRITER_H_
+#define RAPIDJSON_WRITER_H_
+
+#include "stream.h"
+#include "internal/stack.h"
+#include "internal/strfunc.h"
+#include "internal/dtoa.h"
+#include "internal/itoa.h"
+#include "stringbuffer.h"
+#include <new> // placement new
+
+#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+#endif
+#ifdef RAPIDJSON_SSE42
+#include <nmmintrin.h>
+#elif defined(RAPIDJSON_SSE2)
+#include <emmintrin.h>
+#endif
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(unreachable-code)
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// WriteFlag
+
+/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kWriteDefaultFlags definition.
+
+ User can define this as any \c WriteFlag combinations.
+*/
+#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
+#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
+#endif
+
+//! Combination of writeFlags
+enum WriteFlag {
+ kWriteNoFlags = 0, //!< No flags are set.
+ kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
+ kWriteNanAndInfFlag = 2, //!< Allow writing of Inf, -Inf and NaN.
+ kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
+};
+
+//! JSON writer
+/*! Writer implements the concept Handler.
+ It generates JSON text by events to an output os.
+
+ User may programmatically calls the functions of a writer to generate JSON text.
+
+ On the other side, a writer can also be passed to objects that generates events,
+
+ for example Reader::Parse() and Document::Accept().
+
+ \tparam OutputStream Type of output stream.
+ \tparam SourceEncoding Encoding of source string.
+ \tparam TargetEncoding Encoding of output stream.
+ \tparam StackAllocator Type of allocator for allocating memory of stack.
+ \note implements Handler concept
+*/
+template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
+class Writer {
+public:
+ typedef typename SourceEncoding::Ch Ch;
+
+ static const int kDefaultMaxDecimalPlaces = 324;
+
+ //! Constructor
+ /*! \param os Output stream.
+ \param stackAllocator User supplied allocator. If it is null, it will create a private one.
+ \param levelDepth Initial capacity of stack.
+ */
+ explicit
+ Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
+ os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
+
+ explicit
+ Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
+ os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
+
+ //! Reset the writer with a new stream.
+ /*!
+ This function reset the writer with a new stream and default settings,
+ in order to make a Writer object reusable for output multiple JSONs.
+
+ \param os New output stream.
+ \code
+ Writer<OutputStream> writer(os1);
+ writer.StartObject();
+ // ...
+ writer.EndObject();
+
+ writer.Reset(os2);
+ writer.StartObject();
+ // ...
+ writer.EndObject();
+ \endcode
+ */
+ void Reset(OutputStream& os) {
+ os_ = &os;
+ hasRoot_ = false;
+ level_stack_.Clear();
+ }
+
+ //! Checks whether the output is a complete JSON.
+ /*!
+ A complete JSON has a complete root object or array.
+ */
+ bool IsComplete() const {
+ return hasRoot_ && level_stack_.Empty();
+ }
+
+ int GetMaxDecimalPlaces() const {
+ return maxDecimalPlaces_;
+ }
+
+ //! Sets the maximum number of decimal places for double output.
+ /*!
+ This setting truncates the output with specified number of decimal places.
+
+ For example,
+
+ \code
+ writer.SetMaxDecimalPlaces(3);
+ writer.StartArray();
+ writer.Double(0.12345); // "0.123"
+ writer.Double(0.0001); // "0.0"
+ writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent)
+ writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent)
+ writer.EndArray();
+ \endcode
+
+ The default setting does not truncate any decimal places. You can restore to this setting by calling
+ \code
+ writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
+ \endcode
+ */
+ void SetMaxDecimalPlaces(int maxDecimalPlaces) {
+ maxDecimalPlaces_ = maxDecimalPlaces;
+ }
+
+ /*!@name Implementation of Handler
+ \see Handler
+ */
+ //@{
+
+ bool Null() { Prefix(kNullType); return WriteNull(); }
+ bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
+ bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
+ bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
+ bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
+ bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
+
+ //! Writes the given \c double value to the stream
+ /*!
+ \param d The value to be written.
+ \return Whether it is succeed.
+ */
+ bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
+
+ bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ Prefix(kNumberType);
+ return WriteString(str, length);
+ }
+
+ bool String(const Ch* str, SizeType length, bool copy = false) {
+ (void)copy;
+ Prefix(kStringType);
+ return WriteString(str, length);
+ }
+
+#if RAPIDJSON_HAS_STDSTRING
+ bool String(const std::basic_string<Ch>& str) {
+ return String(str.data(), SizeType(str.size()));
+ }
+#endif
+
+ bool StartObject() {
+ Prefix(kObjectType);
+ new (level_stack_.template Push<Level>()) Level(false);
+ return WriteStartObject();
+ }
+
+ bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
+
+ bool EndObject(SizeType memberCount = 0) {
+ (void)memberCount;
+ RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
+ RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
+ level_stack_.template Pop<Level>(1);
+ bool ret = WriteEndObject();
+ if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
+ os_->Flush();
+ return ret;
+ }
+
+ bool StartArray() {
+ Prefix(kArrayType);
+ new (level_stack_.template Push<Level>()) Level(true);
+ return WriteStartArray();
+ }
+
+ bool EndArray(SizeType elementCount = 0) {
+ (void)elementCount;
+ RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
+ RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
+ level_stack_.template Pop<Level>(1);
+ bool ret = WriteEndArray();
+ if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
+ os_->Flush();
+ return ret;
+ }
+ //@}
+
+ /*! @name Convenience extensions */
+ //@{
+
+ //! Simpler but slower overload.
+ bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
+ bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
+
+ //@}
+
+ //! Write a raw JSON value.
+ /*!
+ For user to write a stringified JSON as a value.
+
+ \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
+ \param length Length of the json.
+ \param type Type of the root of json.
+ */
+ bool RawValue(const Ch* json, size_t length, Type type) { Prefix(type); return WriteRawValue(json, length); }
+
+protected:
+ //! Information for each nested level
+ struct Level {
+ Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
+ size_t valueCount; //!< number of values in this level
+ bool inArray; //!< true if in array, otherwise in object
+ };
+
+ static const size_t kDefaultLevelDepth = 32;
+
+ bool WriteNull() {
+ PutReserve(*os_, 4);
+ PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
+ }
+
+ bool WriteBool(bool b) {
+ if (b) {
+ PutReserve(*os_, 4);
+ PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
+ }
+ else {
+ PutReserve(*os_, 5);
+ PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
+ }
+ return true;
+ }
+
+ bool WriteInt(int i) {
+ char buffer[11];
+ const char* end = internal::i32toa(i, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (const char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteUint(unsigned u) {
+ char buffer[10];
+ const char* end = internal::u32toa(u, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (const char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteInt64(int64_t i64) {
+ char buffer[21];
+ const char* end = internal::i64toa(i64, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (const char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteUint64(uint64_t u64) {
+ char buffer[20];
+ char* end = internal::u64toa(u64, buffer);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteDouble(double d) {
+ if (internal::Double(d).IsNanOrInf()) {
+ if (!(writeFlags & kWriteNanAndInfFlag))
+ return false;
+ if (internal::Double(d).IsNan()) {
+ PutReserve(*os_, 3);
+ PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
+ return true;
+ }
+ if (internal::Double(d).Sign()) {
+ PutReserve(*os_, 9);
+ PutUnsafe(*os_, '-');
+ }
+ else
+ PutReserve(*os_, 8);
+ PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
+ PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
+ return true;
+ }
+
+ char buffer[25];
+ char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+ PutReserve(*os_, static_cast<size_t>(end - buffer));
+ for (char* p = buffer; p != end; ++p)
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(*p));
+ return true;
+ }
+
+ bool WriteString(const Ch* str, SizeType length) {
+ static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ static const char escape[256] = {
+#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
+ 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
+ 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
+ Z16, Z16, // 30~4F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
+ Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
+#undef Z16
+ };
+
+ if (TargetEncoding::supportUnicode)
+ PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
+ else
+ PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..."
+
+ PutUnsafe(*os_, '\"');
+ GenericStringStream<SourceEncoding> is(str);
+ while (ScanWriteUnescapedString(is, length)) {
+ const Ch c = is.Peek();
+ if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
+ // Unicode escaping
+ unsigned codepoint;
+ if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
+ return false;
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, 'u');
+ if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
+ PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(codepoint ) & 15]);
+ }
+ else {
+ RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
+ // Surrogate pair
+ unsigned s = codepoint - 0x010000;
+ unsigned lead = (s >> 10) + 0xD800;
+ unsigned trail = (s & 0x3FF) + 0xDC00;
+ PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(lead ) & 15]);
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, 'u');
+ PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]);
+ PutUnsafe(*os_, hexDigits[(trail ) & 15]);
+ }
+ }
+ else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
+ is.Take();
+ PutUnsafe(*os_, '\\');
+ PutUnsafe(*os_, static_cast<typename TargetEncoding::Ch>(escape[static_cast<unsigned char>(c)]));
+ if (escape[static_cast<unsigned char>(c)] == 'u') {
+ PutUnsafe(*os_, '0');
+ PutUnsafe(*os_, '0');
+ PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
+ PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
+ }
+ }
+ else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
+ Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
+ Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
+ return false;
+ }
+ PutUnsafe(*os_, '\"');
+ return true;
+ }
+
+ bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+ }
+
+ bool WriteStartObject() { os_->Put('{'); return true; }
+ bool WriteEndObject() { os_->Put('}'); return true; }
+ bool WriteStartArray() { os_->Put('['); return true; }
+ bool WriteEndArray() { os_->Put(']'); return true; }
+
+ bool WriteRawValue(const Ch* json, size_t length) {
+ PutReserve(*os_, length);
+ for (size_t i = 0; i < length; i++) {
+ RAPIDJSON_ASSERT(json[i] != '\0');
+ PutUnsafe(*os_, json[i]);
+ }
+ return true;
+ }
+
+ void Prefix(Type type) {
+ (void)type;
+ if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
+ Level* level = level_stack_.template Top<Level>();
+ if (level->valueCount > 0) {
+ if (level->inArray)
+ os_->Put(','); // add comma if it is not the first element in array
+ else // in object
+ os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
+ }
+ if (!level->inArray && level->valueCount % 2 == 0)
+ RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
+ level->valueCount++;
+ }
+ else {
+ RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
+ hasRoot_ = true;
+ }
+ }
+
+ OutputStream* os_;
+ internal::Stack<StackAllocator> level_stack_;
+ int maxDecimalPlaces_;
+ bool hasRoot_;
+
+private:
+ // Prohibit copy constructor & assignment operator.
+ Writer(const Writer&);
+ Writer& operator=(const Writer&);
+};
+
+// Full specialization for StringStream to prevent memory copying
+
+template<>
+inline bool Writer<StringBuffer>::WriteInt(int i) {
+ char *buffer = os_->Push(11);
+ const char* end = internal::i32toa(i, buffer);
+ os_->Pop(static_cast<size_t>(11 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
+ char *buffer = os_->Push(10);
+ const char* end = internal::u32toa(u, buffer);
+ os_->Pop(static_cast<size_t>(10 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
+ char *buffer = os_->Push(21);
+ const char* end = internal::i64toa(i64, buffer);
+ os_->Pop(static_cast<size_t>(21 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
+ char *buffer = os_->Push(20);
+ const char* end = internal::u64toa(u, buffer);
+ os_->Pop(static_cast<size_t>(20 - (end - buffer)));
+ return true;
+}
+
+template<>
+inline bool Writer<StringBuffer>::WriteDouble(double d) {
+ if (internal::Double(d).IsNanOrInf()) {
+ // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
+ if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
+ return false;
+ if (internal::Double(d).IsNan()) {
+ PutReserve(*os_, 3);
+ PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
+ return true;
+ }
+ if (internal::Double(d).Sign()) {
+ PutReserve(*os_, 9);
+ PutUnsafe(*os_, '-');
+ }
+ else
+ PutReserve(*os_, 8);
+ PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
+ PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
+ return true;
+ }
+
+ char *buffer = os_->Push(25);
+ char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
+ os_->Pop(static_cast<size_t>(25 - (end - buffer)));
+ return true;
+}
+
+#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+template<>
+inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
+ if (length < 16)
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+
+ if (!RAPIDJSON_LIKELY(is.Tell() < length))
+ return false;
+
+ const char* p = is.src_;
+ const char* end = is.head_ + length;
+ const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
+ const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
+ if (nextAligned > end)
+ return true;
+
+ while (p != nextAligned)
+ if (*p < 0x20 || *p == '\"' || *p == '\\') {
+ is.src_ = p;
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+ }
+ else
+ os_->PutUnsafe(*p++);
+
+ // The rest of string using SIMD
+ static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
+ static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
+ static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 };
+ const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
+ const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
+ const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
+
+ for (; p != endAligned; p += 16) {
+ const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
+ const __m128i t1 = _mm_cmpeq_epi8(s, dq);
+ const __m128i t2 = _mm_cmpeq_epi8(s, bs);
+ const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19
+ const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
+ unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
+ if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
+ SizeType len;
+#ifdef _MSC_VER // Find the index of first escaped
+ unsigned long offset;
+ _BitScanForward(&offset, r);
+ len = offset;
+#else
+ len = static_cast<SizeType>(__builtin_ffs(r) - 1);
+#endif
+ char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
+ for (size_t i = 0; i < len; i++)
+ q[i] = p[i];
+
+ p += len;
+ break;
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
+ }
+
+ is.src_ = p;
+ return RAPIDJSON_LIKELY(is.Tell() < length);
+}
+#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
+
+RAPIDJSON_NAMESPACE_END
+
+#ifdef _MSC_VER
+RAPIDJSON_DIAG_POP
+#endif
+
+#ifdef __clang__
+RAPIDJSON_DIAG_POP
+#endif
+
+#endif // RAPIDJSON_RAPIDJSON_H_
diff --git a/ext/librethinkdbxx/src/term.cc b/ext/librethinkdbxx/src/term.cc
new file mode 100644
index 00000000..711ef27d
--- /dev/null
+++ b/ext/librethinkdbxx/src/term.cc
@@ -0,0 +1,285 @@
+#include <cstdlib>
+#include <set>
+
+#include "term.h"
+#include "json_p.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+struct {
+ Datum operator() (const Array& array) {
+ Array copy;
+ copy.reserve(array.size());
+ for (const auto& it : array) {
+ copy.emplace_back(it.apply<Datum>(*this));
+ }
+ return Datum(Array{TT::MAKE_ARRAY, std::move(copy)});
+ }
+ Datum operator() (const Object& object) {
+ Object copy;
+ for (const auto& it : object) {
+ copy.emplace(it.first, it.second.apply<Datum>(*this));
+ }
+ return std::move(copy);
+ }
+ template<class T>
+ Datum operator() (T&& atomic) {
+ return Datum(std::forward<T>(atomic));
+ }
+} datum_to_term;
+
+Term::Term(Datum&& datum_) : datum(datum_.apply<Datum>(datum_to_term)) { }
+Term::Term(const Datum& datum_) : datum(datum_.apply<Datum>(datum_to_term)) { }
+
+Term::Term(Term&& orig, OptArgs&& new_optargs) : datum(Nil()) {
+ Datum* cur = orig.datum.get_nth(2);
+ Object optargs;
+ free_vars = std::move(orig.free_vars);
+ if (cur) {
+ optargs = std::move(cur->extract_object());
+ }
+ for (auto& it : new_optargs) {
+ optargs.emplace(std::move(it.first), alpha_rename(std::move(it.second)));
+ }
+ datum = Array{ std::move(orig.datum.extract_nth(0)), std::move(orig.datum.extract_nth(1)), std::move(optargs) };
+}
+
+Term nil() {
+ return Term(Nil());
+}
+
+Cursor Term::run(Connection& conn, OptArgs&& opts) {
+ if (!free_vars.empty()) {
+ throw Error("run: term has free variables");
+ }
+
+ return conn.start_query(this, std::move(opts));
+}
+
+struct {
+ Datum operator() (Object&& object, const std::map<int, int>& subst, bool) {
+ Object ret;
+ for (auto& it : object) {
+ ret.emplace(std::move(it.first), std::move(it.second).apply<Datum>(*this, subst, false));
+ }
+ return ret;
+ }
+ Datum operator() (Array&& array, const std::map<int, int>& subst, bool args) {
+ if (!args) {
+ double cmd = array[0].extract_number();
+ if (cmd == static_cast<int>(TT::VAR)) {
+ double var = array[1].extract_nth(0).extract_number();
+ auto it = subst.find(static_cast<int>(var));
+ if (it != subst.end()) {
+ return Array{ TT::VAR, { it->second }};
+ }
+ }
+ if (array.size() == 2) {
+ return Array{ std::move(array[0]), std::move(array[1]).apply<Datum>(*this, subst, true) };
+ } else {
+ return Array{
+ std::move(array[0]),
+ std::move(array[1]).apply<Datum>(*this, subst, true),
+ std::move(array[2]).apply<Datum>(*this, subst, false) };
+ }
+ } else {
+ Array ret;
+ for (auto& it : array) {
+ ret.emplace_back(std::move(it).apply<Datum>(*this, subst, false));
+ }
+ return ret;
+ }
+ }
+ template <class T>
+ Datum operator() (T&& a, const std::map<int, int>&, bool) {
+ return std::move(a);
+ }
+} alpha_renamer;
+
+static int new_var_id(const std::map<int, int*>& vars) {
+ while (true) {
+ int id = gen_var_id();
+ if (vars.find(id) == vars.end()) {
+ return id;
+ }
+ }
+}
+
+Datum Term::alpha_rename(Term&& term) {
+ if (free_vars.empty()) {
+ free_vars = std::move(term.free_vars);
+ return std::move(term.datum);
+ }
+
+ std::map<int, int> subst;
+ for (auto it = term.free_vars.begin(); it != term.free_vars.end(); ++it) {
+ auto var = free_vars.find(it->first);
+ if (var == free_vars.end()) {
+ free_vars.emplace(it->first, it->second);
+ } else if (var->second != it->second) {
+ int id = new_var_id(free_vars);
+ subst.emplace(it->first, id);
+ free_vars.emplace(id, it->second);
+ }
+ }
+ if (subst.empty()) {
+ return std::move(term.datum);
+ } else {
+ return term.datum.apply<Datum>(alpha_renamer, subst, false);
+ }
+}
+
+int gen_var_id() {
+ return ::random() % (1<<30);
+}
+
+C0_IMPL(db_list, DB_LIST)
+C0_IMPL(table_list, TABLE_LIST)
+C0_IMPL(random, RANDOM)
+C0_IMPL(now, NOW)
+C0_IMPL(range, RANGE)
+C0_IMPL(error, ERROR)
+C0_IMPL(uuid, UUID)
+C0_IMPL(literal, LITERAL)
+CO0_IMPL(wait, WAIT)
+C0_IMPL(rebalance, REBALANCE)
+CO0_IMPL(random, RANDOM)
+
+Term row(TT::IMPLICIT_VAR, {});
+Term minval(TT::MINVAL, {});
+Term maxval(TT::MAXVAL, {});
+
+Term binary(const std::string& data) {
+ return expr(Binary(data));
+}
+
+Term binary(std::string&& data) {
+ return expr(Binary(data));
+}
+
+Term binary(const char* data) {
+ return expr(Binary(data));
+}
+
+struct {
+ bool operator() (const Object& object) {
+ for (const auto& it : object) {
+ if (it.second.apply<bool>(*this)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ bool operator() (const Array& array) {
+ int type = *array[0].get_number();
+ if (type == static_cast<int>(TT::IMPLICIT_VAR)) {
+ return true;
+ }
+ if (type == static_cast<int>(TT::FUNC)) {
+ return false;
+ }
+ for (const auto& it : *array[1].get_array()) {
+ if (it.apply<bool>(*this)) {
+ return true;
+ }
+ }
+ if (array.size() == 3) {
+ return array[2].apply<bool>(*this);
+ } else {
+ return false;
+ }
+ }
+ template <class T>
+ bool operator() (T) {
+ return false;
+ }
+} needs_func_wrap;
+
+Term Term::func_wrap(Term&& term) {
+ if (term.datum.apply<bool>(needs_func_wrap)) {
+ return Term(TT::FUNC, {expr(Array{new_var_id(term.free_vars)}), std::move(term)});
+ }
+ return term;
+}
+
+Term Term::func_wrap(const Term& term) {
+ if (term.datum.apply<bool>(needs_func_wrap)) {
+ // TODO return Term(TT::FUNC, {expr(Array{new_var_id(Term.free_vars)}), Term.copy()});
+ return Term(Nil());
+ }
+ return term;
+}
+
+Term Term::make_object(std::vector<Term>&& args) {
+ if (args.size() % 2 != 0) {
+ return Term(TT::OBJECT, std::move(args));
+ }
+ std::set<std::string> keys;
+ for (auto it = args.begin(); it != args.end() && it + 1 != args.end(); it += 2) {
+ std::string* key = it->datum.get_string();
+ if (!key || keys.count(*key)) {
+ return Term(TT::OBJECT, std::move(args));
+ }
+ keys.insert(*key);
+ }
+ Term ret{Nil()};
+ Object object;
+ for (auto it = args.begin(); it != args.end(); it += 2) {
+ std::string* key = it->datum.get_string();
+ object.emplace(std::move(*key), ret.alpha_rename(std::move(*(it + 1))));
+ }
+ ret.datum = std::move(object);
+ return ret;
+}
+
+Term Term::make_binary(Term&& term) {
+ std::string* string = term.datum.get_string();
+ if (string) {
+ return expr(Binary(std::move(*string)));
+ }
+ return Term(TT::BINARY, std::vector<Term>{term});
+}
+
+Term::Term(OptArgs&& optargs) : datum(Nil()) {
+ Object oargs;
+ for (auto& it : optargs) {
+ oargs.emplace(it.first, alpha_rename(std::move(it.second)));
+ }
+ datum = std::move(oargs);
+}
+
+OptArgs optargs() {
+ return OptArgs{};
+}
+
+Term january(TT::JANUARY, {});
+Term february(TT::FEBRUARY, {});
+Term march(TT::MARCH, {});
+Term april(TT::APRIL, {});
+Term may(TT::MAY, {});
+Term june(TT::JUNE, {});
+Term july(TT::JULY, {});
+Term august(TT::AUGUST, {});
+Term september(TT::SEPTEMBER, {});
+Term october(TT::OCTOBER, {});
+Term november(TT::NOVEMBER, {});
+Term december(TT::DECEMBER, {});
+Term monday(TT::MONDAY, {});
+Term tuesday(TT::TUESDAY, {});
+Term wednesday(TT::WEDNESDAY, {});
+Term thursday(TT::THURSDAY, {});
+Term friday(TT::FRIDAY, {});
+Term saturday(TT::SATURDAY, {});
+Term sunday(TT::SUNDAY, {});
+
+Term Term::copy() const {
+ return *this;
+}
+
+Datum Term::get_datum() const {
+ return datum;
+}
+
+}
diff --git a/ext/librethinkdbxx/src/term.h b/ext/librethinkdbxx/src/term.h
new file mode 100644
index 00000000..cdee5ec4
--- /dev/null
+++ b/ext/librethinkdbxx/src/term.h
@@ -0,0 +1,592 @@
+#pragma once
+
+#include "datum.h"
+#include "connection.h"
+#include "protocol_defs.h"
+#include "cursor.h"
+
+namespace RethinkDB {
+
+using TT = Protocol::Term::TermType;
+
+class Term;
+class Var;
+
+// An alias for the Term constructor
+template <class T>
+Term expr(T&&);
+
+int gen_var_id();
+
+// Can be used as the last argument to some ReQL commands that expect named arguments
+using OptArgs = std::map<std::string, Term>;
+
+// Represents a ReQL Term (RethinkDB Query Language)
+// Designed to be used with r-value *this
+class Term {
+public:
+ Term(const Term& other) = default;
+ Term(Term&& other) = default;
+ Term& operator= (const Term& other) = default;
+ Term& operator= (Term&& other) = default;
+
+ explicit Term(Datum&&);
+ explicit Term(const Datum&);
+ explicit Term(OptArgs&&);
+
+ // Create a copy of the Term
+ Term copy() const;
+
+ Term(std::function<Term()> f) : datum(Nil()) { set_function<std::function<Term()>>(f); }
+ Term(std::function<Term(Var)> f) : datum(Nil()) { set_function<std::function<Term(Var)>, 0>(f); }
+ Term(std::function<Term(Var, Var)> f) : datum(Nil()) { set_function<std::function<Term(Var, Var)>, 0, 1>(f); }
+ Term(std::function<Term(Var, Var, Var)> f) : datum(Nil()) { set_function<std::function<Term(Var, Var, Var)>, 0, 1, 2>(f); }
+ Term(Protocol::Term::TermType type, std::vector<Term>&& args) : datum(Array()) {
+ Array dargs;
+ for (auto& it : args) {
+ dargs.emplace_back(alpha_rename(std::move(it)));
+ }
+ datum = Datum(Array{ type, std::move(dargs) });
+ }
+
+ Term(Protocol::Term::TermType type, std::vector<Term>&& args, OptArgs&& optargs) : datum(Array()) {
+ Array dargs;
+ for (auto& it : args) {
+ dargs.emplace_back(alpha_rename(std::move(it)));
+ }
+ Object oargs;
+ for (auto& it : optargs) {
+ oargs.emplace(it.first, alpha_rename(std::move(it.second)));
+ }
+ datum = Array{ type, std::move(dargs), std::move(oargs) };
+ }
+
+ // Used internally to support row
+ static Term func_wrap(Term&&);
+ static Term func_wrap(const Term&);
+
+
+ // These macros are used to define most ReQL commands
+ // * Cn represents a method with n arguments
+ // * COn represents a method with n arguments and optional named arguments
+ // * C_ represents a method with any number of arguments
+ // Each method is implemented twice, once with r-value *this, and once with const *this
+ // The third argument, wrap, allows converting arguments into functions if they contain row
+
+#define C0(name, type) \
+ Term name() && { return Term(TT::type, std::vector<Term>{ std::move(*this) }); } \
+ Term name() const & { return Term(TT::type, std::vector<Term>{ *this }); }
+#define C1(name, type, wrap) \
+ template <class T> \
+ Term name(T&& a) && { return Term(TT::type, std::vector<Term>{ std::move(*this), wrap(expr(std::forward<T>(a))) }); } \
+ template <class T> \
+ Term name(T&& a) const & { return Term(TT::type, std::vector<Term>{ *this, wrap(expr(std::forward<T>(a))) }); }
+#define C2(name, type) \
+ template <class T, class U> Term name(T&& a, U&& b) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ expr(std::forward<T>(a)), expr(std::forward<U>(b)) }); } \
+ template <class T, class U> Term name(T&& a, U&& b) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ expr(std::forward<T>(a)), expr(std::forward<U>(b)) }); }
+#define C_(name, type, wrap) \
+ template <class ...T> Term name(T&& ...a) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a)))... }); } \
+ template <class ...T> Term name(T&& ...a) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a)))... }); }
+#define CO0(name, type) \
+ Term name(OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this) }, std::move(optarg)); } \
+ Term name(OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this }, std::move(optarg)); }
+#define CO1(name, type, wrap) \
+ template <class T> Term name(T&& a, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))) }, std::move(optarg)); } \
+ template <class T> Term name(T&& a, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))) }, std::move(optarg)); }
+#define CO2(name, type, wrap) \
+ template <class T, class U> Term name(T&& a, U&& b, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))) }, std::move(optarg)); } \
+ template <class T, class U> Term name(T&& a, U&& b, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))) }, std::move(optarg)); }
+#define CO3(name, type, wrap) \
+ template <class T, class U, class V> Term name(T&& a, U&& b, V&& c, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c))) }, std::move(optarg)); } \
+ template <class T, class U, class V> Term name(T&& a, U&& b, V&& c, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c)))}, std::move(optarg)); }
+#define CO4(name, type, wrap) \
+ template <class T, class U, class V, class W> Term name(T&& a, U&& b, V&& c, W&& d, OptArgs&& optarg = {}) && { \
+ return Term(TT::type, std::vector<Term>{ std::move(*this), \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c))), wrap(expr(std::forward<W>(d))) }, std::move(optarg)); } \
+ template <class T, class U, class V, class W> Term name(T&& a, U&& b, V&& c, W&& d, OptArgs&& optarg = {}) const & { \
+ return Term(TT::type, std::vector<Term>{ *this, \
+ wrap(expr(std::forward<T>(a))), wrap(expr(std::forward<U>(b))), \
+ wrap(expr(std::forward<V>(c))), wrap(expr(std::forward<W>(d))) }, std::move(optarg)); }
+#define CO_(name, type, wrap) \
+ C_(name, type, wrap) \
+ CO0(name, type) \
+ CO1(name, type, wrap) \
+ CO2(name, type, wrap) \
+ CO3(name, type, wrap) \
+ CO4(name, type, wrap)
+#define no_wrap(x) x
+
+ CO1(table_create, TABLE_CREATE, no_wrap)
+ C1(table_drop, TABLE_DROP, no_wrap)
+ C0(table_list, TABLE_LIST)
+ CO1(index_create, INDEX_CREATE, no_wrap)
+ CO2(index_create, INDEX_CREATE, func_wrap)
+ C1(index_drop, INDEX_DROP, no_wrap)
+ C0(index_list, INDEX_LIST)
+ CO2(index_rename, INDEX_RENAME, no_wrap)
+ C_(index_status, INDEX_STATUS, no_wrap)
+ C_(index_wait, INDEX_WAIT, no_wrap)
+ CO0(changes, CHANGES)
+ CO1(insert, INSERT, no_wrap)
+ CO1(update, UPDATE, func_wrap)
+ CO1(replace, REPLACE, func_wrap)
+ CO0(delete_, DELETE)
+ C0(sync, SYNC)
+ CO1(table, TABLE, no_wrap)
+ C1(get, GET, no_wrap)
+ CO_(get_all, GET_ALL, no_wrap)
+ CO2(between, BETWEEN, no_wrap)
+ CO1(filter, FILTER, func_wrap)
+ C2(inner_join, INNER_JOIN)
+ C2(outer_join, OUTER_JOIN)
+ CO2(eq_join, EQ_JOIN, func_wrap)
+ C0(zip, ZIP)
+ C_(map, MAP, func_wrap)
+ C_(with_fields, WITH_FIELDS, no_wrap)
+ C1(concat_map, CONCAT_MAP, func_wrap)
+ CO_(order_by, ORDER_BY, func_wrap)
+ C1(skip, SKIP, no_wrap)
+ C1(limit, LIMIT, no_wrap)
+ CO1(slice, SLICE, no_wrap)
+ CO2(slice, SLICE, no_wrap)
+ C1(nth, NTH, no_wrap)
+ C1(offsets_of, OFFSETS_OF, func_wrap)
+ C0(is_empty, IS_EMPTY)
+ CO_(union_, UNION, no_wrap)
+ C1(sample, SAMPLE, no_wrap)
+ CO_(group, GROUP, func_wrap)
+ C0(ungroup, UNGROUP)
+ C1(reduce, REDUCE, no_wrap)
+ CO2(fold, FOLD, no_wrap)
+ C0(count, COUNT)
+ C1(count, COUNT, func_wrap)
+ C0(sum, SUM)
+ C1(sum, SUM, func_wrap)
+ C0(avg, AVG)
+ C1(avg, AVG, func_wrap)
+ C1(min, MIN, func_wrap)
+ CO0(min, MIN)
+ C1(max, MAX, func_wrap)
+ CO0(max, MAX)
+ CO0(distinct, DISTINCT)
+ C_(contains, CONTAINS, func_wrap)
+ C_(pluck, PLUCK, no_wrap)
+ C_(without, WITHOUT, no_wrap)
+ C_(merge, MERGE, func_wrap)
+ C1(append, APPEND, no_wrap)
+ C1(prepend, PREPEND, no_wrap)
+ C1(difference, DIFFERENCE, no_wrap)
+ C1(set_insert, SET_INSERT, no_wrap)
+ C1(set_union, SET_UNION, no_wrap)
+ C1(set_intersection, SET_INTERSECTION, no_wrap)
+ C1(set_difference, SET_DIFFERENCE, no_wrap)
+ C1(operator[], BRACKET, no_wrap)
+ C1(get_field, GET_FIELD, no_wrap)
+ C_(has_fields, HAS_FIELDS, no_wrap)
+ C2(insert_at, INSERT_AT)
+ C2(splice_at, SPLICE_AT)
+ C1(delete_at, DELETE_AT, no_wrap)
+ C2(delete_at, DELETE_AT)
+ C2(change_at, CHANGE_AT)
+ C0(keys, KEYS)
+ C1(match, MATCH, no_wrap)
+ C0(split, SPLIT)
+ C1(split, SPLIT, no_wrap)
+ C2(split, SPLIT)
+ C0(upcase, UPCASE)
+ C0(downcase, DOWNCASE)
+ C_(add, ADD, no_wrap)
+ C1(operator+, ADD, no_wrap)
+ C_(sub, SUB, no_wrap)
+ C1(operator-, SUB, no_wrap)
+ C_(mul, MUL, no_wrap)
+ C1(operator*, MUL, no_wrap)
+ C_(div, DIV, no_wrap)
+ C1(operator/, DIV, no_wrap)
+ C1(mod, MOD, no_wrap)
+ C1(operator%, MOD, no_wrap)
+ C_(and_, AND, no_wrap)
+ C1(operator&&, AND, no_wrap)
+ C_(or_, OR, no_wrap)
+ C1(operator||, OR, no_wrap)
+ C1(eq, EQ, no_wrap)
+ C1(operator==, EQ, no_wrap)
+ C1(ne, NE, no_wrap)
+ C1(operator!=, NE, no_wrap)
+ C1(gt, GT, no_wrap)
+ C1(operator>, GT, no_wrap)
+ C1(ge, GE, no_wrap)
+ C1(operator>=, GE, no_wrap)
+ C1(lt, LT, no_wrap)
+ C1(operator<, LT, no_wrap)
+ C1(le, LE, no_wrap)
+ C1(operator<=, LE, no_wrap)
+ C0(not_, NOT)
+ C0(operator!, NOT)
+ C1(in_timezone, IN_TIMEZONE, no_wrap)
+ C0(timezone, TIMEZONE)
+ CO2(during, DURING, no_wrap)
+ C0(date, DATE)
+ C0(time_of_day, TIME_OF_DAY)
+ C0(year, YEAR)
+ C0(month, MONTH)
+ C0(day, DAY)
+ C0(day_of_week, DAY_OF_WEEK)
+ C0(day_of_year, DAY_OF_YEAR)
+ C0(hours, HOURS)
+ C0(minutes, MINUTES)
+ C0(seconds, SECONDS)
+ C0(to_iso8601, TO_ISO8601)
+ C0(to_epoch_time, TO_EPOCH_TIME)
+ C1(for_each, FOR_EACH, func_wrap)
+ C1(default_, DEFAULT, no_wrap)
+ CO1(js, JAVASCRIPT, no_wrap)
+ C1(coerce_to, COERCE_TO, no_wrap)
+ C0(type_of, TYPE_OF)
+ C0(info, INFO)
+ C0(to_json, TO_JSON_STRING)
+ C0(to_json_string, TO_JSON_STRING)
+ C1(distance, DISTANCE, no_wrap)
+ C0(fill, FILL)
+ C0(to_geojson, TO_GEOJSON)
+ CO1(get_intersecting, GET_INTERSECTING, no_wrap)
+ CO1(get_nearest, GET_NEAREST, no_wrap)
+ C1(includes, INCLUDES, no_wrap)
+ C1(intersects, INTERSECTS, no_wrap)
+ C1(polygon_sub, POLYGON_SUB, no_wrap)
+ C0(config, CONFIG)
+ C0(rebalance, REBALANCE)
+ CO0(reconfigure, RECONFIGURE)
+ C0(status, STATUS)
+ CO0(wait, WAIT)
+ C0(floor, FLOOR)
+ C0(ceil, CEIL)
+ C0(round, ROUND)
+ C0(values, VALUES)
+
+ // The expansion of this macro fails to compile on some versions of GCC and Clang:
+ // C_(operator(), FUNCALL, no_wrap)
+ // The std::enable_if makes the error go away
+
+ // $doc(do)
+
+ template <class T, class ...U>
+ typename std::enable_if<!std::is_same<T, Var>::value, Term>::type
+ operator() (T&& a, U&& ...b) && {
+ return Term(TT::FUNCALL, std::vector<Term>{
+ std::move(*this),
+ expr(std::forward<T>(a)),
+ expr(std::forward<U>(b))... });
+ }
+ template <class T, class ...U>
+ typename std::enable_if<!std::is_same<T, Var>::value, Term>::type
+ operator() (T&& a, U&& ...b) const & {
+ return Term(TT::FUNCALL, std::vector<Term>{
+ *this,
+ expr(std::forward<T>(a)),
+ expr(std::forward<U>(b))... });
+ }
+
+#undef C0
+#undef C1
+#undef C2
+#undef C_
+#undef CO0
+#undef CO1
+#undef CO2
+
+ // Send the term to the server and return the results.
+ // Errors returned by the server are thrown.
+ Cursor run(Connection&, OptArgs&& args = {});
+
+ // $doc(do)
+ template <class ...T>
+ Term do_(T&& ...a) && {
+ auto list = { std::move(*this), Term::func_wrap(expr(std::forward<T>(a)))... };
+ std::vector<Term> args;
+ args.reserve(list.size() + 1);
+ args.emplace_back(func_wrap(std::move(*(list.end()-1))));
+ for (auto it = list.begin(); it + 1 != list.end(); ++it) {
+ args.emplace_back(std::move(*it));
+ }
+ return Term(TT::FUNCALL, std::move(args));
+ }
+
+ // Adds optargs to an already built term
+ Term opt(OptArgs&& optargs) && {
+ return Term(std::move(*this), std::move(optargs));
+ }
+
+ // Used internally to implement object()
+ static Term make_object(std::vector<Term>&&);
+
+ // Used internally to implement array()
+ static Term make_binary(Term&&);
+
+ Datum get_datum() const;
+
+private:
+ friend class Var;
+ friend class Connection;
+ friend struct Query;
+
+ template <int _>
+ Var mkvar(std::vector<int>& vars);
+
+ template <class F, int ...N>
+ void set_function(F);
+
+ Datum alpha_rename(Term&&);
+
+ Term(Term&& orig, OptArgs&& optargs);
+
+ std::map<int, int*> free_vars;
+ Datum datum;
+};
+
+// A term representing null
+Term nil();
+
+template <class T>
+Term expr(T&& a) {
+ return Term(std::forward<T>(a));
+}
+
+// Represents a ReQL variable.
+// This type is passed to functions used in ReQL queries.
+class Var {
+public:
+ // Convert to a term
+ Term operator*() const {
+ Term term(TT::VAR, std::vector<Term>{expr(*id)});
+ term.free_vars = {{*id, id}};
+ return term;
+ }
+
+ Var(int* id_) : id(id_) { }
+private:
+ int* id;
+};
+
+template <int N>
+Var Term::mkvar(std::vector<int>& vars) {
+ int id = gen_var_id();
+ vars.push_back(id);
+ return Var(&*vars.rbegin());
+}
+
+template <class F, int ...N>
+void Term::set_function(F f) {
+ std::vector<int> vars;
+ vars.reserve(sizeof...(N));
+ std::vector<Var> args = { mkvar<N>(vars)... };
+ Term body = f(args[N] ...);
+
+ int* low = &*vars.begin();
+ int* high = &*(vars.end() - 1);
+ for (auto it = body.free_vars.begin(); it != body.free_vars.end(); ) {
+ if (it->second >= low && it->second <= high) {
+ if (it->first != *it->second) {
+ throw Error("Internal error: variable index mis-match");
+ }
+ ++it;
+ } else {
+ free_vars.emplace(*it);
+ ++it;
+ }
+ }
+ datum = Array{TT::FUNC, Array{Array{TT::MAKE_ARRAY, vars}, body.datum}};
+}
+
+// These macros are similar to those defined above, but for top-level ReQL operations
+
+#define C0(name) Term name();
+#define C0_IMPL(name, type) Term name() { return Term(TT::type, std::vector<Term>{}); }
+#define CO0(name) Term name(OptArgs&& optargs = {});
+#define CO0_IMPL(name, type) Term name(OptArgs&& optargs) { return Term(TT::type, std::vector<Term>{}, std::move(optargs)); }
+#define C1(name, type, wrap) template <class T> Term name(T&& a) { \
+ return Term(TT::type, std::vector<Term>{ wrap(expr(std::forward<T>(a))) }); }
+#define C2(name, type) template <class T, class U> Term name(T&& a, U&& b) { \
+ return Term(TT::type, std::vector<Term>{ expr(std::forward<T>(a)), expr(std::forward<U>(b)) }); }
+#define C3(name, type) template <class A, class B, class C> \
+ Term name(A&& a, B&& b, C&& c) { return Term(TT::type, std::vector<Term>{ \
+ expr(std::forward<A>(a)), expr(std::forward<B>(b)), expr(std::forward<C>(c)) }); }
+#define C4(name, type) template <class A, class B, class C, class D> \
+ Term name(A&& a, B&& b, C&& c, D&& d) { return Term(TT::type, std::vector<Term>{ \
+ expr(std::forward<A>(a)), expr(std::forward<B>(b)), \
+ expr(std::forward<C>(c)), expr(std::forward<D>(d))}); }
+#define C7(name, type) template <class A, class B, class C, class D, class E, class F, class G> \
+ Term name(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) { return Term(TT::type, std::vector<Term>{ \
+ expr(std::forward<A>(a)), expr(std::forward<B>(b)), expr(std::forward<C>(c)), \
+ expr(std::forward<D>(d)), expr(std::forward<E>(e)), expr(std::forward<F>(f)), \
+ expr(std::forward<G>(g))}); }
+#define C_(name, type, wrap) template <class ...T> Term name(T&& ...a) { \
+ return Term(TT::type, std::vector<Term>{ wrap(expr(std::forward<T>(a)))... }); }
+#define CO1(name, type, wrap) template <class T> Term name(T&& a, OptArgs&& optarg = {}) { \
+ return Term(TT::type, std::vector<Term>{ wrap(expr(std::forward<T>(a)))}, std::move(optarg)); }
+#define CO2(name, type) template <class T, class U> Term name(T&& a, U&& b, OptArgs&& optarg = {}) { \
+ return Term(TT::type, std::vector<Term>{ expr(std::forward<T>(a)), expr(std::forward<U>(b))}, std::move(optarg)); }
+#define func_wrap Term::func_wrap
+
+C1(db_create, DB_CREATE, no_wrap)
+C1(db_drop, DB_DROP, no_wrap)
+C0(db_list)
+CO1(table_create, TABLE_CREATE, no_wrap)
+C1(table_drop, TABLE_DROP, no_wrap)
+C0(table_list)
+C1(db, DB, no_wrap)
+CO1(table, TABLE, no_wrap)
+C_(add, ADD, no_wrap)
+C2(sub, SUB)
+C_(mul, MUL, no_wrap)
+C_(div, DIV, no_wrap)
+C2(mod, MOD)
+C_(and_, AND, no_wrap)
+C_(or_, OR, no_wrap)
+C2(eq, EQ)
+C2(ne, NE)
+C2(gt, GT)
+C2(ge, GE)
+C2(lt, LT)
+C2(le, LE)
+C1(not_, NOT, no_wrap)
+CO0(random)
+CO1(random, RANDOM, no_wrap)
+CO2(random, RANDOM)
+C0(now)
+C4(time, TIME)
+C7(time, TIME)
+C1(epoch_time, EPOCH_TIME, no_wrap)
+CO1(iso8601, ISO8601, no_wrap)
+CO1(js, JAVASCRIPT, no_wrap)
+C1(args, ARGS, no_wrap)
+C_(branch, BRANCH, no_wrap)
+C0(range)
+C1(range, RANGE, no_wrap)
+C2(range, RANGE)
+C0(error)
+C1(error, ERROR, no_wrap)
+C1(json, JSON, no_wrap)
+CO1(http, HTTP, func_wrap)
+C0(uuid)
+C1(uuid, UUID, no_wrap)
+CO2(circle, CIRCLE)
+C1(geojson, GEOJSON, no_wrap)
+C_(line, LINE, no_wrap)
+C2(point, POINT)
+C_(polygon, POLYGON, no_wrap)
+C_(array, MAKE_ARRAY, no_wrap)
+C1(desc, DESC, func_wrap)
+C1(asc, ASC, func_wrap)
+C0(literal)
+C1(literal, LITERAL, no_wrap)
+C1(type_of, TYPE_OF, no_wrap)
+C_(map, MAP, func_wrap)
+C1(floor, FLOOR, no_wrap)
+C1(ceil, CEIL, no_wrap)
+C1(round, ROUND, no_wrap)
+C_(union_, UNION, no_wrap)
+C_(group, GROUP, func_wrap)
+C1(count, COUNT, no_wrap)
+C_(count, COUNT, func_wrap)
+C1(sum, SUM, no_wrap)
+C_(sum, SUM, func_wrap)
+C1(avg, AVG, no_wrap)
+C_(avg, AVG, func_wrap)
+C1(min, MIN, no_wrap)
+C_(min, MIN, func_wrap)
+C1(max, MAX, no_wrap)
+C_(max, MAX, func_wrap)
+C1(distinct, DISTINCT, no_wrap)
+C1(contains, CONTAINS, no_wrap)
+C_(contains, CONTAINS, func_wrap)
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C7
+#undef C_
+#undef CO1
+#undef CO2
+#undef func_wrap
+
+// $doc(do)
+template <class R, class ...T>
+Term do_(R&& a, T&& ...b) {
+ return expr(std::forward<R>(a)).do_(std::forward<T>(b)...);
+}
+
+// $doc(object)
+template <class ...T>
+Term object(T&& ...a) {
+ return Term::make_object(std::vector<Term>{ expr(std::forward<T>(a))... });
+}
+
+// $doc(binary)
+template <class T>
+Term binary(T&& a) {
+ return Term::make_binary(expr(std::forward<T>(a)));
+}
+
+// Construct an empty optarg
+OptArgs optargs();
+
+// Construct an optarg made out of pairs of arguments
+// For example: optargs("k1", v1, "k2", v2)
+template <class V, class ...T>
+OptArgs optargs(const char* key, V&& val, T&& ...rest) {
+ OptArgs opts = optargs(rest...);
+ opts.emplace(key, expr(std::forward<V>(val)));
+ return opts;
+}
+
+extern Term row;
+extern Term maxval;
+extern Term minval;
+extern Term january;
+extern Term february;
+extern Term march;
+extern Term april;
+extern Term may;
+extern Term june;
+extern Term july;
+extern Term august;
+extern Term september;
+extern Term october;
+extern Term november;
+extern Term december;
+extern Term monday;
+extern Term tuesday;
+extern Term wednesday;
+extern Term thursday;
+extern Term friday;
+extern Term saturday;
+extern Term sunday;
+}
diff --git a/ext/librethinkdbxx/src/types.cc b/ext/librethinkdbxx/src/types.cc
new file mode 100644
index 00000000..ea9becaf
--- /dev/null
+++ b/ext/librethinkdbxx/src/types.cc
@@ -0,0 +1,47 @@
+#include <cstdlib>
+
+#include "types.h"
+#include "error.h"
+
+namespace RethinkDB {
+
+bool Time::parse_utc_offset(const std::string& string, double* offset) {
+ const char *s = string.c_str();
+ double sign = 1;
+ switch (s[0]) {
+ case '-':
+ sign = -1;
+ case '+':
+ ++s;
+ break;
+ case 0:
+ return false;
+ }
+ for (int i = 0; i < 5; ++i) {
+ if (s[i] == 0) return false;
+ if (i == 2) continue;
+ if (s[i] < '0' || s[i] > '9') return false;
+ }
+ if (s[2] != ':') return false;
+ *offset = sign * ((s[0] - '0') * 36000 + (s[1] - '0') * 3600 + (s[3] - '0') * 600 + (s[4] - '0') * 60);
+ return true;
+}
+
+double Time::parse_utc_offset(const std::string& string) {
+ double out;
+ if (!parse_utc_offset(string, &out)) {
+ throw Error("invalid utc offset `%s'", string.c_str());
+ }
+ return out;
+}
+
+std::string Time::utc_offset_string(double offset) {
+ char buf[8];
+ int hour = offset / 3600;
+ int minutes = std::abs(static_cast<int>(offset / 60)) % 60;
+ int n = snprintf(buf, 7, "%+03d:%02d", hour, minutes);
+ buf[n] = 0;
+ return std::string(buf);
+}
+
+}
diff --git a/ext/librethinkdbxx/src/types.h b/ext/librethinkdbxx/src/types.h
new file mode 100644
index 00000000..ac35a871
--- /dev/null
+++ b/ext/librethinkdbxx/src/types.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <vector>
+#include <map>
+#include <ctime>
+#include <string>
+
+namespace RethinkDB {
+
+class Datum;
+
+// Represents a null datum
+struct Nil { };
+
+using Array = std::vector<Datum>;
+using Object = std::map<std::string, Datum>;
+
+// Represents a string of bytes. Plain std::strings are passed on to the server as utf-8 strings
+struct Binary {
+ bool operator== (const Binary& other) const {
+ return data == other.data;
+ }
+
+ Binary(const std::string& data_) : data(data_) { }
+ Binary(std::string&& data_) : data(std::move(data_)) { }
+ std::string data;
+};
+
+// Represents a point in time as
+// * A floating amount of seconds since the UNIX epoch
+// * And a timezone offset represented as seconds relative to UTC
+struct Time {
+ Time(double epoch_time_, double utc_offset_ = 0) :
+ epoch_time(epoch_time_), utc_offset(utc_offset_) { }
+
+ static Time now() {
+ return Time(time(NULL));
+ }
+
+ static bool parse_utc_offset(const std::string&, double*);
+ static double parse_utc_offset(const std::string&);
+ static std::string utc_offset_string(double);
+
+ double epoch_time;
+ double utc_offset;
+};
+
+// Not implemented
+class Point;
+class Line;
+class Polygon;
+
+}
diff --git a/ext/librethinkdbxx/src/utils.cc b/ext/librethinkdbxx/src/utils.cc
new file mode 100644
index 00000000..5a2c244d
--- /dev/null
+++ b/ext/librethinkdbxx/src/utils.cc
@@ -0,0 +1,153 @@
+#include "utils.h"
+#include "error.h"
+
+namespace RethinkDB {
+
+size_t utf8_encode(unsigned int code, char* buf) {
+ if (!(code & ~0x7F)) {
+ buf[0] = code;
+ return 1;
+ } else if (!(code & ~0x7FF)) {
+ buf[0] = 0xC0 | (code >> 6);
+ buf[1] = 0x80 | (code & 0x3F);
+ return 2;
+ } else if (!(code & ~0xFFFF)) {
+ buf[0] = 0xE0 | (code >> 12);
+ buf[1] = 0x80 | ((code >> 6) & 0x3F);
+ buf[2] = 0x80 | (code & 0x3F);
+ return 3;
+ } else if (!(code & ~0x1FFFFF)) {
+ buf[0] = 0xF0 | (code >> 18);
+ buf[1] = 0x80 | ((code >> 12) & 0x3F);
+ buf[2] = 0x80 | ((code >> 6) & 0x3F);
+ buf[3] = 0x80 | (code & 0x3F);
+ return 4;
+ } else if (!(code & ~0x3FFFFFF)) {
+ buf[0] = 0xF8 | (code >> 24);
+ buf[1] = 0x80 | ((code >> 18) & 0x3F);
+ buf[2] = 0x80 | ((code >> 12) & 0x3F);
+ buf[3] = 0x80 | ((code >> 6) & 0x3F);
+ buf[4] = 0x80 | (code & 0x3F);
+ return 5;
+ } else if (!(code & ~0x7FFFFFFF)) {
+ buf[0] = 0xFC | (code >> 30);
+ buf[1] = 0x80 | ((code >> 24) & 0x3F);
+ buf[2] = 0x80 | ((code >> 18) & 0x3F);
+ buf[3] = 0x80 | ((code >> 12) & 0x3F);
+ buf[4] = 0x80 | ((code >> 6) & 0x3F);
+ buf[5] = 0x80 | (code & 0x3F);
+ return 6;
+ } else {
+ throw Error("Invalid unicode codepoint %ud", code);
+ }
+}
+
+bool base64_decode(char c, int* out) {
+ if (c >= 'A' && c <= 'Z') {
+ *out = c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ *out = c - ('a' - 26);
+ } else if (c >= '0' && c <= '9') {
+ *out = c - ('0' - 52);
+ } else if (c == '+') {
+ *out = 62;
+ } else if (c == '/') {
+ *out = 63;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool base64_decode(const std::string& in, std::string& out) {
+ out.clear();
+ out.reserve(in.size() * 3 / 4);
+ auto read = in.begin();
+ while (true) {
+ int c[4];
+ int end = 4;
+ for (int i = 0; i < 4; i++) {
+ while (true) {
+ if (read == in.end()) {
+ c[i] = 0;
+ end = i;
+ i = 3;
+ break;
+ } else if (base64_decode(*read, &c[i])) {
+ ++read;
+ break;
+ } else {
+ ++read;
+ }
+ }
+ }
+ if (end == 1) return false;
+ int val = c[0] << 18 | c[1] << 12 | c[2] << 6 | c[3];
+ if (end > 1) out.append(1, val >> 16);
+ if (end > 2) out.append(1, val >> 8 & 0xFF);
+ if (end > 3) out.append(1, val & 0xFF);
+ if (end != 4) break;
+ }
+ return true;
+}
+
+char base64_encode(unsigned int c) {
+ if (c < 26) {
+ return 'A' + c;
+ } else if (c < 52) {
+ return 'a' + c - 26;
+ } else if (c < 62) {
+ return '0' + c - 52;
+ } else if (c == 62) {
+ return '+';
+ } else if (c == 63) {
+ return '/';
+ } else {
+ throw Error("unreachable: base64 encoding %d", c);
+ }
+}
+
+void base64_encode(unsigned int* c, int n, std::string& out) {
+ if (n == 0) {
+ return;
+ }
+ out.append(1, base64_encode(c[0] >> 2));
+ out.append(1, base64_encode((c[0] & 0x3) << 4 | c[1] >> 4));
+ if (n == 1) {
+ out.append("==");
+ return;
+ }
+ out.append(1, base64_encode((c[1] & 0xF) << 2 | c[2] >> 6));
+ if (n == 2) {
+ out.append("=");
+ return;
+ }
+ out.append(1, base64_encode(c[2] & 0x3F));
+}
+
+std::string base64_encode(const std::string& in) {
+ std::string out;
+ out.reserve(in.size() * 4 / 3 + in.size() / 48 + 3);
+ auto read = in.begin();
+ while (true) {
+ for (int group = 0; group < 16; ++group) {
+ unsigned int c[3];
+ int i = 0;
+ for (; i < 3; ++i) {
+ if (read == in.end()) {
+ c[i] = 0;
+ break;
+ } else {
+ c[i] = static_cast<unsigned char>(*read++);
+ }
+ }
+ base64_encode(c, i, out);
+ if (i != 3) {
+ return out;
+ }
+ }
+ out.append("\n");
+ }
+}
+
+}
diff --git a/ext/librethinkdbxx/src/utils.h b/ext/librethinkdbxx/src/utils.h
new file mode 100644
index 00000000..04496e2c
--- /dev/null
+++ b/ext/librethinkdbxx/src/utils.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+namespace RethinkDB {
+
+// The size of the longest UTF-8 encoded unicode codepoint
+const size_t max_utf8_encoded_size = 6;
+
+// Decode a base64 string. Returns false on failure.
+bool base64_decode(const std::string& in, std::string& out);
+std::string base64_encode(const std::string&);
+
+// Encodes a single unicode codepoint into UTF-8. Returns the number of bytes written.
+// Does not add a trailing null byte
+size_t utf8_encode(unsigned int, char*);
+
+}
diff --git a/ext/lz4/lz4.c b/ext/lz4/lz4.c
deleted file mode 100644
index 08cf6b5c..00000000
--- a/ext/lz4/lz4.c
+++ /dev/null
@@ -1,1516 +0,0 @@
-/*
- LZ4 - Fast LZ compression algorithm
- Copyright (C) 2011-2015, Yann Collet.
-
- BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
- 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.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER 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.
-
- You can contact the author at :
- - LZ4 source repository : https://github.com/Cyan4973/lz4
- - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
-*/
-
-
-/**************************************
-* Tuning parameters
-**************************************/
-/*
- * HEAPMODE :
- * Select how default compression functions will allocate memory for their hash table,
- * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
- */
-#define HEAPMODE 0
-
-/*
- * ACCELERATION_DEFAULT :
- * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
- */
-#define ACCELERATION_DEFAULT 1
-
-
-/**************************************
-* CPU Feature Detection
-**************************************/
-/*
- * LZ4_FORCE_SW_BITCOUNT
- * Define this parameter if your target system or compiler does not support hardware bit count
- */
-#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
-# define LZ4_FORCE_SW_BITCOUNT
-#endif
-
-
-/**************************************
-* Includes
-**************************************/
-#include "lz4.h"
-
-
-/**************************************
-* Compiler Options
-**************************************/
-#ifdef _MSC_VER /* Visual Studio */
-# define FORCE_INLINE static __forceinline
-# include <intrin.h>
-# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
-# pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
-#else
-# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
-# if defined(__GNUC__) || defined(__clang__)
-# define FORCE_INLINE static inline __attribute__((always_inline))
-# else
-# define FORCE_INLINE static inline
-# endif
-# else
-# define FORCE_INLINE static
-# endif /* __STDC_VERSION__ */
-#endif /* _MSC_VER */
-
-/* LZ4_GCC_VERSION is defined into lz4.h */
-#if (LZ4_GCC_VERSION >= 302) || (__INTEL_COMPILER >= 800) || defined(__clang__)
-# define expect(expr,value) (__builtin_expect ((expr),(value)) )
-#else
-# define expect(expr,value) (expr)
-#endif
-
-#define likely(expr) expect((expr) != 0, 1)
-#define unlikely(expr) expect((expr) != 0, 0)
-
-
-/**************************************
-* Memory routines
-**************************************/
-#include <stdlib.h> /* malloc, calloc, free */
-#define ALLOCATOR(n,s) calloc(n,s)
-#define FREEMEM free
-#include <string.h> /* memset, memcpy */
-#define MEM_INIT memset
-
-
-/**************************************
-* Basic Types
-**************************************/
-#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
-# include <stdint.h>
- typedef uint8_t BYTE;
- typedef uint16_t U16;
- typedef uint32_t U32;
- typedef int32_t S32;
- typedef uint64_t U64;
-#else
- typedef unsigned char BYTE;
- typedef unsigned short U16;
- typedef unsigned int U32;
- typedef signed int S32;
- typedef unsigned long long U64;
-#endif
-
-
-/**************************************
-* Reading and writing into memory
-**************************************/
-#define STEPSIZE sizeof(size_t)
-
-static unsigned LZ4_64bits(void) { return sizeof(void*)==8; }
-
-static unsigned LZ4_isLittleEndian(void)
-{
- const union { U32 i; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
- return one.c[0];
-}
-
-
-static U16 LZ4_read16(const void* memPtr)
-{
- U16 val16;
- memcpy(&val16, memPtr, 2);
- return val16;
-}
-
-static U16 LZ4_readLE16(const void* memPtr)
-{
- if (LZ4_isLittleEndian())
- {
- return LZ4_read16(memPtr);
- }
- else
- {
- const BYTE* p = (const BYTE*)memPtr;
- return (U16)((U16)p[0] + (p[1]<<8));
- }
-}
-
-static void LZ4_writeLE16(void* memPtr, U16 value)
-{
- if (LZ4_isLittleEndian())
- {
- memcpy(memPtr, &value, 2);
- }
- else
- {
- BYTE* p = (BYTE*)memPtr;
- p[0] = (BYTE) value;
- p[1] = (BYTE)(value>>8);
- }
-}
-
-static U32 LZ4_read32(const void* memPtr)
-{
- U32 val32;
- memcpy(&val32, memPtr, 4);
- return val32;
-}
-
-static U64 LZ4_read64(const void* memPtr)
-{
- U64 val64;
- memcpy(&val64, memPtr, 8);
- return val64;
-}
-
-static size_t LZ4_read_ARCH(const void* p)
-{
- if (LZ4_64bits())
- return (size_t)LZ4_read64(p);
- else
- return (size_t)LZ4_read32(p);
-}
-
-
-static void LZ4_copy4(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 4); }
-
-static void LZ4_copy8(void* dstPtr, const void* srcPtr) { memcpy(dstPtr, srcPtr, 8); }
-
-/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */
-static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
-{
- BYTE* d = (BYTE*)dstPtr;
- const BYTE* s = (const BYTE*)srcPtr;
- BYTE* e = (BYTE*)dstEnd;
- do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
-}
-
-
-/**************************************
-* Common Constants
-**************************************/
-#define MINMATCH 4
-
-#define COPYLENGTH 8
-#define LASTLITERALS 5
-#define MFLIMIT (COPYLENGTH+MINMATCH)
-static const int LZ4_minLength = (MFLIMIT+1);
-
-#define KB *(1 <<10)
-#define MB *(1 <<20)
-#define GB *(1U<<30)
-
-#define MAXD_LOG 16
-#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
-
-#define ML_BITS 4
-#define ML_MASK ((1U<<ML_BITS)-1)
-#define RUN_BITS (8-ML_BITS)
-#define RUN_MASK ((1U<<RUN_BITS)-1)
-
-
-/**************************************
-* Common Utils
-**************************************/
-#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
-
-
-/**************************************
-* Common functions
-**************************************/
-static unsigned LZ4_NbCommonBytes (register size_t val)
-{
- if (LZ4_isLittleEndian())
- {
- if (LZ4_64bits())
- {
-# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanForward64( &r, (U64)val );
- return (int)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_ctzll((U64)val) >> 3);
-# else
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-# endif
- }
- else /* 32 bits */
- {
-# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r;
- _BitScanForward( &r, (U32)val );
- return (int)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_ctz((U32)val) >> 3);
-# else
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-# endif
- }
- }
- else /* Big Endian CPU */
- {
- if (LZ4_64bits())
- {
-# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanReverse64( &r, val );
- return (unsigned)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_clzll((U64)val) >> 3);
-# else
- unsigned r;
- if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
-# endif
- }
- else /* 32 bits */
- {
-# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
- unsigned long r = 0;
- _BitScanReverse( &r, (unsigned long)val );
- return (unsigned)(r>>3);
-# elif (defined(__clang__) || (LZ4_GCC_VERSION >= 304)) && !defined(LZ4_FORCE_SW_BITCOUNT)
- return (__builtin_clz((U32)val) >> 3);
-# else
- unsigned r;
- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
- r += (!val);
- return r;
-# endif
- }
- }
-}
-
-static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
-{
- const BYTE* const pStart = pIn;
-
- while (likely(pIn<pInLimit-(STEPSIZE-1)))
- {
- size_t diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
- if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
- pIn += LZ4_NbCommonBytes(diff);
- return (unsigned)(pIn - pStart);
- }
-
- if (LZ4_64bits()) if ((pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
- if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
- if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
- return (unsigned)(pIn - pStart);
-}
-
-
-#ifndef LZ4_COMMONDEFS_ONLY
-/**************************************
-* Local Constants
-**************************************/
-#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
-#define HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
-#define HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
-
-static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
-static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
-
-
-/**************************************
-* Local Structures and types
-**************************************/
-typedef struct {
- U32 hashTable[HASH_SIZE_U32];
- U32 currentOffset;
- U32 initCheck;
- const BYTE* dictionary;
- BYTE* bufferStart; /* obsolete, used for slideInputBuffer */
- U32 dictSize;
-} LZ4_stream_t_internal;
-
-typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
-typedef enum { byPtr, byU32, byU16 } tableType_t;
-
-typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
-typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
-
-typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
-typedef enum { full = 0, partial = 1 } earlyEnd_directive;
-
-
-/**************************************
-* Local Utils
-**************************************/
-int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
-int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
-int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
-
-
-
-/********************************
-* Compression functions
-********************************/
-
-static U32 LZ4_hashSequence(U32 sequence, tableType_t const tableType)
-{
- if (tableType == byU16)
- return (((sequence) * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
- else
- return (((sequence) * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
-}
-
-static const U64 prime5bytes = 889523592379ULL;
-static U32 LZ4_hashSequence64(size_t sequence, tableType_t const tableType)
-{
- const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
- const U32 hashMask = (1<<hashLog) - 1;
- return ((sequence * prime5bytes) >> (40 - hashLog)) & hashMask;
-}
-
-static U32 LZ4_hashSequenceT(size_t sequence, tableType_t const tableType)
-{
- if (LZ4_64bits())
- return LZ4_hashSequence64(sequence, tableType);
- return LZ4_hashSequence((U32)sequence, tableType);
-}
-
-static U32 LZ4_hashPosition(const void* p, tableType_t tableType) { return LZ4_hashSequenceT(LZ4_read_ARCH(p), tableType); }
-
-static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
-{
- switch (tableType)
- {
- case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
- case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
- case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
- }
-}
-
-static void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
-{
- U32 h = LZ4_hashPosition(p, tableType);
- LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
-}
-
-static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
-{
- if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
- if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
- { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
-}
-
-static const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
-{
- U32 h = LZ4_hashPosition(p, tableType);
- return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
-}
-
-FORCE_INLINE int LZ4_compress_generic(
- void* const ctx,
- const char* const source,
- char* const dest,
- const int inputSize,
- const int maxOutputSize,
- const limitedOutput_directive outputLimited,
- const tableType_t tableType,
- const dict_directive dict,
- const dictIssue_directive dictIssue,
- const U32 acceleration)
-{
- LZ4_stream_t_internal* const dictPtr = (LZ4_stream_t_internal*)ctx;
-
- const BYTE* ip = (const BYTE*) source;
- const BYTE* base;
- const BYTE* lowLimit;
- const BYTE* const lowRefLimit = ip - dictPtr->dictSize;
- const BYTE* const dictionary = dictPtr->dictionary;
- const BYTE* const dictEnd = dictionary + dictPtr->dictSize;
- const size_t dictDelta = dictEnd - (const BYTE*)source;
- const BYTE* anchor = (const BYTE*) source;
- const BYTE* const iend = ip + inputSize;
- const BYTE* const mflimit = iend - MFLIMIT;
- const BYTE* const matchlimit = iend - LASTLITERALS;
-
- BYTE* op = (BYTE*) dest;
- BYTE* const olimit = op + maxOutputSize;
-
- U32 forwardH;
- size_t refDelta=0;
-
- /* Init conditions */
- if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
- switch(dict)
- {
- case noDict:
- default:
- base = (const BYTE*)source;
- lowLimit = (const BYTE*)source;
- break;
- case withPrefix64k:
- base = (const BYTE*)source - dictPtr->currentOffset;
- lowLimit = (const BYTE*)source - dictPtr->dictSize;
- break;
- case usingExtDict:
- base = (const BYTE*)source - dictPtr->currentOffset;
- lowLimit = (const BYTE*)source;
- break;
- }
- if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
- if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
-
- /* First Byte */
- LZ4_putPosition(ip, ctx, tableType, base);
- ip++; forwardH = LZ4_hashPosition(ip, tableType);
-
- /* Main Loop */
- for ( ; ; )
- {
- const BYTE* match;
- BYTE* token;
- {
- const BYTE* forwardIp = ip;
- unsigned step = 1;
- unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
-
- /* Find a match */
- do {
- U32 h = forwardH;
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimit)) goto _last_literals;
-
- match = LZ4_getPositionOnHash(h, ctx, tableType, base);
- if (dict==usingExtDict)
- {
- if (match<(const BYTE*)source)
- {
- refDelta = dictDelta;
- lowLimit = dictionary;
- }
- else
- {
- refDelta = 0;
- lowLimit = (const BYTE*)source;
- }
- }
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, ctx, tableType, base);
-
- } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
- || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
- || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
- }
-
- /* Catch up */
- while ((ip>anchor) && (match+refDelta > lowLimit) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
-
- {
- /* Encode Literal length */
- unsigned litLength = (unsigned)(ip - anchor);
- token = op++;
- if ((outputLimited) && (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
- return 0; /* Check output limit */
- if (litLength>=RUN_MASK)
- {
- int len = (int)litLength-RUN_MASK;
- *token=(RUN_MASK<<ML_BITS);
- for(; len >= 255 ; len-=255) *op++ = 255;
- *op++ = (BYTE)len;
- }
- else *token = (BYTE)(litLength<<ML_BITS);
-
- /* Copy Literals */
- LZ4_wildCopy(op, anchor, op+litLength);
- op+=litLength;
- }
-
-_next_match:
- /* Encode Offset */
- LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
-
- /* Encode MatchLength */
- {
- unsigned matchLength;
-
- if ((dict==usingExtDict) && (lowLimit==dictionary))
- {
- const BYTE* limit;
- match += refDelta;
- limit = ip + (dictEnd-match);
- if (limit > matchlimit) limit = matchlimit;
- matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
- ip += MINMATCH + matchLength;
- if (ip==limit)
- {
- unsigned more = LZ4_count(ip, (const BYTE*)source, matchlimit);
- matchLength += more;
- ip += more;
- }
- }
- else
- {
- matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
- ip += MINMATCH + matchLength;
- }
-
- if ((outputLimited) && (unlikely(op + (1 + LASTLITERALS) + (matchLength>>8) > olimit)))
- return 0; /* Check output limit */
- if (matchLength>=ML_MASK)
- {
- *token += ML_MASK;
- matchLength -= ML_MASK;
- for (; matchLength >= 510 ; matchLength-=510) { *op++ = 255; *op++ = 255; }
- if (matchLength >= 255) { matchLength-=255; *op++ = 255; }
- *op++ = (BYTE)matchLength;
- }
- else *token += (BYTE)(matchLength);
- }
-
- anchor = ip;
-
- /* Test end of chunk */
- if (ip > mflimit) break;
-
- /* Fill table */
- LZ4_putPosition(ip-2, ctx, tableType, base);
-
- /* Test next position */
- match = LZ4_getPosition(ip, ctx, tableType, base);
- if (dict==usingExtDict)
- {
- if (match<(const BYTE*)source)
- {
- refDelta = dictDelta;
- lowLimit = dictionary;
- }
- else
- {
- refDelta = 0;
- lowLimit = (const BYTE*)source;
- }
- }
- LZ4_putPosition(ip, ctx, tableType, base);
- if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
- && (match+MAX_DISTANCE>=ip)
- && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
-
- /* Prepare next loop */
- forwardH = LZ4_hashPosition(++ip, tableType);
- }
-
-_last_literals:
- /* Encode Last Literals */
- {
- const size_t lastRun = (size_t)(iend - anchor);
- if ((outputLimited) && ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize))
- return 0; /* Check output limit */
- if (lastRun >= RUN_MASK)
- {
- size_t accumulator = lastRun - RUN_MASK;
- *op++ = RUN_MASK << ML_BITS;
- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
- *op++ = (BYTE) accumulator;
- }
- else
- {
- *op++ = (BYTE)(lastRun<<ML_BITS);
- }
- memcpy(op, anchor, lastRun);
- op += lastRun;
- }
-
- /* End */
- return (int) (((char*)op)-dest);
-}
-
-
-int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_resetStream((LZ4_stream_t*)state);
- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
-
- if (maxOutputSize >= LZ4_compressBound(inputSize))
- {
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(state, source, dest, inputSize, 0, notLimited, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
- }
- else
- {
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(state, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
- }
-}
-
-
-int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
-#if (HEAPMODE)
- void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
-#else
- LZ4_stream_t ctx;
- void* ctxPtr = &ctx;
-#endif
-
- int result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
-
-#if (HEAPMODE)
- FREEMEM(ctxPtr);
-#endif
- return result;
-}
-
-
-int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
-{
- return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
-}
-
-
-/* hidden debug function */
-/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
-int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_stream_t ctx;
-
- LZ4_resetStream(&ctx);
-
- if (inputSize < LZ4_64Klimit)
- return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
- else
- return LZ4_compress_generic(&ctx, source, dest, inputSize, maxOutputSize, limitedOutput, LZ4_64bits() ? byU32 : byPtr, noDict, noDictIssue, acceleration);
-}
-
-
-/********************************
-* destSize variant
-********************************/
-
-static int LZ4_compress_destSize_generic(
- void* const ctx,
- const char* const src,
- char* const dst,
- int* const srcSizePtr,
- const int targetDstSize,
- const tableType_t tableType)
-{
- const BYTE* ip = (const BYTE*) src;
- const BYTE* base = (const BYTE*) src;
- const BYTE* lowLimit = (const BYTE*) src;
- const BYTE* anchor = ip;
- const BYTE* const iend = ip + *srcSizePtr;
- const BYTE* const mflimit = iend - MFLIMIT;
- const BYTE* const matchlimit = iend - LASTLITERALS;
-
- BYTE* op = (BYTE*) dst;
- BYTE* const oend = op + targetDstSize;
- BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
- BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
- BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
-
- U32 forwardH;
-
-
- /* Init conditions */
- if (targetDstSize < 1) return 0; /* Impossible to store anything */
- if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size, too large (or negative) */
- if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
- if (*srcSizePtr<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
-
- /* First Byte */
- *srcSizePtr = 0;
- LZ4_putPosition(ip, ctx, tableType, base);
- ip++; forwardH = LZ4_hashPosition(ip, tableType);
-
- /* Main Loop */
- for ( ; ; )
- {
- const BYTE* match;
- BYTE* token;
- {
- const BYTE* forwardIp = ip;
- unsigned step = 1;
- unsigned searchMatchNb = 1 << LZ4_skipTrigger;
-
- /* Find a match */
- do {
- U32 h = forwardH;
- ip = forwardIp;
- forwardIp += step;
- step = (searchMatchNb++ >> LZ4_skipTrigger);
-
- if (unlikely(forwardIp > mflimit))
- goto _last_literals;
-
- match = LZ4_getPositionOnHash(h, ctx, tableType, base);
- forwardH = LZ4_hashPosition(forwardIp, tableType);
- LZ4_putPositionOnHash(ip, h, ctx, tableType, base);
-
- } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
- || (LZ4_read32(match) != LZ4_read32(ip)) );
- }
-
- /* Catch up */
- while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
-
- {
- /* Encode Literal length */
- unsigned litLength = (unsigned)(ip - anchor);
- token = op++;
- if (op + ((litLength+240)/255) + litLength > oMaxLit)
- {
- /* Not enough space for a last match */
- op--;
- goto _last_literals;
- }
- if (litLength>=RUN_MASK)
- {
- unsigned len = litLength - RUN_MASK;
- *token=(RUN_MASK<<ML_BITS);
- for(; len >= 255 ; len-=255) *op++ = 255;
- *op++ = (BYTE)len;
- }
- else *token = (BYTE)(litLength<<ML_BITS);
-
- /* Copy Literals */
- LZ4_wildCopy(op, anchor, op+litLength);
- op += litLength;
- }
-
-_next_match:
- /* Encode Offset */
- LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
-
- /* Encode MatchLength */
- {
- size_t matchLength;
-
- matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
-
- if (op + ((matchLength+240)/255) > oMaxMatch)
- {
- /* Match description too long : reduce it */
- matchLength = (15-1) + (oMaxMatch-op) * 255;
- }
- //printf("offset %5i, matchLength%5i \n", (int)(ip-match), matchLength + MINMATCH);
- ip += MINMATCH + matchLength;
-
- if (matchLength>=ML_MASK)
- {
- *token += ML_MASK;
- matchLength -= ML_MASK;
- while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
- *op++ = (BYTE)matchLength;
- }
- else *token += (BYTE)(matchLength);
- }
-
- anchor = ip;
-
- /* Test end of block */
- if (ip > mflimit) break;
- if (op > oMaxSeq) break;
-
- /* Fill table */
- LZ4_putPosition(ip-2, ctx, tableType, base);
-
- /* Test next position */
- match = LZ4_getPosition(ip, ctx, tableType, base);
- LZ4_putPosition(ip, ctx, tableType, base);
- if ( (match+MAX_DISTANCE>=ip)
- && (LZ4_read32(match)==LZ4_read32(ip)) )
- { token=op++; *token=0; goto _next_match; }
-
- /* Prepare next loop */
- forwardH = LZ4_hashPosition(++ip, tableType);
- }
-
-_last_literals:
- /* Encode Last Literals */
- {
- size_t lastRunSize = (size_t)(iend - anchor);
- if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend)
- {
- /* adapt lastRunSize to fill 'dst' */
- lastRunSize = (oend-op) - 1;
- lastRunSize -= (lastRunSize+240)/255;
- }
- ip = anchor + lastRunSize;
-
- if (lastRunSize >= RUN_MASK)
- {
- size_t accumulator = lastRunSize - RUN_MASK;
- *op++ = RUN_MASK << ML_BITS;
- for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
- *op++ = (BYTE) accumulator;
- }
- else
- {
- *op++ = (BYTE)(lastRunSize<<ML_BITS);
- }
- memcpy(op, anchor, lastRunSize);
- op += lastRunSize;
- }
-
- /* End */
- *srcSizePtr = (int) (((const char*)ip)-src);
- return (int) (((char*)op)-dst);
-}
-
-
-static int LZ4_compress_destSize_extState (void* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
-{
- LZ4_resetStream((LZ4_stream_t*)state);
-
- if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) /* compression success is guaranteed */
- {
- return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
- }
- else
- {
- if (*srcSizePtr < LZ4_64Klimit)
- return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, byU16);
- else
- return LZ4_compress_destSize_generic(state, src, dst, srcSizePtr, targetDstSize, LZ4_64bits() ? byU32 : byPtr);
- }
-}
-
-
-int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
-{
-#if (HEAPMODE)
- void* ctx = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
-#else
- LZ4_stream_t ctxBody;
- void* ctx = &ctxBody;
-#endif
-
- int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
-
-#if (HEAPMODE)
- FREEMEM(ctx);
-#endif
- return result;
-}
-
-
-
-/********************************
-* Streaming functions
-********************************/
-
-LZ4_stream_t* LZ4_createStream(void)
-{
- LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64);
- LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */
- LZ4_resetStream(lz4s);
- return lz4s;
-}
-
-void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
-{
- MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
-}
-
-int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
-{
- FREEMEM(LZ4_stream);
- return (0);
-}
-
-
-#define HASH_UNIT sizeof(size_t)
-int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
-{
- LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict;
- const BYTE* p = (const BYTE*)dictionary;
- const BYTE* const dictEnd = p + dictSize;
- const BYTE* base;
-
- if ((dict->initCheck) || (dict->currentOffset > 1 GB)) /* Uninitialized structure, or reuse overflow */
- LZ4_resetStream(LZ4_dict);
-
- if (dictSize < (int)HASH_UNIT)
- {
- dict->dictionary = NULL;
- dict->dictSize = 0;
- return 0;
- }
-
- if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
- dict->currentOffset += 64 KB;
- base = p - dict->currentOffset;
- dict->dictionary = p;
- dict->dictSize = (U32)(dictEnd - p);
- dict->currentOffset += dict->dictSize;
-
- while (p <= dictEnd-HASH_UNIT)
- {
- LZ4_putPosition(p, dict->hashTable, byU32, base);
- p+=3;
- }
-
- return dict->dictSize;
-}
-
-
-static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src)
-{
- if ((LZ4_dict->currentOffset > 0x80000000) ||
- ((size_t)LZ4_dict->currentOffset > (size_t)src)) /* address space overflow */
- {
- /* rescale hash table */
- U32 delta = LZ4_dict->currentOffset - 64 KB;
- const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
- int i;
- for (i=0; i<HASH_SIZE_U32; i++)
- {
- if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;
- else LZ4_dict->hashTable[i] -= delta;
- }
- LZ4_dict->currentOffset = 64 KB;
- if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
- LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
- }
-}
-
-
-int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
-{
- LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_stream;
- const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
-
- const BYTE* smallest = (const BYTE*) source;
- if (streamPtr->initCheck) return 0; /* Uninitialized structure detected */
- if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd;
- LZ4_renormDictT(streamPtr, smallest);
- if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
-
- /* Check overlapping input/dictionary space */
- {
- const BYTE* sourceEnd = (const BYTE*) source + inputSize;
- if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd))
- {
- streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
- if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
- if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
- streamPtr->dictionary = dictEnd - streamPtr->dictSize;
- }
- }
-
- /* prefix mode : source data follows dictionary */
- if (dictEnd == (const BYTE*)source)
- {
- int result;
- if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration);
- else
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration);
- streamPtr->dictSize += (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
- return result;
- }
-
- /* external dictionary mode */
- {
- int result;
- if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration);
- else
- result = LZ4_compress_generic(LZ4_stream, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration);
- streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
- return result;
- }
-}
-
-
-/* Hidden debug function, to force external dictionary mode */
-int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize)
-{
- LZ4_stream_t_internal* streamPtr = (LZ4_stream_t_internal*)LZ4_dict;
- int result;
- const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
-
- const BYTE* smallest = dictEnd;
- if (smallest > (const BYTE*) source) smallest = (const BYTE*) source;
- LZ4_renormDictT((LZ4_stream_t_internal*)LZ4_dict, smallest);
-
- result = LZ4_compress_generic(LZ4_dict, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
-
- streamPtr->dictionary = (const BYTE*)source;
- streamPtr->dictSize = (U32)inputSize;
- streamPtr->currentOffset += (U32)inputSize;
-
- return result;
-}
-
-
-int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
-{
- LZ4_stream_t_internal* dict = (LZ4_stream_t_internal*) LZ4_dict;
- const BYTE* previousDictEnd = dict->dictionary + dict->dictSize;
-
- if ((U32)dictSize > 64 KB) dictSize = 64 KB; /* useless to define a dictionary > 64 KB */
- if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
-
- memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
-
- dict->dictionary = (const BYTE*)safeBuffer;
- dict->dictSize = (U32)dictSize;
-
- return dictSize;
-}
-
-
-
-/*******************************
-* Decompression functions
-*******************************/
-/*
- * This generic decompression function cover all use cases.
- * It shall be instantiated several times, using different sets of directives
- * Note that it is essential this generic function is really inlined,
- * in order to remove useless branches during compilation optimization.
- */
-FORCE_INLINE int LZ4_decompress_generic(
- const char* const source,
- char* const dest,
- int inputSize,
- int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
-
- int endOnInput, /* endOnOutputSize, endOnInputSize */
- int partialDecoding, /* full, partial */
- int targetOutputSize, /* only used if partialDecoding==partial */
- int dict, /* noDict, withPrefix64k, usingExtDict */
- const BYTE* const lowPrefix, /* == dest if dict == noDict */
- const BYTE* const dictStart, /* only if dict==usingExtDict */
- const size_t dictSize /* note : = 0 if noDict */
- )
-{
- /* Local Variables */
- const BYTE* ip = (const BYTE*) source;
- const BYTE* const iend = ip + inputSize;
-
- BYTE* op = (BYTE*) dest;
- BYTE* const oend = op + outputSize;
- BYTE* cpy;
- BYTE* oexit = op + targetOutputSize;
- const BYTE* const lowLimit = lowPrefix - dictSize;
-
- const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
- const size_t dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4};
- const size_t dec64table[] = {0, 0, 0, (size_t)-1, 0, 1, 2, 3};
-
- const int safeDecode = (endOnInput==endOnInputSize);
- const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
-
-
- /* Special cases */
- if ((partialDecoding) && (oexit> oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
- if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
- if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
-
-
- /* Main Loop */
- while (1)
- {
- unsigned token;
- size_t length;
- const BYTE* match;
-
- /* get literal length */
- token = *ip++;
- if ((length=(token>>ML_BITS)) == RUN_MASK)
- {
- unsigned s;
- do
- {
- s = *ip++;
- length += s;
- }
- while (likely((endOnInput)?ip<iend-RUN_MASK:1) && (s==255));
- if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)(op))) goto _output_error; /* overflow detection */
- if ((safeDecode) && unlikely((size_t)(ip+length)<(size_t)(ip))) goto _output_error; /* overflow detection */
- }
-
- /* copy literals */
- cpy = op+length;
- if (((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
- || ((!endOnInput) && (cpy>oend-COPYLENGTH)))
- {
- if (partialDecoding)
- {
- if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
- if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
- }
- else
- {
- if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
- if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
- }
- memcpy(op, ip, length);
- ip += length;
- op += length;
- break; /* Necessarily EOF, due to parsing restrictions */
- }
- LZ4_wildCopy(op, ip, cpy);
- ip += length; op = cpy;
-
- /* get offset */
- match = cpy - LZ4_readLE16(ip); ip+=2;
- if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */
-
- /* get matchlength */
- length = token & ML_MASK;
- if (length == ML_MASK)
- {
- unsigned s;
- do
- {
- if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
- s = *ip++;
- length += s;
- } while (s==255);
- if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */
- }
- length += MINMATCH;
-
- /* check external dictionary */
- if ((dict==usingExtDict) && (match < lowPrefix))
- {
- if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
-
- if (length <= (size_t)(lowPrefix-match))
- {
- /* match can be copied as a single segment from external dictionary */
- match = dictEnd - (lowPrefix-match);
- memmove(op, match, length); op += length;
- }
- else
- {
- /* match encompass external dictionary and current segment */
- size_t copySize = (size_t)(lowPrefix-match);
- memcpy(op, dictEnd - copySize, copySize);
- op += copySize;
- copySize = length - copySize;
- if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */
- {
- BYTE* const endOfMatch = op + copySize;
- const BYTE* copyFrom = lowPrefix;
- while (op < endOfMatch) *op++ = *copyFrom++;
- }
- else
- {
- memcpy(op, lowPrefix, copySize);
- op += copySize;
- }
- }
- continue;
- }
-
- /* copy repeated sequence */
- cpy = op + length;
- if (unlikely((op-match)<8))
- {
- const size_t dec64 = dec64table[op-match];
- op[0] = match[0];
- op[1] = match[1];
- op[2] = match[2];
- op[3] = match[3];
- match += dec32table[op-match];
- LZ4_copy4(op+4, match);
- op += 8; match -= dec64;
- } else { LZ4_copy8(op, match); op+=8; match+=8; }
-
- if (unlikely(cpy>oend-12))
- {
- if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */
- if (op < oend-8)
- {
- LZ4_wildCopy(op, match, oend-8);
- match += (oend-8) - op;
- op = oend-8;
- }
- while (op<cpy) *op++ = *match++;
- }
- else
- LZ4_wildCopy(op, match, cpy);
- op=cpy; /* correction */
- }
-
- /* end of decoding */
- if (endOnInput)
- return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
- else
- return (int) (((const char*)ip)-source); /* Nb of input bytes read */
-
- /* Overflow error detected */
-_output_error:
- return (int) (-(((const char*)ip)-source))-1;
-}
-
-
-int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
-}
-
-int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
-}
-
-int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
-{
- return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
-}
-
-
-/* streaming decompression functions */
-
-typedef struct
-{
- const BYTE* externalDict;
- size_t extDictSize;
- const BYTE* prefixEnd;
- size_t prefixSize;
-} LZ4_streamDecode_t_internal;
-
-/*
- * If you prefer dynamic allocation methods,
- * LZ4_createStreamDecode()
- * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure.
- */
-LZ4_streamDecode_t* LZ4_createStreamDecode(void)
-{
- LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t));
- return lz4s;
-}
-
-int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
-{
- FREEMEM(LZ4_stream);
- return 0;
-}
-
-/*
- * LZ4_setStreamDecode
- * Use this function to instruct where to find the dictionary
- * This function is not necessary if previous data is still available where it was decoded.
- * Loading a size of 0 is allowed (same effect as no dictionary).
- * Return : 1 if OK, 0 if error
- */
-int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode;
- lz4sd->prefixSize = (size_t) dictSize;
- lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
- lz4sd->externalDict = NULL;
- lz4sd->extDictSize = 0;
- return 1;
-}
-
-/*
-*_continue() :
- These decoding functions allow decompression of multiple blocks in "streaming" mode.
- Previously decoded blocks must still be available at the memory position where they were decoded.
- If it's not possible, save the relevant part of decoded data into a safe buffer,
- and indicate where it stands using LZ4_setStreamDecode()
-*/
-int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode;
- int result;
-
- if (lz4sd->prefixEnd == (BYTE*)dest)
- {
- result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0,
- usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize += result;
- lz4sd->prefixEnd += result;
- }
- else
- {
- lz4sd->extDictSize = lz4sd->prefixSize;
- lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
- result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
- endOnInputSize, full, 0,
- usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = result;
- lz4sd->prefixEnd = (BYTE*)dest + result;
- }
-
- return result;
-}
-
-int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
-{
- LZ4_streamDecode_t_internal* lz4sd = (LZ4_streamDecode_t_internal*) LZ4_streamDecode;
- int result;
-
- if (lz4sd->prefixEnd == (BYTE*)dest)
- {
- result = LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0,
- usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize += originalSize;
- lz4sd->prefixEnd += originalSize;
- }
- else
- {
- lz4sd->extDictSize = lz4sd->prefixSize;
- lz4sd->externalDict = (BYTE*)dest - lz4sd->extDictSize;
- result = LZ4_decompress_generic(source, dest, 0, originalSize,
- endOnOutputSize, full, 0,
- usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
- if (result <= 0) return result;
- lz4sd->prefixSize = originalSize;
- lz4sd->prefixEnd = (BYTE*)dest + originalSize;
- }
-
- return result;
-}
-
-
-/*
-Advanced decoding functions :
-*_usingDict() :
- These decoding functions work the same as "_continue" ones,
- the dictionary must be explicitly provided within parameters
-*/
-
-FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
-{
- if (dictSize==0)
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
- if (dictStart+dictSize == dest)
- {
- if (dictSize >= (int)(64 KB - 1))
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
- }
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
-}
-
-int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
-}
-
-/* debug function */
-int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
-}
-
-
-/***************************************************
-* Obsolete Functions
-***************************************************/
-/* obsolete compression functions */
-int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
-int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
-int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
-int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
-int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
-
-/*
-These function names are deprecated and should no longer be used.
-They are only provided here for compatibility with older user programs.
-- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
-- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
-*/
-int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
-int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
-
-
-/* Obsolete Streaming functions */
-
-int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
-
-static void LZ4_init(LZ4_stream_t_internal* lz4ds, BYTE* base)
-{
- MEM_INIT(lz4ds, 0, LZ4_STREAMSIZE);
- lz4ds->bufferStart = base;
-}
-
-int LZ4_resetStreamState(void* state, char* inputBuffer)
-{
- if ((((size_t)state) & 3) != 0) return 1; /* Error : pointer is not aligned on 4-bytes boundary */
- LZ4_init((LZ4_stream_t_internal*)state, (BYTE*)inputBuffer);
- return 0;
-}
-
-void* LZ4_create (char* inputBuffer)
-{
- void* lz4ds = ALLOCATOR(8, LZ4_STREAMSIZE_U64);
- LZ4_init ((LZ4_stream_t_internal*)lz4ds, (BYTE*)inputBuffer);
- return lz4ds;
-}
-
-char* LZ4_slideInputBuffer (void* LZ4_Data)
-{
- LZ4_stream_t_internal* ctx = (LZ4_stream_t_internal*)LZ4_Data;
- int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB);
- return (char*)(ctx->bufferStart + dictSize);
-}
-
-/* Obsolete streaming decompression functions */
-
-int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
-{
- return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
-}
-
-int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
-{
- return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
-}
-
-#endif /* LZ4_COMMONDEFS_ONLY */
-
diff --git a/ext/lz4/lz4.h b/ext/lz4/lz4.h
deleted file mode 100644
index 3e740022..00000000
--- a/ext/lz4/lz4.h
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- LZ4 - Fast LZ compression algorithm
- Header File
- Copyright (C) 2011-2015, Yann Collet.
-
- BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
-
- 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.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER 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.
-
- You can contact the author at :
- - LZ4 source repository : https://github.com/Cyan4973/lz4
- - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
-*/
-#pragma once
-
-#if defined (__cplusplus)
-extern "C" {
-#endif
-
-/*
- * lz4.h provides block compression functions, and gives full buffer control to programmer.
- * If you need to generate inter-operable compressed data (respecting LZ4 frame specification),
- * and can let the library handle its own memory, please use lz4frame.h instead.
-*/
-
-/**************************************
-* Version
-**************************************/
-#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
-#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
-#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
-#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
-int LZ4_versionNumber (void);
-
-/**************************************
-* Tuning parameter
-**************************************/
-/*
- * LZ4_MEMORY_USAGE :
- * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
- * Increasing memory usage improves compression ratio
- * Reduced memory usage can improve speed, due to cache effect
- * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
- */
-#define LZ4_MEMORY_USAGE 14
-
-
-/**************************************
-* Simple Functions
-**************************************/
-
-int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
-int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
-
-/*
-LZ4_compress_default() :
- Compresses 'sourceSize' bytes from buffer 'source'
- into already allocated 'dest' buffer of size 'maxDestSize'.
- Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
- It also runs faster, so it's a recommended setting.
- If the function cannot compress 'source' into a more limited 'dest' budget,
- compression stops *immediately*, and the function result is zero.
- As a consequence, 'dest' content is not valid.
- This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
- sourceSize : Max supported value is LZ4_MAX_INPUT_VALUE
- maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
- return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
- or 0 if compression fails
-
-LZ4_decompress_safe() :
- compressedSize : is the precise full size of the compressed block.
- maxDecompressedSize : is the size of destination buffer, which must be already allocated.
- return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
- If destination buffer is not large enough, decoding will stop and output an error code (<0).
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- This function is protected against buffer overflow exploits, including malicious data packets.
- It never writes outside output buffer, nor reads outside input buffer.
-*/
-
-
-/**************************************
-* Advanced Functions
-**************************************/
-#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
-#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
-
-/*
-LZ4_compressBound() :
- Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
- This function is primarily useful for memory allocation purposes (destination buffer size).
- Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
- Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
- inputSize : max supported value is LZ4_MAX_INPUT_SIZE
- return : maximum output size in a "worst case" scenario
- or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
-*/
-int LZ4_compressBound(int inputSize);
-
-/*
-LZ4_compress_fast() :
- Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
- The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
- It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
- An acceleration value of "1" is the same as regular LZ4_compress_default()
- Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
-*/
-int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
-
-
-/*
-LZ4_compress_fast_extState() :
- Same compression function, just using an externally allocated memory space to store compression state.
- Use LZ4_sizeofState() to know how much memory must be allocated,
- and allocate it on 8-bytes boundaries (using malloc() typically).
- Then, provide it as 'void* state' to compression function.
-*/
-int LZ4_sizeofState(void);
-int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
-
-
-/*
-LZ4_compress_destSize() :
- Reverse the logic, by compressing as much data as possible from 'source' buffer
- into already allocated buffer 'dest' of size 'targetDestSize'.
- This function either compresses the entire 'source' content into 'dest' if it's large enough,
- or fill 'dest' buffer completely with as much data as possible from 'source'.
- *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
- New value is necessarily <= old value.
- return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
- or 0 if compression fails
-*/
-int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
-
-
-/*
-LZ4_decompress_fast() :
- originalSize : is the original and therefore uncompressed size
- return : the number of bytes read from the source buffer (in other words, the compressed size)
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
- note : This function fully respect memory boundaries for properly formed compressed data.
- It is a bit faster than LZ4_decompress_safe().
- However, it does not provide any protection against intentionally modified data stream (malicious input).
- Use this function in trusted environment only (data to decode comes from a trusted source).
-*/
-int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
-
-/*
-LZ4_decompress_safe_partial() :
- This function decompress a compressed block of size 'compressedSize' at position 'source'
- into destination buffer 'dest' of size 'maxDecompressedSize'.
- The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
- reducing decompression time.
- return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
- Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
- Always control how many bytes were decoded.
- If the source stream is detected malformed, the function will stop decoding and return a negative result.
- This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
-*/
-int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
-
-
-/***********************************************
-* Streaming Compression Functions
-***********************************************/
-#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
-#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(long long))
-/*
- * LZ4_stream_t
- * information structure to track an LZ4 stream.
- * important : init this structure content before first use !
- * note : only allocated directly the structure if you are statically linking LZ4
- * If you are using liblz4 as a DLL, please use below construction methods instead.
- */
-typedef struct { long long table[LZ4_STREAMSIZE_U64]; } LZ4_stream_t;
-
-/*
- * LZ4_resetStream
- * Use this function to init an allocated LZ4_stream_t structure
- */
-void LZ4_resetStream (LZ4_stream_t* streamPtr);
-
-/*
- * LZ4_createStream will allocate and initialize an LZ4_stream_t structure
- * LZ4_freeStream releases its memory.
- * In the context of a DLL (liblz4), please use these methods rather than the static struct.
- * They are more future proof, in case of a change of LZ4_stream_t size.
- */
-LZ4_stream_t* LZ4_createStream(void);
-int LZ4_freeStream (LZ4_stream_t* streamPtr);
-
-/*
- * LZ4_loadDict
- * Use this function to load a static dictionary into LZ4_stream.
- * Any previous data will be forgotten, only 'dictionary' will remain in memory.
- * Loading a size of 0 is allowed.
- * Return : dictionary size, in bytes (necessarily <= 64 KB)
- */
-int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
-
-/*
- * LZ4_compress_fast_continue
- * Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
- * Important : Previous data blocks are assumed to still be present and unmodified !
- * 'dst' buffer must be already allocated.
- * If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
- * If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
- */
-int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
-
-/*
- * LZ4_saveDict
- * If previously compressed data block is not guaranteed to remain available at its memory location
- * save it into a safer place (char* safeBuffer)
- * Note : you don't need to call LZ4_loadDict() afterwards,
- * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue()
- * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error
- */
-int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
-
-
-/************************************************
-* Streaming Decompression Functions
-************************************************/
-
-#define LZ4_STREAMDECODESIZE_U64 4
-#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
-typedef struct { unsigned long long table[LZ4_STREAMDECODESIZE_U64]; } LZ4_streamDecode_t;
-/*
- * LZ4_streamDecode_t
- * information structure to track an LZ4 stream.
- * init this structure content using LZ4_setStreamDecode or memset() before first use !
- *
- * In the context of a DLL (liblz4) please prefer usage of construction methods below.
- * They are more future proof, in case of a change of LZ4_streamDecode_t size in the future.
- * LZ4_createStreamDecode will allocate and initialize an LZ4_streamDecode_t structure
- * LZ4_freeStreamDecode releases its memory.
- */
-LZ4_streamDecode_t* LZ4_createStreamDecode(void);
-int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
-
-/*
- * LZ4_setStreamDecode
- * Use this function to instruct where to find the dictionary.
- * Setting a size of 0 is allowed (same effect as reset).
- * Return : 1 if OK, 0 if error
- */
-int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
-
-/*
-*_continue() :
- These decoding functions allow decompression of multiple blocks in "streaming" mode.
- Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
- In the case of a ring buffers, decoding buffer must be either :
- - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
- In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
- - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
- maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
- In which case, encoding and decoding buffers do not need to be synchronized,
- and encoding ring buffer can have any size, including small ones ( < 64 KB).
- - _At least_ 64 KB + 8 bytes + maxBlockSize.
- In which case, encoding and decoding buffers do not need to be synchronized,
- and encoding ring buffer can have any size, including larger than decoding buffer.
- Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
- and indicate where it is saved using LZ4_setStreamDecode()
-*/
-int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
-int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
-
-
-/*
-Advanced decoding functions :
-*_usingDict() :
- These decoding functions work the same as
- a combination of LZ4_setStreamDecode() followed by LZ4_decompress_x_continue()
- They are stand-alone. They don't need nor update an LZ4_streamDecode_t structure.
-*/
-int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
-int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
-
-
-
-/**************************************
-* Obsolete Functions
-**************************************/
-/* Deprecate Warnings */
-/* Should these warnings messages be a problem,
- it is generally possible to disable them,
- with -Wno-deprecated-declarations for gcc
- or _CRT_SECURE_NO_WARNINGS in Visual for example.
- You can also define LZ4_DEPRECATE_WARNING_DEFBLOCK. */
-#ifndef LZ4_DEPRECATE_WARNING_DEFBLOCK
-# define LZ4_DEPRECATE_WARNING_DEFBLOCK
-# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-# if (LZ4_GCC_VERSION >= 405) || defined(__clang__)
-# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
-# elif (LZ4_GCC_VERSION >= 301)
-# define LZ4_DEPRECATED(message) __attribute__((deprecated))
-# elif defined(_MSC_VER)
-# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
-# else
-# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
-# define LZ4_DEPRECATED(message)
-# endif
-#endif /* LZ4_DEPRECATE_WARNING_DEFBLOCK */
-
-/* Obsolete compression functions */
-/* These functions are planned to start generate warnings by r131 approximately */
-int LZ4_compress (const char* source, char* dest, int sourceSize);
-int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
-int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
-int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
-int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
-int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
-
-/* Obsolete decompression functions */
-/* These function names are completely deprecated and must no longer be used.
- They are only provided here for compatibility with older programs.
- - LZ4_uncompress is the same as LZ4_decompress_fast
- - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
- These function prototypes are now disabled; uncomment them only if you really need them.
- It is highly recommended to stop using these prototypes and migrate to maintained ones */
-/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
-/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
-
-/* Obsolete streaming functions; use new streaming interface whenever possible */
-LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
-LZ4_DEPRECATED("use LZ4_createStream() instead") int LZ4_sizeofStreamState(void);
-LZ4_DEPRECATED("use LZ4_resetStream() instead") int LZ4_resetStreamState(void* state, char* inputBuffer);
-LZ4_DEPRECATED("use LZ4_saveDict() instead") char* LZ4_slideInputBuffer (void* state);
-
-/* Obsolete streaming decoding functions */
-LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
-LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
-
-
-#if defined (__cplusplus)
-}
-#endif
diff --git a/ext/miniupnpc/Changelog.txt b/ext/miniupnpc/Changelog.txt
index 078bebce..37562c6a 100644
--- a/ext/miniupnpc/Changelog.txt
+++ b/ext/miniupnpc/Changelog.txt
@@ -1,6 +1,20 @@
-$Id: Changelog.txt,v 1.223 2016/04/19 21:06:20 nanard Exp $
+$Id: Changelog.txt,v 1.229 2017/12/12 11:26:25 nanard Exp $
miniUPnP client Changelog.
+2017/12/11:
+ Fix buffer over run in minixml.c
+ Fix uninitialized variable access in upnpreplyparse.c
+
+2017/05/05:
+ Fix CVE-2017-8798 Thanks to tin/Team OSTStrom
+
+2016/11/11:
+ check strlen before memcmp in XML parsing portlistingparse.c
+ fix build under SOLARIS and CYGWIN
+
+2016/10/11:
+ Add python 3 compatibility to IGD test
+
VERSION 2.0 : released 2016/04/19
2016/01/24:
diff --git a/ext/miniupnpc/LICENSE b/ext/miniupnpc/LICENSE
index cb5a0604..08167337 100644
--- a/ext/miniupnpc/LICENSE
+++ b/ext/miniupnpc/LICENSE
@@ -1,5 +1,5 @@
MiniUPnPc
-Copyright (c) 2005-2015, Thomas BERNARD
+Copyright (c) 2005-2016, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/ext/miniupnpc/MANIFEST.in b/ext/miniupnpc/MANIFEST.in
deleted file mode 100644
index 54b86f95..00000000
--- a/ext/miniupnpc/MANIFEST.in
+++ /dev/null
@@ -1,5 +0,0 @@
-include README
-include miniupnpcmodule.c
-include setup.py
-include *.h
-include libminiupnpc.a
diff --git a/ext/miniupnpc/README b/ext/miniupnpc/README
index 91535dbc..0d3b8054 100644
--- a/ext/miniupnpc/README
+++ b/ext/miniupnpc/README
@@ -1,9 +1,8 @@
Project: miniupnp
-Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
github: https://github.com/miniupnp/miniupnp
-freecode: http://freecode.com/projects/miniupnp
Author: Thomas Bernard
-Copyright (c) 2005-2016 Thomas Bernard
+Copyright (c) 2005-2017 Thomas Bernard
This software is subject to the conditions detailed in the
LICENSE file provided within this distribution.
@@ -58,7 +57,7 @@ If you are using libminiupnpc in your application, please
send me an email !
For any question, you can use the web forum :
-http://miniupnp.tuxfamily.org/forum/
+https://miniupnp.tuxfamily.org/forum/
Bugs should be reported on github :
https://github.com/miniupnp/miniupnp/issues
diff --git a/ext/miniupnpc/apiversions.txt b/ext/miniupnpc/apiversions.txt
deleted file mode 100644
index 9464a867..00000000
--- a/ext/miniupnpc/apiversions.txt
+++ /dev/null
@@ -1,172 +0,0 @@
-$Id: apiversions.txt,v 1.9 2016/01/24 17:24:36 nanard Exp $
-
-Differences in API between miniUPnPc versions
-
-API version 16
- added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
- updated macro :
- #define MINIUPNPC_API_VERSION 16
-
-API version 15
- changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
- to "localport". When 0 or 1, behaviour is not changed, but it can take
- any other value between 2 and 65535
- Existing programs should be compatible
- updated macro :
- #define MINIUPNPC_API_VERSION 15
-
-API version 14
-miniupnpc.h
- add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
- upnpDiscoverDevices()
- getDevicesFromMiniSSDPD() :
- connectToMiniSSDPD() / disconnectFromMiniSSDPD()
- requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
- updated macro :
- #define MINIUPNPC_API_VERSION 14
-
-API version 13
-miniupnpc.h:
- add searchalltype param to upnpDiscoverDevices() function
- updated macro :
- #define MINIUPNPC_API_VERSION 13
-
-API version 12
-miniupnpc.h :
- add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
- functions
- updated macros :
- #define MINIUPNPC_API_VERSION 12
-
-API version 11
-
-upnpreplyparse.h / portlistingparse.h :
- removed usage of sys/queue.h / bsdqueue.h
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_API_VERSION 11
-
-====================== miniUPnPc version 1.9 ======================
-API version 10
-
-upnpcommands.h:
- added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_VERSION "1.9"
- #define MINIUPNPC_API_VERSION 10
-
-====================== miniUPnPc version 1.8 ======================
-API version 9
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_VERSION "1.8"
- #define MINIUPNPC_API_VERSION 9
- added "unsigned int scope_id;" to struct UPNPDev
- added scope_id argument to GetUPNPUrls()
-
-
-
-====================== miniUPnPc version 1.7 ======================
-API version 8
-
-miniupnpc.h :
- add new macros :
- #define MINIUPNPC_VERSION "1.7"
- #define MINIUPNPC_API_VERSION 8
- add rootdescURL to struct UPNPUrls
-
-
-
-====================== miniUPnPc version 1.6 ======================
-API version 8
-
-Adding support for IPv6.
-igd_desc_parse.h :
- struct IGDdatas_service :
- add char presentationurl[MINIUPNPC_URL_MAXSIZE];
- struct IGDdatas :
- add struct IGDdatas_service IPv6FC;
-miniupnpc.h :
- new macros :
- #define UPNPDISCOVER_SUCCESS (0)
- #define UPNPDISCOVER_UNKNOWN_ERROR (-1)
- #define UPNPDISCOVER_SOCKET_ERROR (-101)
- #define UPNPDISCOVER_MEMORY_ERROR (-102)
- simpleUPnPcommand() prototype changed (but is normaly not used by API users)
- add arguments ipv6 and error to upnpDiscover() :
- struct UPNPDev *
- upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport,
- int ipv6,
- int * error);
- add controlURL_6FC member to struct UPNPUrls :
- struct UPNPUrls {
- char * controlURL;
- char * ipcondescURL;
- char * controlURL_CIF;
- char * controlURL_6FC;
- };
-
-upnpcommands.h :
- add leaseDuration argument to UPNP_AddPortMapping()
- add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
- add UPNP_GetListOfPortMappings() function (IGDv2)
- add IGDv2 IPv6 related functions :
- UPNP_GetFirewallStatus()
- UPNP_GetOutboundPinholeTimeout()
- UPNP_AddPinhole()
- UPNP_UpdatePinhole()
- UPNP_DeletePinhole()
- UPNP_CheckPinholeWorking()
- UPNP_GetPinholePackets()
-
-
-
-====================== miniUPnPc version 1.5 ======================
-API version 5
-
-new function :
-int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
-new macro in upnpcommands.h :
-#define UPNPCOMMAND_HTTP_ERROR
-
-====================== miniUPnPc version 1.4 ======================
-Same API as version 1.3
-
-====================== miniUPnPc version 1.3 ======================
-API version 4
-
-Use UNSIGNED_INTEGER type for
-UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
-UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
-Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
-
-====================== miniUPnPc version 1.2 ======================
-API version 3
-
-added sameport argument to upnpDiscover()
-struct UPNPDev *
-upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport);
-
-====================== miniUPnPc Version 1.1 ======================
-Same API as 1.0
-
-
-====================== miniUPnPc Version 1.0 ======================
-API version 2
-
-
-struct UPNPDev {
- struct UPNPDev * pNext;
- char * descURL;
- char * st;
- char buffer[2];
-};
-struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock);
-
diff --git a/ext/miniupnpc/connecthostport.c b/ext/miniupnpc/connecthostport.c
index 854203e0..aed62c76 100644
--- a/ext/miniupnpc/connecthostport.c
+++ b/ext/miniupnpc/connecthostport.c
@@ -1,12 +1,10 @@
-/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
+/* $Id: connecthostport.c,v 1.17 2017/04/21 09:58:30 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2010-2015 Thomas Bernard
+ * Copyright (c) 2010-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
-#define _CRT_SECURE_NO_WARNINGS
-
/* use getaddrinfo() or gethostbyname()
* uncomment the following line in order to use gethostbyname() */
#ifdef NO_GETADDRINFO
@@ -38,15 +36,13 @@
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
* during the connect() call */
#define MINIUPNPC_IGNORE_EINTR
-#ifndef USE_GETHOSTBYNAME
#include <sys/socket.h>
#include <sys/select.h>
-#endif /* #ifndef USE_GETHOSTBYNAME */
#endif /* #else _WIN32 */
/* definition of PRINT_SOCKET_ERROR */
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
@@ -102,13 +98,13 @@ int connecthostport(const char * host, unsigned short port,
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
}
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
}
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
dest.sin_family = AF_INET;
diff --git a/ext/miniupnpc/external-ip.sh b/ext/miniupnpc/external-ip.sh
deleted file mode 100755
index 965d86b2..00000000
--- a/ext/miniupnpc/external-ip.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# $Id: external-ip.sh,v 1.1 2010/08/05 12:57:41 nanard Exp $
-# (c) 2010 Reuben Hawkins
-upnpc -s | grep ExternalIPAddress | sed 's/[^0-9\.]//g'
diff --git a/ext/miniupnpc/minihttptestserver.c b/ext/miniupnpc/minihttptestserver.c
deleted file mode 100644
index 6663bc08..00000000
--- a/ext/miniupnpc/minihttptestserver.c
+++ /dev/null
@@ -1,659 +0,0 @@
-/* $Id: minihttptestserver.c,v 1.19 2015/11/17 09:07:17 nanard Exp $ */
-/* Project : miniUPnP
- * Author : Thomas Bernard
- * Copyright (c) 2011-2015 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <signal.h>
-#include <time.h>
-#include <errno.h>
-
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001
-#endif
-
-#define CRAP_LENGTH (2048)
-
-volatile sig_atomic_t quit = 0;
-volatile sig_atomic_t child_to_wait_for = 0;
-
-/**
- * signal handler for SIGCHLD (child status has changed)
- */
-void handle_signal_chld(int sig)
-{
- (void)sig;
- /* printf("handle_signal_chld(%d)\n", sig); */
- ++child_to_wait_for;
-}
-
-/**
- * signal handler for SIGINT (CRTL C)
- */
-void handle_signal_int(int sig)
-{
- (void)sig;
- /* printf("handle_signal_int(%d)\n", sig); */
- quit = 1;
-}
-
-/**
- * build a text/plain content of the specified length
- */
-void build_content(char * p, int n)
-{
- char line_buffer[80];
- int k;
- int i = 0;
-
- while(n > 0) {
- k = snprintf(line_buffer, sizeof(line_buffer),
- "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n",
- i, i);
- if(k != 64) {
- fprintf(stderr, "snprintf() returned %d in build_content()\n", k);
- }
- ++i;
- if(n >= 64) {
- memcpy(p, line_buffer, 64);
- p += 64;
- n -= 64;
- } else {
- memcpy(p, line_buffer, n);
- p += n;
- n = 0;
- }
- }
-}
-
-/**
- * build crappy content
- */
-void build_crap(char * p, int n)
-{
- static const char crap[] = "_CRAP_\r\n";
- int i;
-
- while(n > 0) {
- i = sizeof(crap) - 1;
- if(i > n)
- i = n;
- memcpy(p, crap, i);
- p += i;
- n -= i;
- }
-}
-
-/**
- * build chunked response.
- * return a malloc'ed buffer
- */
-char * build_chunked_response(int content_length, int * response_len)
-{
- char * response_buffer;
- char * content_buffer;
- int buffer_length;
- int i, n;
-
- /* allocate to have some margin */
- buffer_length = 256 + content_length + (content_length >> 4);
- response_buffer = malloc(buffer_length);
- if(response_buffer == NULL)
- return NULL;
- *response_len = snprintf(response_buffer, buffer_length,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: text/plain\r\n"
- "Transfer-Encoding: chunked\r\n"
- "\r\n");
-
- /* build the content */
- content_buffer = malloc(content_length);
- if(content_buffer == NULL) {
- free(response_buffer);
- return NULL;
- }
- build_content(content_buffer, content_length);
-
- /* chunk it */
- i = 0;
- while(i < content_length) {
- n = (rand() % 199) + 1;
- if(i + n > content_length) {
- n = content_length - i;
- }
- /* TODO : check buffer size ! */
- *response_len += snprintf(response_buffer + *response_len,
- buffer_length - *response_len,
- "%x\r\n", n);
- memcpy(response_buffer + *response_len, content_buffer + i, n);
- *response_len += n;
- i += n;
- response_buffer[(*response_len)++] = '\r';
- response_buffer[(*response_len)++] = '\n';
- }
- /* the last chunk : "0\r\n" a empty body and then
- * the final "\r\n" */
- memcpy(response_buffer + *response_len, "0\r\n\r\n", 5);
- *response_len += 5;
- free(content_buffer);
-
- printf("resp_length=%d buffer_length=%d content_length=%d\n",
- *response_len, buffer_length, content_length);
- return response_buffer;
-}
-
-/* favicon.ico generator */
-#ifdef OLD_HEADER
-#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4)
-#else
-#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4)
-#endif
-void build_favicon_content(char * p, int n)
-{
- int i;
- if(n < FAVICON_LENGTH)
- return;
- /* header : 6 bytes */
- *p++ = 0;
- *p++ = 0;
- *p++ = 1; /* type : ICO */
- *p++ = 0;
- *p++ = 1; /* number of images in file */
- *p++ = 0;
- /* image directory (1 entry) : 16 bytes */
- *p++ = 16; /* width */
- *p++ = 16; /* height */
- *p++ = 2; /* number of colors in the palette. 0 = no palette */
- *p++ = 0; /* reserved */
- *p++ = 1; /* color planes */
- *p++ = 0; /* " */
- *p++ = 1; /* bpp */
- *p++ = 0; /* " */
-#ifdef OLD_HEADER
- *p++ = 12 + 8 + 32 * 4; /* bmp size */
-#else
- *p++ = 40 + 8 + 32 * 4; /* bmp size */
-#endif
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 6 + 16; /* bmp offset */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- /* BMP */
-#ifdef OLD_HEADER
- /* BITMAPCOREHEADER */
- *p++ = 12; /* size of this header */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 16; /* width */
- *p++ = 0; /* " */
- *p++ = 16 * 2; /* height x 2 ! */
- *p++ = 0; /* " */
- *p++ = 1; /* color planes */
- *p++ = 0; /* " */
- *p++ = 1; /* bpp */
- *p++ = 0; /* " */
-#else
- /* BITMAPINFOHEADER */
- *p++ = 40; /* size of this header */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 16; /* width */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 16 * 2; /* height x 2 ! */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 0; /* " */
- *p++ = 1; /* color planes */
- *p++ = 0; /* " */
- *p++ = 1; /* bpp */
- *p++ = 0; /* " */
- /* compression method, image size, ppm x, ppm y */
- /* colors in the palette ? */
- /* important colors */
- for(i = 4 * 6; i > 0; --i)
- *p++ = 0;
-#endif
- /* palette */
- *p++ = 0; /* b */
- *p++ = 0; /* g */
- *p++ = 0; /* r */
- *p++ = 0; /* reserved */
- *p++ = 255; /* b */
- *p++ = 255; /* g */
- *p++ = 255; /* r */
- *p++ = 0; /* reserved */
- /* pixel data */
- for(i = 16; i > 0; --i) {
- if(i & 1) {
- *p++ = 0125;
- *p++ = 0125;
- } else {
- *p++ = 0252;
- *p++ = 0252;
- }
- *p++ = 0;
- *p++ = 0;
- }
- /* Opacity MASK */
- for(i = 16 * 4; i > 0; --i) {
- *p++ = 0;
- }
-}
-
-enum modes {
- MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON
-};
-
-const struct {
- const enum modes mode;
- const char * text;
-} modes_array[] = {
- {MODE_CHUNKED, "chunked"},
- {MODE_ADDCRAP, "addcrap"},
- {MODE_NORMAL, "normal"},
- {MODE_FAVICON, "favicon.ico"},
- {MODE_INVALID, NULL}
-};
-
-/**
- * write the response with random behaviour !
- */
-void send_response(int c, const char * buffer, int len)
-{
- int n;
- while(len > 0) {
- n = (rand() % 99) + 1;
- if(n > len)
- n = len;
- n = write(c, buffer, n);
- if(n < 0) {
- if(errno != EINTR) {
- perror("write");
- return;
- }
- /* if errno == EINTR, try again */
- } else {
- len -= n;
- buffer += n;
- }
- usleep(10000); /* 10ms */
- }
-}
-
-/**
- * handle the HTTP connection
- */
-void handle_http_connection(int c)
-{
- char request_buffer[2048];
- int request_len = 0;
- int headers_found = 0;
- int n, i;
- char request_method[16];
- char request_uri[256];
- char http_version[16];
- char * p;
- char * response_buffer;
- int response_len;
- enum modes mode;
- int content_length = 16*1024;
-
- /* read the request */
- while(request_len < (int)sizeof(request_buffer) && !headers_found) {
- n = read(c,
- request_buffer + request_len,
- sizeof(request_buffer) - request_len);
- if(n < 0) {
- if(errno == EINTR)
- continue;
- perror("read");
- return;
- } else if(n==0) {
- /* remote host closed the connection */
- break;
- } else {
- request_len += n;
- for(i = 0; i < request_len - 3; i++) {
- if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) {
- /* found the end of headers */
- headers_found = 1;
- break;
- }
- }
- }
- }
- if(!headers_found) {
- /* error */
- printf("no HTTP header found in the request\n");
- return;
- }
- printf("headers :\n%.*s", request_len, request_buffer);
- /* the request have been received, now parse the request line */
- p = request_buffer;
- for(i = 0; i < (int)sizeof(request_method) - 1; i++) {
- if(*p == ' ' || *p == '\r')
- break;
- request_method[i] = *p;
- ++p;
- }
- request_method[i] = '\0';
- while(*p == ' ')
- p++;
- for(i = 0; i < (int)sizeof(request_uri) - 1; i++) {
- if(*p == ' ' || *p == '\r')
- break;
- request_uri[i] = *p;
- ++p;
- }
- request_uri[i] = '\0';
- while(*p == ' ')
- p++;
- for(i = 0; i < (int)sizeof(http_version) - 1; i++) {
- if(*p == ' ' || *p == '\r')
- break;
- http_version[i] = *p;
- ++p;
- }
- http_version[i] = '\0';
- printf("Method = %s, URI = %s, %s\n",
- request_method, request_uri, http_version);
- /* check if the request method is allowed */
- if(0 != strcmp(request_method, "GET")) {
- const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n"
- "Allow: GET\r\n\r\n";
- const char * pc;
- /* 405 Method Not Allowed */
- /* The response MUST include an Allow header containing a list
- * of valid methods for the requested resource. */
- n = sizeof(response405) - 1;
- pc = response405;
- while(n > 0) {
- i = write(c, pc, n);
- if(i<0) {
- if(errno != EINTR) {
- perror("write");
- return;
- }
- } else {
- n -= i;
- pc += i;
- }
- }
- return;
- }
-
- mode = MODE_INVALID;
- /* use the request URI to know what to do */
- for(i = 0; modes_array[i].mode != MODE_INVALID; i++) {
- if(strstr(request_uri, modes_array[i].text)) {
- mode = modes_array[i].mode; /* found */
- break;
- }
- }
-
- switch(mode) {
- case MODE_CHUNKED:
- response_buffer = build_chunked_response(content_length, &response_len);
- break;
- case MODE_ADDCRAP:
- response_len = content_length+256;
- response_buffer = malloc(response_len);
- if(!response_buffer)
- break;
- n = snprintf(response_buffer, response_len,
- "HTTP/1.1 200 OK\r\n"
- "Server: minihttptestserver\r\n"
- "Content-Type: text/plain\r\n"
- "Content-Length: %d\r\n"
- "\r\n", content_length);
- response_len = content_length+n+CRAP_LENGTH;
- p = realloc(response_buffer, response_len);
- if(p == NULL) {
- /* error 500 */
- free(response_buffer);
- response_buffer = NULL;
- break;
- }
- response_buffer = p;
- build_content(response_buffer + n, content_length);
- build_crap(response_buffer + n + content_length, CRAP_LENGTH);
- break;
- case MODE_FAVICON:
- content_length = FAVICON_LENGTH;
- response_len = content_length + 256;
- response_buffer = malloc(response_len);
- if(!response_buffer)
- break;
- n = snprintf(response_buffer, response_len,
- "HTTP/1.1 200 OK\r\n"
- "Server: minihttptestserver\r\n"
- "Content-Type: image/vnd.microsoft.icon\r\n"
- "Content-Length: %d\r\n"
- "\r\n", content_length);
- /* image/x-icon */
- build_favicon_content(response_buffer + n, content_length);
- response_len = content_length + n;
- break;
- default:
- response_len = content_length+256;
- response_buffer = malloc(response_len);
- if(!response_buffer)
- break;
- n = snprintf(response_buffer, response_len,
- "HTTP/1.1 200 OK\r\n"
- "Server: minihttptestserver\r\n"
- "Content-Type: text/plain\r\n"
- "\r\n");
- response_len = content_length+n;
- p = realloc(response_buffer, response_len);
- if(p == NULL) {
- /* Error 500 */
- free(response_buffer);
- response_buffer = NULL;
- break;
- }
- response_buffer = p;
- build_content(response_buffer + n, response_len - n);
- }
-
- if(response_buffer) {
- send_response(c, response_buffer, response_len);
- free(response_buffer);
- } else {
- /* Error 500 */
- }
-}
-
-/**
- */
-int main(int argc, char * * argv) {
- int ipv6 = 0;
- int s, c, i;
- unsigned short port = 0;
- struct sockaddr_storage server_addr;
- socklen_t server_addrlen;
- struct sockaddr_storage client_addr;
- socklen_t client_addrlen;
- pid_t pid;
- int child = 0;
- int status;
- const char * expected_file_name = NULL;
- struct sigaction sa;
-
- for(i = 1; i < argc; i++) {
- if(argv[i][0] == '-') {
- switch(argv[i][1]) {
- case '6':
- ipv6 = 1;
- break;
- case 'e':
- /* write expected file ! */
- expected_file_name = argv[++i];
- break;
- case 'p':
- /* port */
- if(++i < argc) {
- port = (unsigned short)atoi(argv[i]);
- }
- break;
- default:
- fprintf(stderr, "unknown command line switch '%s'\n", argv[i]);
- }
- } else {
- fprintf(stderr, "unkown command line argument '%s'\n", argv[i]);
- }
- }
-
- srand(time(NULL));
-
- memset(&sa, 0, sizeof(struct sigaction));
-
- /*signal(SIGCHLD, handle_signal_chld);*/
- sa.sa_handler = handle_signal_chld;
- if(sigaction(SIGCHLD, &sa, NULL) < 0) {
- perror("sigaction");
- return 1;
- }
- /*signal(SIGINT, handle_signal_int);*/
- sa.sa_handler = handle_signal_int;
- if(sigaction(SIGINT, &sa, NULL) < 0) {
- perror("sigaction");
- return 1;
- }
-
- s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
- if(s < 0) {
- perror("socket");
- return 1;
- }
- memset(&server_addr, 0, sizeof(struct sockaddr_storage));
- memset(&client_addr, 0, sizeof(struct sockaddr_storage));
- if(ipv6) {
- struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
- addr->sin6_family = AF_INET6;
- addr->sin6_port = htons(port);
- addr->sin6_addr = in6addr_loopback;
- } else {
- struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
- addr->sin_family = AF_INET;
- addr->sin_port = htons(port);
- addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- }
- if(bind(s, (struct sockaddr *)&server_addr,
- ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) {
- perror("bind");
- return 1;
- }
- if(listen(s, 5) < 0) {
- perror("listen");
- }
- if(port == 0) {
- server_addrlen = sizeof(struct sockaddr_storage);
- if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) {
- perror("getsockname");
- return 1;
- }
- if(ipv6) {
- struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr;
- port = ntohs(addr->sin6_port);
- } else {
- struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr;
- port = ntohs(addr->sin_port);
- }
- printf("Listening on port %hu\n", port);
- fflush(stdout);
- }
-
- /* write expected file */
- if(expected_file_name) {
- FILE * f;
- f = fopen(expected_file_name, "wb");
- if(f) {
- char * buffer;
- buffer = malloc(16*1024);
- if(buffer == NULL) {
- fprintf(stderr, "memory allocation error\n");
- } else {
- build_content(buffer, 16*1024);
- i = fwrite(buffer, 1, 16*1024, f);
- if(i != 16*1024) {
- fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024);
- }
- free(buffer);
- }
- fclose(f);
- } else {
- fprintf(stderr, "error opening file %s for writing\n", expected_file_name);
- }
- }
-
- /* fork() loop */
- while(!child && !quit) {
- while(child_to_wait_for > 0) {
- pid = wait(&status);
- if(pid < 0) {
- perror("wait");
- } else {
- printf("child(%d) terminated with status %d\n", pid, status);
- }
- --child_to_wait_for;
- }
- client_addrlen = sizeof(struct sockaddr_storage);
- c = accept(s, (struct sockaddr *)&client_addr,
- &client_addrlen);
- if(c < 0) {
- if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
- continue;
- perror("accept");
- return 1;
- }
- printf("accept...\n");
- pid = fork();
- if(pid < 0) {
- perror("fork");
- return 1;
- } else if(pid == 0) {
- /* child */
- child = 1;
- close(s);
- s = -1;
- handle_http_connection(c);
- }
- close(c);
- }
- if(s >= 0) {
- close(s);
- s = -1;
- }
- if(!child) {
- while(child_to_wait_for > 0) {
- pid = wait(&status);
- if(pid < 0) {
- perror("wait");
- } else {
- printf("child(%d) terminated with status %d\n", pid, status);
- }
- --child_to_wait_for;
- }
- printf("Bye...\n");
- }
- return 0;
-}
-
diff --git a/ext/miniupnpc/minisoap.c b/ext/miniupnpc/minisoap.c
index e2efd8f8..5b8c0784 100644
--- a/ext/miniupnpc/minisoap.c
+++ b/ext/miniupnpc/minisoap.c
@@ -1,5 +1,4 @@
-#define _CRT_SECURE_NO_WARNINGS
-/* $Id: minisoap.c,v 1.24 2015/10/26 17:05:07 nanard Exp $ */
+/* $Id: minisoap.c,v 1.25 2017/04/21 10:03:24 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2015 Thomas Bernard
@@ -20,17 +19,24 @@
#include <sys/socket.h>
#endif
#include "minisoap.h"
+
#ifdef _WIN32
#define OS_STRING "Win32"
#define MINIUPNPC_VERSION_STRING "2.0"
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
+#ifdef __ANDROID__
+#define OS_STRING "Android"
+#define MINIUPNPC_VERSION_STRING "2.0"
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
/* only for malloc */
#include <stdlib.h>
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
@@ -124,3 +130,5 @@ int soapPostSubmit(int fd,
#endif
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
}
+
+
diff --git a/ext/miniupnpc/minissdpc.c b/ext/miniupnpc/minissdpc.c
index 0f7271e2..3479de8e 100644
--- a/ext/miniupnpc/minissdpc.c
+++ b/ext/miniupnpc/minissdpc.c
@@ -1,11 +1,9 @@
-#define _CRT_SECURE_NO_WARNINGS
-
-/* $Id: minissdpc.c,v 1.31 2016/01/19 09:56:46 nanard Exp $ */
+/* $Id: minissdpc.c,v 1.35 2017/11/02 15:34:36 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Author : Thomas BERNARD
- * copyright (c) 2005-2015 Thomas Bernard
+ * copyright (c) 2005-2017 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
/*#include <syslog.h>*/
@@ -13,6 +11,9 @@
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
+#if defined (__NetBSD__)
+#include <net/if.h>
+#endif
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
#ifdef _WIN32
#include <winsock2.h>
@@ -61,7 +62,7 @@ struct sockaddr_un {
#endif
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
@@ -72,6 +73,9 @@ struct sockaddr_un {
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
#include <sys/ioctl.h>
+#if defined(__sun)
+#include <sys/sockio.h>
+#endif
#endif
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
@@ -168,7 +172,7 @@ connectToMiniSSDPD(const char * socketpath)
{
int s;
struct sockaddr_un addr;
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
struct timeval timeout;
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
@@ -179,23 +183,25 @@ connectToMiniSSDPD(const char * socketpath)
perror("socket(unix)");
return MINISSDPC_SOCKET_ERROR;
}
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
/* setting a 3 seconds timeout */
+ /* not supported for AF_UNIX sockets under Solaris */
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- perror("setsockopt");
+ perror("setsockopt SO_RCVTIMEO unix");
}
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- perror("setsockopt");
+ perror("setsockopt SO_SNDTIMEO unix");
}
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
if(!socketpath)
socketpath = "/var/run/minissdpd.sock";
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
/* TODO : check if we need to handle the EINTR */
@@ -497,6 +503,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
unsigned long _ttl = (unsigned long)ttl;
#endif
int linklocal = 1;
+ int sentok;
if(error)
*error = MINISSDPC_UNKNOWN_ERROR;
@@ -607,14 +614,27 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
return NULL;
}
+ if(ipv6) {
#ifdef _WIN32
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+ DWORD mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
#else /* _WIN32 */
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+ int mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
#endif /* _WIN32 */
- {
- /* not a fatal error */
- PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ {
+ PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
+ }
+ } else {
+#ifdef _WIN32
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+#else /* _WIN32 */
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+#endif /* _WIN32 */
+ {
+ /* not a fatal error */
+ PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ }
}
if(multicastif)
@@ -627,7 +647,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
}
#else
#ifdef DEBUG
@@ -642,7 +662,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
} else {
#ifdef HAS_IP_MREQN
@@ -652,7 +672,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
reqn.imr_ifindex = if_nametoindex(multicastif);
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#elif !defined(_WIN32)
struct ifreq ifr;
@@ -666,7 +686,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#else /* _WIN32 */
#ifdef DEBUG
@@ -699,6 +719,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
}
/* receiving SSDP response packet */
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ sentok = 0;
/* sending the SSDP M-SEARCH packet */
n = snprintf(bufr, sizeof(bufr),
MSearchMsgFmt,
@@ -742,7 +763,8 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(error)
*error = MINISSDPC_SOCKET_ERROR;
PRINT_SOCKET_ERROR("sendto");
- break;
+ } else {
+ sentok = 1;
}
#else /* #ifdef NO_GETADDRINFO */
memset(&hints, 0, sizeof(hints));
@@ -774,19 +796,20 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
#endif
PRINT_SOCKET_ERROR("sendto");
continue;
+ } else {
+ sentok = 1;
}
}
freeaddrinfo(servinfo);
- if(n < 0) {
+ if(!sentok) {
if(error)
*error = MINISSDPC_SOCKET_ERROR;
- break;
}
#endif /* #ifdef NO_GETADDRINFO */
/* Waiting for SSDP REPLY packet to M-SEARCH
* if searchalltypes is set, enter the loop only
* when the last deviceType is reached */
- if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do {
+ if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) do {
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
if (n < 0) {
/* error */
diff --git a/ext/miniupnpc/miniupnpc.c b/ext/miniupnpc/miniupnpc.c
index 68d562fa..2dc5c95c 100644
--- a/ext/miniupnpc/miniupnpc.c
+++ b/ext/miniupnpc/miniupnpc.c
@@ -1,5 +1,3 @@
-#define _CRT_SECURE_NO_WARNINGS
-
/* $Id: miniupnpc.c,v 1.149 2016/02/09 09:50:46 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
diff --git a/ext/miniupnpc/miniupnpc.h b/ext/miniupnpc/miniupnpc.h
index 0b5b4732..4a805b11 100644
--- a/ext/miniupnpc/miniupnpc.h
+++ b/ext/miniupnpc/miniupnpc.h
@@ -19,7 +19,7 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */
-#define MINIUPNPC_VERSION "2.0"
+#define MINIUPNPC_VERSION "2.0.20171212"
#define MINIUPNPC_API_VERSION 16
/* Source port:
diff --git a/ext/miniupnpc/miniupnpcmodule.c b/ext/miniupnpc/miniupnpcmodule.c
index a5bdce44..bbff0738 100644
--- a/ext/miniupnpc/miniupnpcmodule.c
+++ b/ext/miniupnpc/miniupnpcmodule.c
@@ -1,8 +1,8 @@
-/* $Id: miniupnpcmodule.c,v 1.29 2015/10/26 17:01:30 nanard Exp $*/
+/* $Id: miniupnpcmodule.c,v 1.31 2017/11/02 15:37:28 nanard Exp $*/
/* Project : miniupnp
* Author : Thomas BERNARD
* website : http://miniupnp.tuxfamily.org/
- * copyright (c) 2007-2014 Thomas Bernard
+ * copyright (c) 2007-2016 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <Python.h>
@@ -12,6 +12,10 @@
#include "upnpcommands.h"
#include "upnperrors.h"
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
/* for compatibility with Python < 2.4 */
#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
@@ -303,7 +307,7 @@ UPnP_addportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost;
const char * leaseDuration = "0";
int r;
- if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto,
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto,
&host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
@@ -345,7 +349,7 @@ UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost;
const char * leaseDuration = "0";
int r;
- if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
@@ -669,6 +673,10 @@ initminiupnpc(void)
PyObject* m;
#ifdef _WIN32
+ /* initialize Winsock. */
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+
UPnPType.tp_new = PyType_GenericNew;
#endif
if (PyType_Ready(&UPnPType) < 0)
diff --git a/ext/miniupnpc/miniwget.c b/ext/miniupnpc/miniwget.c
index 1af106d0..e23f11e3 100644
--- a/ext/miniupnpc/miniwget.c
+++ b/ext/miniupnpc/miniwget.c
@@ -1,10 +1,8 @@
-#define _CRT_SECURE_NO_WARNINGS
-
-/* $Id: miniwget.c,v 1.75 2016/01/24 17:24:36 nanard Exp $ */
+/* $Id: miniwget.c,v 1.77 2017/05/09 10:04:57 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -56,6 +54,12 @@
#define UPNP_VERSION_STRING "UPnP/1.1"
#endif
+#ifdef __ANDROID__
+#define OS_STRING "Android"
+#define MINIUPNPC_VERSION_STRING "2.0"
+#define UPNP_VERSION_STRING "UPnP/1.1"
+#endif
+
#include "miniwget.h"
#include "connecthostport.h"
#include "receivedata.h"
@@ -89,8 +93,10 @@ getHTTPResponse(int s, int * size, int * status_code)
unsigned int content_buf_used = 0;
char chunksize_buf[32];
unsigned int chunksize_buf_index;
+#ifdef DEBUG
char * reason_phrase = NULL;
int reason_phrase_len = 0;
+#endif
if(status_code) *status_code = -1;
header_buf = malloc(header_buf_len);
@@ -115,7 +121,7 @@ getHTTPResponse(int s, int * size, int * status_code)
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
- while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
+ while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
{
if(endofheaders == 0)
{
@@ -187,8 +193,10 @@ getHTTPResponse(int s, int * size, int * status_code)
*status_code = atoi(header_buf + sp + 1);
else
{
+#ifdef DEBUG
reason_phrase = header_buf + sp + 1;
reason_phrase_len = i - sp - 1;
+#endif
break;
}
}
@@ -286,11 +294,12 @@ getHTTPResponse(int s, int * size, int * status_code)
goto end_of_stream;
}
}
- bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
+ /* it is guaranteed that (n >= i) */
+ bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
if((content_buf_used + bytestocopy) > content_buf_len)
{
char * tmp;
- if(content_length >= (int)(content_buf_used + bytestocopy)) {
+ if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + bytestocopy;
@@ -315,14 +324,15 @@ getHTTPResponse(int s, int * size, int * status_code)
{
/* not chunked */
if(content_length > 0
- && (int)(content_buf_used + n) > content_length) {
+ && (content_buf_used + n) > (unsigned int)content_length) {
/* skipping additional bytes */
n = content_length - content_buf_used;
}
if(content_buf_used + n > content_buf_len)
{
char * tmp;
- if(content_length >= (int)(content_buf_used + n)) {
+ if(content_length >= 0
+ && (unsigned int)content_length >= (content_buf_used + n)) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + n;
@@ -342,7 +352,7 @@ getHTTPResponse(int s, int * size, int * status_code)
}
}
/* use the Content-Length header value if available */
- if(content_length > 0 && (int)content_buf_used >= content_length)
+ if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
{
#ifdef DEBUG
printf("End of HTTP content\n");
diff --git a/ext/miniupnpc/minixml.c b/ext/miniupnpc/minixml.c
index 5c79b3c9..935ec443 100644
--- a/ext/miniupnpc/minixml.c
+++ b/ext/miniupnpc/minixml.c
@@ -1,11 +1,11 @@
-#define _CRT_SECURE_NO_WARNINGS
-/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
-/* minixml.c : the minimum size a xml parser can be ! */
+/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
-Copyright (c) 2005-2014, Thomas BERNARD
+Copyright (c) 2005-2017, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -162,7 +162,8 @@ static void parseelt(struct xmlparser * p)
if (p->xml >= p->xmlend)
return;
}
- if(memcmp(p->xml, "<![CDATA[", 9) == 0)
+ /* CDATA are at least 9 + 3 characters long : <![CDATA[ ]]> */
+ if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "<![CDATA[", 9) == 0))
{
/* CDATA handling */
p->xml += 9;
diff --git a/ext/miniupnpc/minixmlvalid.c b/ext/miniupnpc/minixmlvalid.c
index a86beba8..dad14881 100644
--- a/ext/miniupnpc/minixmlvalid.c
+++ b/ext/miniupnpc/minixmlvalid.c
@@ -1,4 +1,3 @@
-#define _CRT_SECURE_NO_WARNINGS
/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */
/* MiniUPnP Project
* http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
diff --git a/ext/miniupnpc/portlistingparse.c b/ext/miniupnpc/portlistingparse.c
index 0e092780..d1954f59 100644
--- a/ext/miniupnpc/portlistingparse.c
+++ b/ext/miniupnpc/portlistingparse.c
@@ -1,7 +1,7 @@
-/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
+/* $Id: portlistingparse.c,v 1.10 2016/12/16 08:53:21 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2011-2015 Thomas Bernard
+ * (c) 2011-2016 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <string.h>
@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l)
pdata->curelt = PortMappingEltNone;
for(i = 0; elements[i].str; i++)
{
- if(memcmp(name, elements[i].str, l) == 0)
+ if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
{
pdata->curelt = elements[i].code;
break;
diff --git a/ext/miniupnpc/pymoduletest.py b/ext/miniupnpc/pymoduletest.py
deleted file mode 100644
index 9fddd9c2..00000000
--- a/ext/miniupnpc/pymoduletest.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#! /usr/bin/python
-# vim: tabstop=2 shiftwidth=2 expandtab
-# MiniUPnP project
-# Author : Thomas Bernard
-# This Sample code is public domain.
-# website : http://miniupnp.tuxfamily.org/
-
-# import the python miniupnpc module
-import miniupnpc
-import sys
-
-try:
- import argparse
- parser = argparse.ArgumentParser()
- parser.add_argument('-m', '--multicastif')
- parser.add_argument('-p', '--minissdpdsocket')
- parser.add_argument('-d', '--discoverdelay', type=int, default=200)
- parser.add_argument('-z', '--localport', type=int, default=0)
- # create the object
- u = miniupnpc.UPnP(**vars(parser.parse_args()))
-except:
- print 'argparse not available'
- i = 1
- multicastif = None
- minissdpdsocket = None
- discoverdelay = 200
- localport = 0
- while i < len(sys.argv):
- print sys.argv[i]
- if sys.argv[i] == '-m' or sys.argv[i] == '--multicastif':
- multicastif = sys.argv[i+1]
- elif sys.argv[i] == '-p' or sys.argv[i] == '--minissdpdsocket':
- minissdpdsocket = sys.argv[i+1]
- elif sys.argv[i] == '-d' or sys.argv[i] == '--discoverdelay':
- discoverdelay = int(sys.argv[i+1])
- elif sys.argv[i] == '-z' or sys.argv[i] == '--localport':
- localport = int(sys.argv[i+1])
- else:
- raise Exception('invalid argument %s' % sys.argv[i])
- i += 2
- # create the object
- u = miniupnpc.UPnP(multicastif, minissdpdsocket, discoverdelay, localport)
-
-print 'inital(default) values :'
-print ' discoverdelay', u.discoverdelay
-print ' lanaddr', u.lanaddr
-print ' multicastif', u.multicastif
-print ' minissdpdsocket', u.minissdpdsocket
-#u.minissdpdsocket = '../minissdpd/minissdpd.sock'
-# discovery process, it usualy takes several seconds (2 seconds or more)
-print 'Discovering... delay=%ums' % u.discoverdelay
-print u.discover(), 'device(s) detected'
-# select an igd
-try:
- u.selectigd()
-except Exception, e:
- print 'Exception :', e
- sys.exit(1)
-# display information about the IGD and the internet connection
-print 'local ip address :', u.lanaddr
-print 'external ip address :', u.externalipaddress()
-print u.statusinfo(), u.connectiontype()
-print 'total bytes : sent', u.totalbytesent(), 'received', u.totalbytereceived()
-print 'total packets : sent', u.totalpacketsent(), 'received', u.totalpacketreceived()
-
-#print u.addportmapping(64000, 'TCP',
-# '192.168.1.166', 63000, 'port mapping test', '')
-#print u.deleteportmapping(64000, 'TCP')
-
-port = 0
-proto = 'UDP'
-# list the redirections :
-i = 0
-while True:
- p = u.getgenericportmapping(i)
- if p==None:
- break
- print i, p
- (port, proto, (ihost,iport), desc, c, d, e) = p
- #print port, desc
- i = i + 1
-
-print u.getspecificportmapping(port, proto)
-try:
- print u.getportmappingnumberofentries()
-except Exception, e:
- print 'GetPortMappingNumberOfEntries() is not supported :', e
-
diff --git a/ext/miniupnpc/receivedata.c b/ext/miniupnpc/receivedata.c
index ef85a3db..5dbd227b 100644
--- a/ext/miniupnpc/receivedata.c
+++ b/ext/miniupnpc/receivedata.c
@@ -1,4 +1,4 @@
-/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */
+/* $Id: receivedata.c,v 1.8 2017/04/21 10:16:45 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
@@ -28,7 +28,7 @@
#endif /* _WIN32 */
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
diff --git a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values
deleted file mode 100644
index cf422218..00000000
--- a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.values
+++ /dev/null
@@ -1,14 +0,0 @@
-# values for linksys_WAG200G_desc.xml
-
-CIF:
- servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
- controlurl = /upnp/control/WANCommonIFC1
- eventsuburl = /upnp/event/WANCommonIFC1
- scpdurl = /cmnicfg.xml
-
-first:
- servicetype = urn:schemas-upnp-org:service:WANPPPConnection:1
- controlurl = /upnp/control/WANPPPConn1
- eventsuburl = /upnp/event/WANPPPConn1
- scpdurl = /pppcfg.xml
-
diff --git a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml b/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml
deleted file mode 100644
index d428d73b..00000000
--- a/ext/miniupnpc/testdesc/linksys_WAG200G_desc.xml
+++ /dev/null
@@ -1,110 +0,0 @@
-<?xml version="1.0"?>
-<root xmlns="urn:schemas-upnp-org:device-1-0">
-<specVersion>
-<major>1</major>
-<minor>0</minor>
-</specVersion>
-<URLBase>http://192.168.1.1:49152</URLBase>
-<device>
-<deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
-<friendlyName>LINKSYS WAG200G Gateway</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com</manufacturerURL>
-<modelDescription>LINKSYS WAG200G Gateway</modelDescription>
-<modelName>Wireless-G ADSL Home Gateway</modelName>
-<modelNumber>WAG200G</modelNumber>
-<modelURL>http://www.linksys.com</modelURL>
-<serialNumber>123456789</serialNumber>
-<UDN>uuid:8ca2eb37-1dd2-11b2-86f1-001a709b5aa8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
-<controlURL>/upnp/control/L3Forwarding1</controlURL>
-<eventSubURL>/upnp/event/L3Forwarding1</eventSubURL>
-<SCPDURL>/l3frwd.xml</SCPDURL>
-</service>
-</serviceList>
-<deviceList>
-<device>
-<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
-<friendlyName>WANDevice</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com/</manufacturerURL>
-<modelDescription>Residential Gateway</modelDescription>
-<modelName>Internet Connection Sharing</modelName>
-<modelNumber>1</modelNumber>
-<modelURL>http://www.linksys.com/</modelURL>
-<serialNumber>0000001</serialNumber>
-<UDN>uuid:8ca2eb36-1dd2-11b2-86f1-001a709b5aa8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
-<controlURL>/upnp/control/WANCommonIFC1</controlURL>
-<eventSubURL>/upnp/event/WANCommonIFC1</eventSubURL>
-<SCPDURL>/cmnicfg.xml</SCPDURL>
-</service>
-</serviceList>
-<deviceList>
-<device>
-<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
-<friendlyName>WANConnectionDevice</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com/</manufacturerURL>
-<modelDescription>Residential Gateway</modelDescription>
-<modelName>Internet Connection Sharing</modelName>
-<modelNumber>1</modelNumber>
-<modelURL>http://www.linksys.com/</modelURL>
-<serialNumber>0000001</serialNumber>
-<UDN>uuid:8ca2eb37-1dd2-11b2-86f0-001a709b5aa8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:WANEthernetLinkConfig:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:WANEthLinkC1</serviceId>
-<controlURL>/upnp/control/WANEthLinkC1</controlURL>
-<eventSubURL>/upnp/event/WANEthLinkC1</eventSubURL>
-<SCPDURL>/wanelcfg.xml</SCPDURL>
-</service>
-<service>
-<serviceType>urn:schemas-upnp-org:service:WANPPPConnection:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:WANPPPConn1</serviceId>
-<controlURL>/upnp/control/WANPPPConn1</controlURL>
-<eventSubURL>/upnp/event/WANPPPConn1</eventSubURL>
-<SCPDURL>/pppcfg.xml</SCPDURL>
-</service>
-</serviceList>
-</device>
-</deviceList>
-</device>
-<device>
-<deviceType>urn:schemas-upnp-org:device:LANDevice:1</deviceType>
-<friendlyName>LANDevice</friendlyName>
-<manufacturer>LINKSYS</manufacturer>
-<manufacturerURL>http://www.linksys.com/</manufacturerURL>
-<modelDescription>Residential Gateway</modelDescription>
-<modelName>Residential Gateway</modelName>
-<modelNumber>1</modelNumber>
-<modelURL>http://www.linksys.com/</modelURL>
-<serialNumber>0000001</serialNumber>
-<UDN>uuid:8ca2eb36-1dd2-11b2-86f0-001a709b5aa
-8</UDN>
-<UPC>WAG200G</UPC>
-<serviceList>
-<service>
-<serviceType>urn:schemas-upnp-org:service:LANHostConfigManagement:1</serviceType>
-<serviceId>urn:upnp-org:serviceId:LANHostCfg1</serviceId>
-<controlURL>/upnp/control/LANHostCfg1</controlURL>
-<eventSubURL>/upnp/event/LANHostCfg1</eventSubURL>
-<SCPDURL>/lanhostc.xml</SCPDURL>
-</service>
-</serviceList>
-</device>
-</deviceList>
-<presentationURL>http://192.168.1.1/index.htm</presentationURL>
-</device>
-</root>
-
diff --git a/ext/miniupnpc/testdesc/new_LiveBox_desc.values b/ext/miniupnpc/testdesc/new_LiveBox_desc.values
deleted file mode 100644
index c55552e5..00000000
--- a/ext/miniupnpc/testdesc/new_LiveBox_desc.values
+++ /dev/null
@@ -1,20 +0,0 @@
-# values for new_LiveBox_desc.xml
-
-CIF:
- servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
- controlurl = /87895a19/upnp/control/WANCommonIFC1
- eventsuburl = /87895a19/upnp/control/WANCommonIFC1
- scpdurl = /87895a19/gateicfgSCPD.xml
-
-first:
- servicetype = urn:schemas-upnp-org:service:WANPPPConnection:2
- controlurl = /87895a19/upnp/control/WANIPConn1
- eventsuburl = /87895a19/upnp/control/WANIPConn1
- scpdurl = /87895a19/gateconnSCPD_PPP.xml
-
-IPv6FC:
- servicetype = urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
- controlurl = /87895a19/upnp/control/WANIPv6FwCtrl1
- eventsuburl = /87895a19/upnp/control/WANIPv6FwCtrl1
- scpdurl = /87895a19/wanipv6fwctrlSCPD.xml
-
diff --git a/ext/miniupnpc/testdesc/new_LiveBox_desc.xml b/ext/miniupnpc/testdesc/new_LiveBox_desc.xml
deleted file mode 100644
index 620eb55a..00000000
--- a/ext/miniupnpc/testdesc/new_LiveBox_desc.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0"?>
-<root xmlns="urn:schemas-upnp-org:device-1-0">
- <specVersion>
- <major>1</major>
- <minor>0</minor>
- </specVersion>
- <device>
- <pnpx:X_hardwareId xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">VEN_0129&amp;DEV_0000&amp;SUBSYS_03&amp;REV_250417</pnpx:X_hardwareId>
- <pnpx:X_compatibleId xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">GenericUmPass</pnpx:X_compatibleId>
- <pnpx:X_deviceCategory xmlns:pnpx="http://schemas.microsoft.com/windows/pnpx/2005/11">NetworkInfrastructure.Gateway</pnpx:X_deviceCategory>
- <df:X_deviceCategory xmlns:df="http://schemas.microsoft.com/windows/2008/09/devicefoundation">Network.Gateway</df:X_deviceCategory>
- <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:2</deviceType>
- <friendlyName>Orange Livebox</friendlyName>
- <manufacturer>Sagemcom</manufacturer>
- <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
- <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
- <UDN>uuid:87895a19-50f9-3736-a87f-115c230155f8</UDN>
- <modelDescription>Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
- <modelNumber>3</modelNumber>
- <serialNumber>LK14129DP441489</serialNumber>
- <presentationURL>http://192.168.1.1</presentationURL>
- <UPC></UPC>
- <iconList>
- <icon>
- <mimetype>image/png</mimetype>
- <width>16</width>
- <height>16</height>
- <depth>8</depth>
- <url>/87895a19/ligd.png</url>
- </icon>
- </iconList>
- <deviceList>
- <device>
- <deviceType>urn:schemas-upnp-org:device:WANDevice:2</deviceType>
- <friendlyName>WANDevice</friendlyName>
- <manufacturer>Sagemcom</manufacturer>
- <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
- <modelDescription>WAN Device on Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
- <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
- <modelNumber>3</modelNumber>
- <modelURL>http://www.sagemcom.com/</modelURL>
- <serialNumber>LK14129DP441489</serialNumber>
- <presentationURL>http://192.168.1.1</presentationURL>
- <UDN>uuid:e2397374-53d8-3fc6-8306-593ba1a34625</UDN>
- <UPC></UPC>
- <serviceList>
- <service>
- <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
- <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
- <controlURL>/87895a19/upnp/control/WANCommonIFC1</controlURL>
- <eventSubURL>/87895a19/upnp/control/WANCommonIFC1</eventSubURL>
- <SCPDURL>/87895a19/gateicfgSCPD.xml</SCPDURL>
- </service>
- </serviceList>
- <deviceList>
- <device>
- <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:2</deviceType>
- <friendlyName>WANConnectionDevice</friendlyName>
- <manufacturer>Sagemcom</manufacturer>
- <manufacturerURL>http://www.sagemcom.com/</manufacturerURL>
- <modelDescription>WanConnectionDevice on Sagemcom,fr,SG30_sip-fr-4.28.35.1</modelDescription>
- <modelName>Residential Livebox,(DSL,WAN Ethernet)</modelName>
- <modelNumber>3</modelNumber>
- <modelURL>http://www.sagemcom.com/</modelURL>
- <serialNumber>LK14129DP441489</serialNumber>
- <presentationURL>http://192.168.1.1</presentationURL>
- <UDN>uuid:44598a08-288e-32c9-8a4d-d3c008ede331</UDN>
- <UPC></UPC>
- <serviceList>
- <service>
- <serviceType>urn:schemas-upnp-org:service:WANPPPConnection:2</serviceType>
- <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
- <controlURL>/87895a19/upnp/control/WANIPConn1</controlURL>
- <eventSubURL>/87895a19/upnp/control/WANIPConn1</eventSubURL>
- <SCPDURL>/87895a19/gateconnSCPD_PPP.xml</SCPDURL>
- </service>
- <service>
- <serviceType>urn:schemas-upnp-org:service:WANIPv6FirewallControl:1</serviceType>
- <serviceId>urn:upnp-org:serviceId:WANIPv6FwCtrl1</serviceId>
- <controlURL>/87895a19/upnp/control/WANIPv6FwCtrl1</controlURL>
- <eventSubURL>/87895a19/upnp/control/WANIPv6FwCtrl1</eventSubURL>
- <SCPDURL>/87895a19/wanipv6fwctrlSCPD.xml</SCPDURL>
- </service>
- </serviceList>
- </device>
- </deviceList>
- </device>
- </deviceList>
- </device>
-</root> \ No newline at end of file
diff --git a/ext/miniupnpc/testigddescparse.c b/ext/miniupnpc/testigddescparse.c
deleted file mode 100644
index c1907fd0..00000000
--- a/ext/miniupnpc/testigddescparse.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/* $Id: testigddescparse.c,v 1.10 2015/08/06 09:55:24 nanard Exp $ */
-/* Project : miniupnp
- * http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2008-2015 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "igd_desc_parse.h"
-#include "minixml.h"
-#include "miniupnpc.h"
-
-/* count number of differences */
-int compare_service(struct IGDdatas_service * s, FILE * f)
-{
- int n = 0;
- char line[1024];
-
- while(fgets(line, sizeof(line), f)) {
- char * value;
- char * equal;
- char * name;
- char * parsedvalue;
- int l;
- l = strlen(line);
- while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n') || (line[l-1] == ' ')))
- line[--l] = '\0';
- if(l == 0)
- break; /* end on blank line */
- if(line[0] == '#')
- continue; /* skip comments */
- equal = strchr(line, '=');
- if(equal == NULL) {
- fprintf(stderr, "Warning, line does not contain '=' : %s\n", line);
- continue;
- }
- *equal = '\0';
- name = line;
- while(*name == ' ' || *name == '\t')
- name++;
- l = strlen(name);
- while((l > 0) && (name[l-1] == ' ' || name[l-1] == '\t'))
- name[--l] = '\0';
- value = equal + 1;
- while(*value == ' ' || *value == '\t')
- value++;
- if(strcmp(name, "controlurl") == 0)
- parsedvalue = s->controlurl;
- else if(strcmp(name, "eventsuburl") == 0)
- parsedvalue = s->eventsuburl;
- else if(strcmp(name, "scpdurl") == 0)
- parsedvalue = s->scpdurl;
- else if(strcmp(name, "servicetype") == 0)
- parsedvalue = s->servicetype;
- else {
- fprintf(stderr, "unknown field '%s'\n", name);
- continue;
- }
- if(0 != strcmp(parsedvalue, value)) {
- fprintf(stderr, "difference : '%s' != '%s'\n", parsedvalue, value);
- n++;
- }
- }
- return n;
-}
-
-int compare_igd(struct IGDdatas * p, FILE * f)
-{
- int n = 0;
- char line[1024];
- struct IGDdatas_service * s;
-
- while(fgets(line, sizeof(line), f)) {
- char * colon;
- int l = (int)strlen(line);
- while((l > 0) && (line[l-1] == '\r' || (line[l-1] == '\n')))
- line[--l] = '\0';
- if(l == 0 || line[0] == '#')
- continue; /* skip blank lines and comments */
- colon = strchr(line, ':');
- if(colon == NULL) {
- fprintf(stderr, "Warning, no ':' : %s\n", line);
- continue;
- }
- s = NULL;
- *colon = '\0';
- if(strcmp(line, "CIF") == 0)
- s = &p->CIF;
- else if(strcmp(line, "first") == 0)
- s = &p->first;
- else if(strcmp(line, "second") == 0)
- s = &p->second;
- else if(strcmp(line, "IPv6FC") == 0)
- s = &p->IPv6FC;
- else {
- s = NULL;
- fprintf(stderr, "*** unknown service '%s' ***\n", line);
- n++;
- continue;
- }
- n += compare_service(s, f);
- }
- if(n > 0)
- fprintf(stderr, "*** %d difference%s ***\n", n, (n > 1) ? "s" : "");
- return n;
-}
-
-int test_igd_desc_parse(char * buffer, int len, FILE * f)
-{
- int n;
- struct IGDdatas igd;
- struct xmlparser parser;
- struct UPNPUrls urls;
-
- memset(&igd, 0, sizeof(struct IGDdatas));
- memset(&parser, 0, sizeof(struct xmlparser));
- parser.xmlstart = buffer;
- parser.xmlsize = len;
- parser.data = &igd;
- parser.starteltfunc = IGDstartelt;
- parser.endeltfunc = IGDendelt;
- parser.datafunc = IGDdata;
- parsexml(&parser);
-#ifdef DEBUG
- printIGD(&igd);
-#endif /* DEBUG */
- GetUPNPUrls(&urls, &igd, "http://fake/desc/url/file.xml", 0);
- printf("ipcondescURL='%s'\n", urls.ipcondescURL);
- printf("controlURL='%s'\n", urls.controlURL);
- printf("controlURL_CIF='%s'\n", urls.controlURL_CIF);
- n = f ? compare_igd(&igd, f) : 0;
- FreeUPNPUrls(&urls);
- return n;
-}
-
-int main(int argc, char * * argv)
-{
- FILE * f;
- char * buffer;
- int len;
- int r;
- if(argc<2) {
- fprintf(stderr, "Usage: %s file.xml [file.values]\n", argv[0]);
- return 1;
- }
- f = fopen(argv[1], "r");
- if(!f) {
- fprintf(stderr, "Cannot open %s for reading.\n", argv[1]);
- return 1;
- }
- fseek(f, 0, SEEK_END);
- len = ftell(f);
- fseek(f, 0, SEEK_SET);
- buffer = malloc(len);
- if(!buffer) {
- fprintf(stderr, "Memory allocation error.\n");
- fclose(f);
- return 1;
- }
- r = (int)fread(buffer, 1, len, f);
- if(r != len) {
- fprintf(stderr, "Failed to read file %s. %d out of %d bytes.\n",
- argv[1], r, len);
- fclose(f);
- free(buffer);
- return 1;
- }
- fclose(f);
- f = NULL;
- if(argc > 2) {
- f = fopen(argv[2], "r");
- if(!f) {
- fprintf(stderr, "Cannot open %s for reading.\n", argv[2]);
- free(buffer);
- return 1;
- }
- }
- r = test_igd_desc_parse(buffer, len, f);
- free(buffer);
- if(f)
- fclose(f);
- return r;
-}
-
diff --git a/ext/miniupnpc/testminiwget.c b/ext/miniupnpc/testminiwget.c
deleted file mode 100644
index 5eb49ec1..00000000
--- a/ext/miniupnpc/testminiwget.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/* $Id: testminiwget.c,v 1.5 2016/01/24 17:24:36 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdio.h>
-#include <stdlib.h>
-#include "miniwget.h"
-
-/**
- * This program uses the miniwget / miniwget_getaddr function
- * from miniwget.c in order to retreive a web ressource using
- * a GET HTTP method, and store it in a file.
- */
-int main(int argc, char * * argv)
-{
- void * data;
- int size, writtensize;
- FILE *f;
- char addr[64];
- int status_code = -1;
-
- if(argc < 3) {
- fprintf(stderr, "Usage:\t%s url file\n", argv[0]);
- fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]);
- return 1;
- }
- data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0, &status_code);
- if(!data || (status_code != 200)) {
- if(data) free(data);
- fprintf(stderr, "Error %d fetching %s\n", status_code, argv[1]);
- return 1;
- }
- printf("local address : %s\n", addr);
- printf("got %d bytes\n", size);
- f = fopen(argv[2], "wb");
- if(!f) {
- fprintf(stderr, "Cannot open file %s for writing\n", argv[2]);
- free(data);
- return 1;
- }
- writtensize = fwrite(data, 1, size, f);
- if(writtensize != size) {
- fprintf(stderr, "Could only write %d bytes out of %d to %s\n",
- writtensize, size, argv[2]);
- } else {
- printf("%d bytes written to %s\n", writtensize, argv[2]);
- }
- fclose(f);
- free(data);
- return 0;
-}
-
diff --git a/ext/miniupnpc/testminiwget.sh b/ext/miniupnpc/testminiwget.sh
deleted file mode 100755
index 690b4056..00000000
--- a/ext/miniupnpc/testminiwget.sh
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/bin/sh
-# $Id: testminiwget.sh,v 1.13 2015/09/03 17:57:44 nanard Exp $
-# project miniupnp : http://miniupnp.free.fr/
-# (c) 2011-2015 Thomas Bernard
-#
-# test program for miniwget.c
-# is usually invoked by "make check"
-#
-# This test program :
-# 1 - launches a local HTTP server (minihttptestserver)
-# 2 - uses testminiwget to retreive data from this server
-# 3 - compares served and received data
-# 4 - kills the local HTTP server and exits
-#
-# The script was tested and works with ksh, bash
-# it should now also run with dash
-
-TMPD=`mktemp -d -t miniwgetXXXXXXXXXX`
-HTTPSERVEROUT="${TMPD}/httpserverout"
-EXPECTEDFILE="${TMPD}/expectedfile"
-DOWNLOADEDFILE="${TMPD}/downloadedfile"
-PORT=
-RET=0
-
-case "$HAVE_IPV6" in
- n|no|0)
- ADDR=localhost
- SERVERARGS=""
- ;;
- *)
- ADDR="[::1]"
- SERVERARGS="-6"
- ;;
-
-esac
-
-#make minihttptestserver
-#make testminiwget
-
-# launching the test HTTP server
-./minihttptestserver $SERVERARGS -e $EXPECTEDFILE > $HTTPSERVEROUT &
-SERVERPID=$!
-while [ -z "$PORT" ]; do
- sleep 1
- PORT=`cat $HTTPSERVEROUT | sed 's/Listening on port \([0-9]*\)/\1/' `
-done
-echo "Test HTTP server is listening on $PORT"
-
-URL1="http://$ADDR:$PORT/index.html"
-URL2="http://$ADDR:$PORT/chunked"
-URL3="http://$ADDR:$PORT/addcrap"
-
-echo "standard test ..."
-./testminiwget $URL1 "${DOWNLOADEDFILE}.1"
-if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.1" ; then
- echo "ok"
-else
- echo "standard test FAILED"
- RET=1
-fi
-
-echo "chunked transfert encoding test ..."
-./testminiwget $URL2 "${DOWNLOADEDFILE}.2"
-if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.2" ; then
- echo "ok"
-else
- echo "chunked transfert encoding test FAILED"
- RET=1
-fi
-
-echo "response too long test ..."
-./testminiwget $URL3 "${DOWNLOADEDFILE}.3"
-if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.3" ; then
- echo "ok"
-else
- echo "response too long test FAILED"
- RET=1
-fi
-
-# kill the test HTTP server
-kill $SERVERPID
-wait $SERVERPID
-
-# remove temporary files (for success cases)
-if [ $RET -eq 0 ]; then
- rm -f "${DOWNLOADEDFILE}.1"
- rm -f "${DOWNLOADEDFILE}.2"
- rm -f "${DOWNLOADEDFILE}.3"
- rm -f $EXPECTEDFILE $HTTPSERVEROUT
- rmdir ${TMPD}
-else
- echo "at least one of the test FAILED"
- echo "directory ${TMPD} is left intact"
-fi
-exit $RET
-
diff --git a/ext/miniupnpc/testminixml.c b/ext/miniupnpc/testminixml.c
deleted file mode 100644
index 57c4a85e..00000000
--- a/ext/miniupnpc/testminixml.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/* $Id: testminixml.c,v 1.10 2014/11/17 17:19:13 nanard Exp $
- * MiniUPnP project
- * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author : Thomas Bernard.
- * Copyright (c) 2005-2014 Thomas Bernard
- *
- * testminixml.c
- * test program for the "minixml" functions.
- */
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "minixml.h"
-#include "igd_desc_parse.h"
-
-/* ---------------------------------------------------------------------- */
-void printeltname1(void * d, const char * name, int l)
-{
- int i;
- (void)d;
- printf("element ");
- for(i=0;i<l;i++)
- putchar(name[i]);
-}
-void printeltname2(void * d, const char * name, int l)
-{
- int i;
- (void)d;
- putchar('/');
- for(i=0;i<l;i++)
- putchar(name[i]);
- putchar('\n');
-}
-void printdata(void *d, const char * data, int l)
-{
- int i;
- (void)d;
- printf("data : ");
- for(i=0;i<l;i++)
- putchar(data[i]);
- putchar('\n');
-}
-
-void burptest(const char * buffer, int bufsize)
-{
- struct IGDdatas data;
- struct xmlparser parser;
- /*objet IGDdatas */
- memset(&data, 0, sizeof(struct IGDdatas));
- /* objet xmlparser */
- parser.xmlstart = buffer;
- parser.xmlsize = bufsize;
- parser.data = &data;
- /*parser.starteltfunc = printeltname1;
- parser.endeltfunc = printeltname2;
- parser.datafunc = printdata; */
- parser.starteltfunc = IGDstartelt;
- parser.endeltfunc = IGDendelt;
- parser.datafunc = IGDdata;
- parsexml(&parser);
-#ifdef DEBUG
- printIGD(&data);
-#endif /* DEBUG */
-}
-
-/* ----- main ---- */
-#define XML_MAX_SIZE (8192)
-int main(int argc, char * * argv)
-{
- FILE * f;
- char buffer[XML_MAX_SIZE];
- int bufsize;
- if(argc<2)
- {
- printf("usage:\t%s file.xml\n", argv[0]);
- return 1;
- }
- f = fopen(argv[1], "r");
- if(!f)
- {
- printf("cannot open file %s\n", argv[1]);
- return 1;
- }
- bufsize = (int)fread(buffer, 1, XML_MAX_SIZE, f);
- fclose(f);
- burptest(buffer, bufsize);
- return 0;
-}
-
diff --git a/ext/miniupnpc/testportlistingparse.c b/ext/miniupnpc/testportlistingparse.c
deleted file mode 100644
index bd9247dc..00000000
--- a/ext/miniupnpc/testportlistingparse.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/* $Id: testportlistingparse.c,v 1.2 2014/11/01 10:37:32 nanard Exp $ */
-/* Project : miniupnp
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author : Thomas Bernard
- * Copyright (c) 2014 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-
-#include <string.h>
-#include <stdio.h>
-#include "portlistingparse.h"
-
-struct port_mapping {
- unsigned int leasetime;
- unsigned short externalport;
- unsigned short internalport;
- const char * remotehost;
- const char * client;
- const char * proto;
- const char * desc;
- unsigned char enabled;
-};
-
-/* return the number of differences */
-int test(const char * portListingXml, int portListingXmlLen,
- const struct port_mapping * ref, int count)
-{
- int i;
- int r = 0;
- struct PortMappingParserData data;
- struct PortMapping * pm;
-
- memset(&data, 0, sizeof(data));
- ParsePortListing(portListingXml, portListingXmlLen, &data);
- for(i = 0, pm = data.l_head;
- (pm != NULL) && (i < count);
- i++, pm = pm->l_next) {
- printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
- i, pm->protocol, pm->externalPort, pm->internalClient,
- pm->internalPort,
- pm->description, pm->remoteHost,
- (unsigned)pm->leaseTime);
- if(0 != strcmp(pm->protocol, ref[i].proto)) {
- printf("protocol : '%s' != '%s'\n", pm->protocol, ref[i].proto);
- r++;
- }
- if(pm->externalPort != ref[i].externalport) {
- printf("externalPort : %hu != %hu\n",
- pm->externalPort, ref[i].externalport);
- r++;
- }
- if(0 != strcmp(pm->internalClient, ref[i].client)) {
- printf("client : '%s' != '%s'\n",
- pm->internalClient, ref[i].client);
- r++;
- }
- if(pm->internalPort != ref[i].internalport) {
- printf("internalPort : %hu != %hu\n",
- pm->internalPort, ref[i].internalport);
- r++;
- }
- if(0 != strcmp(pm->description, ref[i].desc)) {
- printf("description : '%s' != '%s'\n",
- pm->description, ref[i].desc);
- r++;
- }
- if(0 != strcmp(pm->remoteHost, ref[i].remotehost)) {
- printf("remoteHost : '%s' != '%s'\n",
- pm->remoteHost, ref[i].remotehost);
- r++;
- }
- if((unsigned)pm->leaseTime != ref[i].leasetime) {
- printf("leaseTime : %u != %u\n",
- (unsigned)pm->leaseTime, ref[i].leasetime);
- r++;
- }
- if(pm->enabled != ref[i].enabled) {
- printf("enabled : %d != %d\n",
- (int)pm->enabled, (int)ref[i].enabled);
- r++;
- }
- }
- if((i != count) || (pm != NULL)) {
- printf("count mismatch : i=%d count=%d pm=%p\n", i, count, pm);
- r++;
- }
- FreePortListing(&data);
- return r;
-}
-
-const char test_document[] =
-"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-"<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\"\n"
-"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n"
-"xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection "
-"http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">\n"
-" <p:PortMappingEntry>\n"
-" <p:NewRemoteHost></p:NewRemoteHost>\n"
-" <p:NewExternalPort>5002</p:NewExternalPort>\n"
-" <p:NewProtocol>UDP</p:NewProtocol>\n"
-" <p:NewInternalPort>4001</p:NewInternalPort>\n"
-" <p:NewInternalClient>192.168.1.123</p:NewInternalClient>\n"
-" <p:NewEnabled>1</p:NewEnabled>\n"
-" <p:NewDescription>xxx</p:NewDescription>\n"
-" <p:NewLeaseTime>0</p:NewLeaseTime>\n"
-" </p:PortMappingEntry>\n"
-" <p:PortMappingEntry>\n"
-" <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>\n"
-" <p:NewExternalPort>2345</p:NewExternalPort>\n"
-" <p:NewProtocol>TCP</p:NewProtocol>\n"
-" <p:NewInternalPort>2349</p:NewInternalPort>\n"
-" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
-" <p:NewEnabled>1</p:NewEnabled>\n"
-" <p:NewDescription>dooom</p:NewDescription>\n"
-" <p:NewLeaseTime>346</p:NewLeaseTime>\n"
-" </p:PortMappingEntry>\n"
-" <p:PortMappingEntry>\n"
-" <p:NewRemoteHost>134.231.2.11</p:NewRemoteHost>\n"
-" <p:NewExternalPort>12345</p:NewExternalPort>\n"
-" <p:NewProtocol>TCP</p:NewProtocol>\n"
-" <p:NewInternalPort>12345</p:NewInternalPort>\n"
-" <p:NewInternalClient>192.168.1.137</p:NewInternalClient>\n"
-" <p:NewEnabled>1</p:NewEnabled>\n"
-" <p:NewDescription>dooom A</p:NewDescription>\n"
-" <p:NewLeaseTime>347</p:NewLeaseTime>\n"
-" </p:PortMappingEntry>\n"
-"</p:PortMappingList>";
-
-#define PORT_MAPPINGS_COUNT 3
-const struct port_mapping port_mappings[PORT_MAPPINGS_COUNT] = {
-{347, 12345, 12345, "134.231.2.11", "192.168.1.137", "TCP", "dooom A", 1},
-{346, 2345, 2349, "202.233.2.1", "192.168.1.137", "TCP", "dooom", 1},
-{0, 5002, 4001, "", "192.168.1.123", "UDP", "xxx", 1}
-};
-
-/* --- main --- */
-int main(void)
-{
- int r;
- r = test(test_document, sizeof(test_document) - 1,
- port_mappings, PORT_MAPPINGS_COUNT);
- if(r == 0) {
- printf("test of portlistingparse OK\n");
- return 0;
- } else {
- printf("test FAILED (%d differences counted)\n", r);
- return 1;
- }
-}
-
diff --git a/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue b/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue
deleted file mode 100644
index 48ca0ccc..00000000
--- a/ext/miniupnpc/testreplyparse/DeletePortMapping.namevalue
+++ /dev/null
@@ -1,3 +0,0 @@
-NewRemoteHost=
-NewExternalPort=123
-NewProtocol=TCP
diff --git a/ext/miniupnpc/testreplyparse/DeletePortMapping.xml b/ext/miniupnpc/testreplyparse/DeletePortMapping.xml
deleted file mode 100644
index a955c53f..00000000
--- a/ext/miniupnpc/testreplyparse/DeletePortMapping.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>123</NewExternalPort>
-<NewProtocol>TCP</NewProtocol></u:DeletePortMapping></s:Body>
-
-</s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue
deleted file mode 100644
index 5aa75f88..00000000
--- a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue
+++ /dev/null
@@ -1,2 +0,0 @@
-NewExternalIPAddress=1.2.3.4
-
diff --git a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml b/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml
deleted file mode 100644
index db7ec1f9..00000000
--- a/ext/miniupnpc/testreplyparse/GetExternalIPAddress.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetExternalIPAddressResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewExternalIPAddress>1.2.3.4</NewExternalIPAddress></u:GetExternalIPAddressResponse></s:Body></s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue
deleted file mode 100644
index 26b169c3..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue
+++ /dev/null
@@ -1,3 +0,0 @@
-NewProtocol=UDP
-NewExternalPort=12345
-NewRemoteHost=
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml
deleted file mode 100644
index bbb540ea..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0"?>
-<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetSpecificPortMappingEntry xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>12345</NewExternalPort><NewProtocol>UDP</NewProtocol></u:GetSpecificPortMappingEntry></s:Body></s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue
deleted file mode 100644
index 2189789b..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue
+++ /dev/null
@@ -1,5 +0,0 @@
-NewInternalPort=12345
-NewInternalClient=192.168.10.110
-NewEnabled=1
-NewPortMappingDescription=libminiupnpc
-NewLeaseDuration=0
diff --git a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml b/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml
deleted file mode 100644
index 77e8d9c7..00000000
--- a/ext/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetSpecificPortMappingEntryResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewInternalPort>12345</NewInternalPort><NewInternalClient>192.168.10.110</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>libminiupnpc</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:GetSpecificPortMappingEntryResponse></s:Body></s:Envelope>
-
diff --git a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue
deleted file mode 100644
index f78c7e2a..00000000
--- a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue
+++ /dev/null
@@ -1 +0,0 @@
-NewDefaultConnectionService=uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID
diff --git a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml b/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml
deleted file mode 100644
index ac04c07a..00000000
--- a/ext/miniupnpc/testreplyparse/SetDefaultConnectionService.xml
+++ /dev/null
@@ -1 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:SetDefaultConnectionService xmlns:u="urn:schemas-upnp-org:service:Layer3Forwarding:1"><NewDefaultConnectionService>uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID</NewDefaultConnectionService></u:SetDefaultConnectionService></s:Body></s:Envelope>
diff --git a/ext/miniupnpc/testreplyparse/readme.txt b/ext/miniupnpc/testreplyparse/readme.txt
deleted file mode 100644
index 3eb1f015..00000000
--- a/ext/miniupnpc/testreplyparse/readme.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This directory contains files used for validation of upnpreplyparse.c code.
-
-Each .xml file to parse should give the results which are in the .namevalue
-file.
-
-A .namevalue file contain name=value lines.
-
diff --git a/ext/miniupnpc/testupnpigd.py b/ext/miniupnpc/testupnpigd.py
deleted file mode 100755
index 6d167a4c..00000000
--- a/ext/miniupnpc/testupnpigd.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#! /usr/bin/python
-# $Id: testupnpigd.py,v 1.4 2008/10/11 10:27:20 nanard Exp $
-# MiniUPnP project
-# Author : Thomas Bernard
-# This Sample code is public domain.
-# website : http://miniupnp.tuxfamily.org/
-
-# import the python miniupnpc module
-import miniupnpc
-import socket
-import BaseHTTPServer
-
-# function definition
-def list_redirections():
- i = 0
- while True:
- p = u.getgenericportmapping(i)
- if p==None:
- break
- print i, p
- i = i + 1
-
-#define the handler class for HTTP connections
-class handler_class(BaseHTTPServer.BaseHTTPRequestHandler):
- def do_GET(self):
- self.send_response(200)
- self.end_headers()
- self.wfile.write("OK MON GARS")
-
-# create the object
-u = miniupnpc.UPnP()
-#print 'inital(default) values :'
-#print ' discoverdelay', u.discoverdelay
-#print ' lanaddr', u.lanaddr
-#print ' multicastif', u.multicastif
-#print ' minissdpdsocket', u.minissdpdsocket
-u.discoverdelay = 200;
-
-try:
- print 'Discovering... delay=%ums' % u.discoverdelay
- ndevices = u.discover()
- print ndevices, 'device(s) detected'
-
- # select an igd
- u.selectigd()
- # display information about the IGD and the internet connection
- print 'local ip address :', u.lanaddr
- externalipaddress = u.externalipaddress()
- print 'external ip address :', externalipaddress
- print u.statusinfo(), u.connectiontype()
-
- #instanciate a HTTPd object. The port is assigned by the system.
- httpd = BaseHTTPServer.HTTPServer((u.lanaddr, 0), handler_class)
- eport = httpd.server_port
-
- # find a free port for the redirection
- r = u.getspecificportmapping(eport, 'TCP')
- while r != None and eport < 65536:
- eport = eport + 1
- r = u.getspecificportmapping(eport, 'TCP')
-
- print 'trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port)
-
- b = u.addportmapping(eport, 'TCP', u.lanaddr, httpd.server_port,
- 'UPnP IGD Tester port %u' % eport, '')
- if b:
- print 'Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport)
- try:
- httpd.handle_request()
- httpd.server_close()
- except KeyboardInterrupt, details:
- print "CTRL-C exception!", details
- b = u.deleteportmapping(eport, 'TCP')
- if b:
- print 'Successfully deleted port mapping'
- else:
- print 'Failed to remove port mapping'
- else:
- print 'Failed'
-
- httpd.server_close()
-
-except Exception, e:
- print 'Exception :', e
diff --git a/ext/miniupnpc/testupnpreplyparse.c b/ext/miniupnpc/testupnpreplyparse.c
deleted file mode 100644
index 7ba7131e..00000000
--- a/ext/miniupnpc/testupnpreplyparse.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/* $Id: testupnpreplyparse.c,v 1.4 2014/01/27 11:45:19 nanard Exp $ */
-/* MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2006-2014 Thomas Bernard
- * This software is subject to the conditions detailed
- * in the LICENCE file provided within the distribution */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include "upnpreplyparse.h"
-
-int
-test_parsing(const char * buf, int len, FILE * f)
-{
- char line[1024];
- struct NameValueParserData pdata;
- int ok = 1;
- ParseNameValue(buf, len, &pdata);
- /* check result */
- if(f != NULL)
- {
- while(fgets(line, sizeof(line), f))
- {
- char * value;
- char * equal;
- char * parsedvalue;
- int l;
- l = strlen(line);
- while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n')))
- line[--l] = '\0';
- /* skip empty lines */
- if(l == 0)
- continue;
- equal = strchr(line, '=');
- if(equal == NULL)
- {
- fprintf(stderr, "Warning, line does not contain '=' : %s\n", line);
- continue;
- }
- *equal = '\0';
- value = equal + 1;
- parsedvalue = GetValueFromNameValueList(&pdata, line);
- if((parsedvalue == NULL) || (strcmp(parsedvalue, value) != 0))
- {
- fprintf(stderr, "Element <%s> : expecting value '%s', got '%s'\n",
- line, value, parsedvalue ? parsedvalue : "<null string>");
- ok = 0;
- }
- }
- }
- ClearNameValueList(&pdata);
- return ok;
-}
-
-int main(int argc, char * * argv)
-{
- FILE * f;
- char buffer[4096];
- int l;
- int ok;
-
- if(argc<2)
- {
- fprintf(stderr, "Usage: %s file.xml [file.namevalues]\n", argv[0]);
- return 1;
- }
- f = fopen(argv[1], "r");
- if(!f)
- {
- fprintf(stderr, "Error : can not open file %s\n", argv[1]);
- return 2;
- }
- l = fread(buffer, 1, sizeof(buffer)-1, f);
- fclose(f);
- f = NULL;
- buffer[l] = '\0';
- if(argc > 2)
- {
- f = fopen(argv[2], "r");
- if(!f)
- {
- fprintf(stderr, "Error : can not open file %s\n", argv[2]);
- return 2;
- }
- }
-#ifdef DEBUG
- DisplayNameValueList(buffer, l);
-#endif
- ok = test_parsing(buffer, l, f);
- if(f)
- {
- fclose(f);
- }
- return ok ? 0 : 3;
-}
-
diff --git a/ext/miniupnpc/testupnpreplyparse.sh b/ext/miniupnpc/testupnpreplyparse.sh
deleted file mode 100755
index 992930b7..00000000
--- a/ext/miniupnpc/testupnpreplyparse.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-for f in testreplyparse/*.xml ; do
- bf="`dirname $f`/`basename $f .xml`"
- if ./testupnpreplyparse $f $bf.namevalue ; then
- echo "$f : passed"
- else
- echo "$f : FAILED"
- exit 1
- fi
-done
-
-exit 0
-
diff --git a/ext/miniupnpc/upnpc.c b/ext/miniupnpc/upnpc.c
index 94f131c8..e719ecec 100644
--- a/ext/miniupnpc/upnpc.c
+++ b/ext/miniupnpc/upnpc.c
@@ -1,7 +1,7 @@
-/* $Id: upnpc.c,v 1.114 2016/01/22 15:04:23 nanard Exp $ */
+/* $Id: upnpc.c,v 1.117 2017/05/26 15:26:55 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -242,7 +242,7 @@ static void NewListRedirections(struct UPNPUrls * urls,
* 2 - get extenal ip address
* 3 - Add port mapping
* 4 - get this port mapping from the IGD */
-static void SetRedirectAndTest(struct UPNPUrls * urls,
+static int SetRedirectAndTest(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * iaddr,
const char * iport,
@@ -262,13 +262,13 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
if(!iaddr || !iport || !eport || !proto)
{
fprintf(stderr, "Wrong arguments\n");
- return;
+ return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "invalid protocol\n");
- return;
+ return -1;
}
r = UPNP_GetExternalIPAddress(urls->controlURL,
@@ -292,9 +292,11 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration);
- if(r!=UPNPCOMMAND_SUCCESS)
+ if(r!=UPNPCOMMAND_SUCCESS) {
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
+ return -2;
+ }
}
r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
@@ -302,17 +304,19 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
eport, proto, NULL/*remoteHost*/,
intClient, intPort, NULL/*desc*/,
NULL/*enabled*/, duration);
- if(r!=UPNPCOMMAND_SUCCESS)
+ if(r!=UPNPCOMMAND_SUCCESS) {
printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
r, strupnperror(r));
- else {
+ return -2;
+ } else {
printf("InternalIP:Port = %s:%s\n", intClient, intPort);
printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
externalIPAddress, eport, proto, intClient, intPort, duration);
}
+ return 0;
}
-static void
+static int
RemoveRedirect(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * eport,
@@ -323,19 +327,25 @@ RemoveRedirect(struct UPNPUrls * urls,
if(!proto || !eport)
{
fprintf(stderr, "invalid arguments\n");
- return;
+ return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
- return;
+ return -1;
}
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
- printf("UPNP_DeletePortMapping() returned : %d\n", r);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMapping() returned : %d\n", r);
+ }
+ return 0;
}
-static void
+static int
RemoveRedirectRange(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * ePortStart, char const * ePortEnd,
@@ -349,16 +359,22 @@ RemoveRedirectRange(struct UPNPUrls * urls,
if(!proto || !ePortStart || !ePortEnd)
{
fprintf(stderr, "invalid arguments\n");
- return;
+ return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
- return;
+ return -1;
}
r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
- printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+ }
+ return 0;
}
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
@@ -562,8 +578,8 @@ int main(int argc, char ** argv)
}
#endif
printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
- printf(" (c) 2005-2016 Thomas Bernard.\n");
- printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
+ printf(" (c) 2005-2017 Thomas Bernard.\n");
+ printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
"for more information.\n");
/* command line processing */
for(i=1; i<argc; i++)
@@ -711,29 +727,33 @@ int main(int argc, char ** argv)
NewListRedirections(&urls, &data);
break;
case 'a':
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
- description, 0);
+ description, 0) < 0)
+ retcode = 2;
break;
case 'd':
- RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
- commandargc > 2 ? commandargv[2] : NULL);
+ if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
+ commandargc > 2 ? commandargv[2] : NULL) < 0)
+ retcode = 2;
break;
case 'n': /* aNy */
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
- description, 1);
+ description, 1) < 0)
+ retcode = 2;
break;
case 'N':
if (commandargc < 3)
fprintf(stderr, "too few arguments\n");
- RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
- commandargc > 3 ? commandargv[3] : NULL);
+ if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
+ commandargc > 3 ? commandargv[3] : NULL) < 0)
+ retcode = 2;
break;
case 's':
GetConnectionStatus(&urls, &data);
@@ -749,17 +769,19 @@ int main(int argc, char ** argv)
break;
} else if(is_int(commandargv[i+1])){
/* 2nd parameter is an integer : <port> <external_port> <protocol> */
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i+1], commandargv[i+2], "0",
- description, 0);
+ description, 0) < 0)
+ retcode = 2;
i+=3; /* 3 parameters parsed */
} else {
/* 2nd parameter not an integer : <port> <protocol> */
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i], commandargv[i+1], "0",
- description, 0);
+ description, 0) < 0)
+ retcode = 2;
i+=2; /* 2 parameters parsed */
}
}
diff --git a/ext/miniupnpc/upnpcommands.c b/ext/miniupnpc/upnpcommands.c
index 2b65651b..d786e533 100644
--- a/ext/miniupnpc/upnpcommands.c
+++ b/ext/miniupnpc/upnpcommands.c
@@ -1,9 +1,8 @@
-#define _CRT_SECURE_NO_WARNINGS
-
-/* $Id: upnpcommands.c,v 1.47 2016/03/07 12:26:48 nanard Exp $ */
-/* Project : miniupnp
+/* $Id: upnpcommands.c,v 1.48 2017/04/21 10:22:40 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
@@ -374,10 +373,11 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
AddPortMappingArgs[7].elt = "NewLeaseDuration";
AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -394,7 +394,6 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
return ret;
}
@@ -438,10 +437,11 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
AddPortMappingArgs[7].elt = "NewLeaseDuration";
AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddAnyPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddAnyPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -463,7 +463,6 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
}
}
ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
return ret;
}
@@ -492,10 +491,11 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
DeletePortMappingArgs[1].val = extPort;
DeletePortMappingArgs[2].elt = "NewProtocol";
DeletePortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMapping",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -509,7 +509,6 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
return ret;
}
@@ -541,10 +540,11 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
DeletePortMappingArgs[3].elt = "NewManage";
DeletePortMappingArgs[3].val = manage;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMappingRange",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMappingRange",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -557,7 +557,6 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
return ret;
}
@@ -589,10 +588,11 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
return UPNPCOMMAND_MEM_ALLOC_ERROR;
GetPortMappingArgs[0].elt = "NewPortMappingIndex";
GetPortMappingArgs[0].val = index;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetGenericPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -654,7 +654,6 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
sscanf(p, "%d", &r);
}
ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
return r;
}
@@ -730,10 +729,11 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
GetPortMappingArgs[1].val = extPort;
GetPortMappingArgs[2].elt = "NewProtocol";
GetPortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetSpecificPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -781,7 +781,6 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
}
ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
return ret;
}
@@ -826,13 +825,13 @@ UPNP_GetListOfPortMappings(const char * controlURL,
GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetListOfPortMappings",
- GetListOfPortMappingsArgs, &bufsize))) {
- free(GetListOfPortMappingsArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize);
+ free(GetListOfPortMappingsArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
- free(GetListOfPortMappingsArgs);
/*DisplayNameValueList(buffer, bufsize);*/
ParseNameValue(buffer, bufsize, &pdata);
@@ -956,6 +955,7 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
GetOutboundPinholeTimeoutArgs[4].val = intClient;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ free(GetOutboundPinholeTimeoutArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -974,7 +974,6 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
*opTimeout = my_atoui(p);
}
ClearNameValueList(&pdata);
- free(GetOutboundPinholeTimeoutArgs);
return ret;
}
@@ -1033,6 +1032,7 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
AddPinholeArgs[5].val = leaseTime;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"AddPinhole", AddPinholeArgs, &bufsize);
+ free(AddPinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1055,7 +1055,6 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(AddPinholeArgs);
return ret;
}
@@ -1083,6 +1082,7 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
UpdatePinholeArgs[1].val = leaseTime;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ free(UpdatePinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1099,7 +1099,6 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(UpdatePinholeArgs);
return ret;
}
@@ -1124,6 +1123,7 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
DeletePinholeArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"DeletePinhole", DeletePinholeArgs, &bufsize);
+ free(DeletePinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
/*DisplayNameValueList(buffer, bufsize);*/
@@ -1140,7 +1140,6 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePinholeArgs);
return ret;
}
@@ -1165,8 +1164,11 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
CheckPinholeWorkingArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ free(CheckPinholeWorkingArgs);
if(!buffer)
+ {
return UPNPCOMMAND_HTTP_ERROR;
+ }
ParseNameValue(buffer, bufsize, &pdata);
free(buffer); buffer = NULL;
@@ -1187,7 +1189,6 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
}
ClearNameValueList(&pdata);
- free(CheckPinholeWorkingArgs);
return ret;
}
@@ -1212,6 +1213,7 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
GetPinholePacketsArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ free(GetPinholePacketsArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1232,7 +1234,6 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
}
ClearNameValueList(&pdata);
- free(GetPinholePacketsArgs);
return ret;
}
diff --git a/ext/miniupnpc/upnpreplyparse.c b/ext/miniupnpc/upnpreplyparse.c
index 88d77a66..5921349d 100644
--- a/ext/miniupnpc/upnpreplyparse.c
+++ b/ext/miniupnpc/upnpreplyparse.c
@@ -1,8 +1,8 @@
-#define _CRT_SECURE_NO_WARNINGS
-/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
-/* MiniUPnP project
+/* $Id: upnpreplyparse.c,v 1.20 2017/12/12 11:26:25 nanard Exp $ */
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2006-2015 Thomas Bernard
+ * (c) 2006-2017 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
@@ -27,12 +27,12 @@ NameValueParserStartElt(void * d, const char * name, int l)
}
static void
-NameValueParserEndElt(void * d, const char * name, int l)
+NameValueParserEndElt(void * d, const char * name, int namelen)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
struct NameValue * nv;
(void)name;
- (void)l;
+ (void)namelen;
if(!data->topelt)
return;
if(strcmp(data->curelt, "NewPortListing") != 0)
@@ -105,9 +105,7 @@ ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data)
{
struct xmlparser parser;
- data->l_head = NULL;
- data->portListing = NULL;
- data->portListingLength = 0;
+ memset(data, 0, sizeof(struct NameValueParserData));
/* init xmlparser object */
parser.xmlstart = buffer;
parser.xmlsize = bufsize;
diff --git a/ext/misc/linux-old-glibc-compat.c b/ext/misc/linux-old-glibc-compat.c
new file mode 100644
index 00000000..6d793a2d
--- /dev/null
+++ b/ext/misc/linux-old-glibc-compat.c
@@ -0,0 +1,18 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void *__wrap_memcpy(void *dest,const void *src,size_t n)
+{
+ return memcpy(dest,src,n);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ext/x64-salsa2012-asm/README.md b/ext/x64-salsa2012-asm/README.md
new file mode 100644
index 00000000..a69a1a67
--- /dev/null
+++ b/ext/x64-salsa2012-asm/README.md
@@ -0,0 +1,6 @@
+Blazingly fast X64 ASM implementation of Salsa20/12
+======
+
+This is ripped from the [cnacl](https://github.com/cjdelisle/cnacl) source. The actual code is by Danial J. Bernstein and is in the public domain.
+
+This is included on Linux and Mac 64-bit builds and is significantly faster than the SSE intrinsics or C versions. It's used for packet encode/decode only since its use differs a bit from the regular Salsa20 C++ class. Specifically it lacks the ability to be called on multiple blocks, preferring instead to take a key and a single stream to encrypt and that's it.
diff --git a/ext/x64-salsa2012-asm/salsa2012.h b/ext/x64-salsa2012-asm/salsa2012.h
new file mode 100644
index 00000000..73e375eb
--- /dev/null
+++ b/ext/x64-salsa2012-asm/salsa2012.h
@@ -0,0 +1,16 @@
+#ifndef ZT_X64_SALSA2012_ASM
+#define ZT_X64_SALSA2012_ASM
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Generates Salsa20/12 key stream
+// output, outlen, nonce, key (256-bit / 32-byte)
+extern int zt_salsa2012_amd64_xmm6(unsigned char *, unsigned long long, const unsigned char *, const unsigned char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ext/x64-salsa2012-asm/salsa2012.s b/ext/x64-salsa2012-asm/salsa2012.s
new file mode 100644
index 00000000..699c89ac
--- /dev/null
+++ b/ext/x64-salsa2012-asm/salsa2012.s
@@ -0,0 +1,4488 @@
+# qhasm: enter zt_salsa2012_amd64_xmm6
+.text
+.p2align 5
+.globl _zt_salsa2012_amd64_xmm6
+.globl zt_salsa2012_amd64_xmm6
+_zt_salsa2012_amd64_xmm6:
+zt_salsa2012_amd64_xmm6:
+mov %rsp,%r11
+and $31,%r11
+add $480,%r11
+sub %r11,%rsp
+
+# qhasm: r11_stack = r11_caller
+# asm 1: movq <r11_caller=int64#9,>r11_stack=stack64#1
+# asm 2: movq <r11_caller=%r11,>r11_stack=352(%rsp)
+movq %r11,352(%rsp)
+
+# qhasm: r12_stack = r12_caller
+# asm 1: movq <r12_caller=int64#10,>r12_stack=stack64#2
+# asm 2: movq <r12_caller=%r12,>r12_stack=360(%rsp)
+movq %r12,360(%rsp)
+
+# qhasm: r13_stack = r13_caller
+# asm 1: movq <r13_caller=int64#11,>r13_stack=stack64#3
+# asm 2: movq <r13_caller=%r13,>r13_stack=368(%rsp)
+movq %r13,368(%rsp)
+
+# qhasm: r14_stack = r14_caller
+# asm 1: movq <r14_caller=int64#12,>r14_stack=stack64#4
+# asm 2: movq <r14_caller=%r14,>r14_stack=376(%rsp)
+movq %r14,376(%rsp)
+
+# qhasm: r15_stack = r15_caller
+# asm 1: movq <r15_caller=int64#13,>r15_stack=stack64#5
+# asm 2: movq <r15_caller=%r15,>r15_stack=384(%rsp)
+movq %r15,384(%rsp)
+
+# qhasm: rbx_stack = rbx_caller
+# asm 1: movq <rbx_caller=int64#14,>rbx_stack=stack64#6
+# asm 2: movq <rbx_caller=%rbx,>rbx_stack=392(%rsp)
+movq %rbx,392(%rsp)
+
+# qhasm: rbp_stack = rbp_caller
+# asm 1: movq <rbp_caller=int64#15,>rbp_stack=stack64#7
+# asm 2: movq <rbp_caller=%rbp,>rbp_stack=400(%rsp)
+movq %rbp,400(%rsp)
+
+# qhasm: bytes = arg2
+# asm 1: mov <arg2=int64#2,>bytes=int64#6
+# asm 2: mov <arg2=%rsi,>bytes=%r9
+mov %rsi,%r9
+
+# qhasm: out = arg1
+# asm 1: mov <arg1=int64#1,>out=int64#1
+# asm 2: mov <arg1=%rdi,>out=%rdi
+mov %rdi,%rdi
+
+# qhasm: m = out
+# asm 1: mov <out=int64#1,>m=int64#2
+# asm 2: mov <out=%rdi,>m=%rsi
+mov %rdi,%rsi
+
+# qhasm: iv = arg3
+# asm 1: mov <arg3=int64#3,>iv=int64#3
+# asm 2: mov <arg3=%rdx,>iv=%rdx
+mov %rdx,%rdx
+
+# qhasm: k = arg4
+# asm 1: mov <arg4=int64#4,>k=int64#8
+# asm 2: mov <arg4=%rcx,>k=%r10
+mov %rcx,%r10
+
+# qhasm: unsigned>? bytes - 0
+# asm 1: cmp $0,<bytes=int64#6
+# asm 2: cmp $0,<bytes=%r9
+cmp $0,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto done if !unsigned>
+jbe ._done
+
+# qhasm: a = 0
+# asm 1: mov $0,>a=int64#7
+# asm 2: mov $0,>a=%rax
+mov $0,%rax
+
+# qhasm: i = bytes
+# asm 1: mov <bytes=int64#6,>i=int64#4
+# asm 2: mov <bytes=%r9,>i=%rcx
+mov %r9,%rcx
+
+# qhasm: while (i) { *out++ = a; --i }
+rep stosb
+
+# qhasm: out -= bytes
+# asm 1: sub <bytes=int64#6,<out=int64#1
+# asm 2: sub <bytes=%r9,<out=%rdi
+sub %r9,%rdi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto start
+jmp ._start
+
+# qhasm: enter zt_salsa2012_amd64_xmm6_xor
+.text
+.p2align 5
+.globl _zt_salsa2012_amd64_xmm6_xor
+.globl zt_salsa2012_amd64_xmm6_xor
+_zt_salsa2012_amd64_xmm6_xor:
+zt_salsa2012_amd64_xmm6_xor:
+mov %rsp,%r11
+and $31,%r11
+add $480,%r11
+sub %r11,%rsp
+
+# qhasm: r11_stack = r11_caller
+# asm 1: movq <r11_caller=int64#9,>r11_stack=stack64#1
+# asm 2: movq <r11_caller=%r11,>r11_stack=352(%rsp)
+movq %r11,352(%rsp)
+
+# qhasm: r12_stack = r12_caller
+# asm 1: movq <r12_caller=int64#10,>r12_stack=stack64#2
+# asm 2: movq <r12_caller=%r12,>r12_stack=360(%rsp)
+movq %r12,360(%rsp)
+
+# qhasm: r13_stack = r13_caller
+# asm 1: movq <r13_caller=int64#11,>r13_stack=stack64#3
+# asm 2: movq <r13_caller=%r13,>r13_stack=368(%rsp)
+movq %r13,368(%rsp)
+
+# qhasm: r14_stack = r14_caller
+# asm 1: movq <r14_caller=int64#12,>r14_stack=stack64#4
+# asm 2: movq <r14_caller=%r14,>r14_stack=376(%rsp)
+movq %r14,376(%rsp)
+
+# qhasm: r15_stack = r15_caller
+# asm 1: movq <r15_caller=int64#13,>r15_stack=stack64#5
+# asm 2: movq <r15_caller=%r15,>r15_stack=384(%rsp)
+movq %r15,384(%rsp)
+
+# qhasm: rbx_stack = rbx_caller
+# asm 1: movq <rbx_caller=int64#14,>rbx_stack=stack64#6
+# asm 2: movq <rbx_caller=%rbx,>rbx_stack=392(%rsp)
+movq %rbx,392(%rsp)
+
+# qhasm: rbp_stack = rbp_caller
+# asm 1: movq <rbp_caller=int64#15,>rbp_stack=stack64#7
+# asm 2: movq <rbp_caller=%rbp,>rbp_stack=400(%rsp)
+movq %rbp,400(%rsp)
+
+# qhasm: out = arg1
+# asm 1: mov <arg1=int64#1,>out=int64#1
+# asm 2: mov <arg1=%rdi,>out=%rdi
+mov %rdi,%rdi
+
+# qhasm: m = arg2
+# asm 1: mov <arg2=int64#2,>m=int64#2
+# asm 2: mov <arg2=%rsi,>m=%rsi
+mov %rsi,%rsi
+
+# qhasm: bytes = arg3
+# asm 1: mov <arg3=int64#3,>bytes=int64#6
+# asm 2: mov <arg3=%rdx,>bytes=%r9
+mov %rdx,%r9
+
+# qhasm: iv = arg4
+# asm 1: mov <arg4=int64#4,>iv=int64#3
+# asm 2: mov <arg4=%rcx,>iv=%rdx
+mov %rcx,%rdx
+
+# qhasm: k = arg5
+# asm 1: mov <arg5=int64#5,>k=int64#8
+# asm 2: mov <arg5=%r8,>k=%r10
+mov %r8,%r10
+
+# qhasm: unsigned>? bytes - 0
+# asm 1: cmp $0,<bytes=int64#6
+# asm 2: cmp $0,<bytes=%r9
+cmp $0,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto done if !unsigned>
+jbe ._done
+# comment:fp stack unchanged by fallthrough
+
+# qhasm: start:
+._start:
+
+# qhasm: in12 = *(uint32 *) (k + 20)
+# asm 1: movl 20(<k=int64#8),>in12=int64#4d
+# asm 2: movl 20(<k=%r10),>in12=%ecx
+movl 20(%r10),%ecx
+
+# qhasm: in1 = *(uint32 *) (k + 0)
+# asm 1: movl 0(<k=int64#8),>in1=int64#5d
+# asm 2: movl 0(<k=%r10),>in1=%r8d
+movl 0(%r10),%r8d
+
+# qhasm: in6 = *(uint32 *) (iv + 0)
+# asm 1: movl 0(<iv=int64#3),>in6=int64#7d
+# asm 2: movl 0(<iv=%rdx),>in6=%eax
+movl 0(%rdx),%eax
+
+# qhasm: in11 = *(uint32 *) (k + 16)
+# asm 1: movl 16(<k=int64#8),>in11=int64#9d
+# asm 2: movl 16(<k=%r10),>in11=%r11d
+movl 16(%r10),%r11d
+
+# qhasm: ((uint32 *)&x1)[0] = in12
+# asm 1: movl <in12=int64#4d,>x1=stack128#1
+# asm 2: movl <in12=%ecx,>x1=0(%rsp)
+movl %ecx,0(%rsp)
+
+# qhasm: ((uint32 *)&x1)[1] = in1
+# asm 1: movl <in1=int64#5d,4+<x1=stack128#1
+# asm 2: movl <in1=%r8d,4+<x1=0(%rsp)
+movl %r8d,4+0(%rsp)
+
+# qhasm: ((uint32 *)&x1)[2] = in6
+# asm 1: movl <in6=int64#7d,8+<x1=stack128#1
+# asm 2: movl <in6=%eax,8+<x1=0(%rsp)
+movl %eax,8+0(%rsp)
+
+# qhasm: ((uint32 *)&x1)[3] = in11
+# asm 1: movl <in11=int64#9d,12+<x1=stack128#1
+# asm 2: movl <in11=%r11d,12+<x1=0(%rsp)
+movl %r11d,12+0(%rsp)
+
+# qhasm: in8 = 0
+# asm 1: mov $0,>in8=int64#4
+# asm 2: mov $0,>in8=%rcx
+mov $0,%rcx
+
+# qhasm: in13 = *(uint32 *) (k + 24)
+# asm 1: movl 24(<k=int64#8),>in13=int64#5d
+# asm 2: movl 24(<k=%r10),>in13=%r8d
+movl 24(%r10),%r8d
+
+# qhasm: in2 = *(uint32 *) (k + 4)
+# asm 1: movl 4(<k=int64#8),>in2=int64#7d
+# asm 2: movl 4(<k=%r10),>in2=%eax
+movl 4(%r10),%eax
+
+# qhasm: in7 = *(uint32 *) (iv + 4)
+# asm 1: movl 4(<iv=int64#3),>in7=int64#3d
+# asm 2: movl 4(<iv=%rdx),>in7=%edx
+movl 4(%rdx),%edx
+
+# qhasm: ((uint32 *)&x2)[0] = in8
+# asm 1: movl <in8=int64#4d,>x2=stack128#2
+# asm 2: movl <in8=%ecx,>x2=16(%rsp)
+movl %ecx,16(%rsp)
+
+# qhasm: ((uint32 *)&x2)[1] = in13
+# asm 1: movl <in13=int64#5d,4+<x2=stack128#2
+# asm 2: movl <in13=%r8d,4+<x2=16(%rsp)
+movl %r8d,4+16(%rsp)
+
+# qhasm: ((uint32 *)&x2)[2] = in2
+# asm 1: movl <in2=int64#7d,8+<x2=stack128#2
+# asm 2: movl <in2=%eax,8+<x2=16(%rsp)
+movl %eax,8+16(%rsp)
+
+# qhasm: ((uint32 *)&x2)[3] = in7
+# asm 1: movl <in7=int64#3d,12+<x2=stack128#2
+# asm 2: movl <in7=%edx,12+<x2=16(%rsp)
+movl %edx,12+16(%rsp)
+
+# qhasm: in4 = *(uint32 *) (k + 12)
+# asm 1: movl 12(<k=int64#8),>in4=int64#3d
+# asm 2: movl 12(<k=%r10),>in4=%edx
+movl 12(%r10),%edx
+
+# qhasm: in9 = 0
+# asm 1: mov $0,>in9=int64#4
+# asm 2: mov $0,>in9=%rcx
+mov $0,%rcx
+
+# qhasm: in14 = *(uint32 *) (k + 28)
+# asm 1: movl 28(<k=int64#8),>in14=int64#5d
+# asm 2: movl 28(<k=%r10),>in14=%r8d
+movl 28(%r10),%r8d
+
+# qhasm: in3 = *(uint32 *) (k + 8)
+# asm 1: movl 8(<k=int64#8),>in3=int64#7d
+# asm 2: movl 8(<k=%r10),>in3=%eax
+movl 8(%r10),%eax
+
+# qhasm: ((uint32 *)&x3)[0] = in4
+# asm 1: movl <in4=int64#3d,>x3=stack128#3
+# asm 2: movl <in4=%edx,>x3=32(%rsp)
+movl %edx,32(%rsp)
+
+# qhasm: ((uint32 *)&x3)[1] = in9
+# asm 1: movl <in9=int64#4d,4+<x3=stack128#3
+# asm 2: movl <in9=%ecx,4+<x3=32(%rsp)
+movl %ecx,4+32(%rsp)
+
+# qhasm: ((uint32 *)&x3)[2] = in14
+# asm 1: movl <in14=int64#5d,8+<x3=stack128#3
+# asm 2: movl <in14=%r8d,8+<x3=32(%rsp)
+movl %r8d,8+32(%rsp)
+
+# qhasm: ((uint32 *)&x3)[3] = in3
+# asm 1: movl <in3=int64#7d,12+<x3=stack128#3
+# asm 2: movl <in3=%eax,12+<x3=32(%rsp)
+movl %eax,12+32(%rsp)
+
+# qhasm: in0 = 1634760805
+# asm 1: mov $1634760805,>in0=int64#3
+# asm 2: mov $1634760805,>in0=%rdx
+mov $1634760805,%rdx
+
+# qhasm: in5 = 857760878
+# asm 1: mov $857760878,>in5=int64#4
+# asm 2: mov $857760878,>in5=%rcx
+mov $857760878,%rcx
+
+# qhasm: in10 = 2036477234
+# asm 1: mov $2036477234,>in10=int64#5
+# asm 2: mov $2036477234,>in10=%r8
+mov $2036477234,%r8
+
+# qhasm: in15 = 1797285236
+# asm 1: mov $1797285236,>in15=int64#7
+# asm 2: mov $1797285236,>in15=%rax
+mov $1797285236,%rax
+
+# qhasm: ((uint32 *)&x0)[0] = in0
+# asm 1: movl <in0=int64#3d,>x0=stack128#4
+# asm 2: movl <in0=%edx,>x0=48(%rsp)
+movl %edx,48(%rsp)
+
+# qhasm: ((uint32 *)&x0)[1] = in5
+# asm 1: movl <in5=int64#4d,4+<x0=stack128#4
+# asm 2: movl <in5=%ecx,4+<x0=48(%rsp)
+movl %ecx,4+48(%rsp)
+
+# qhasm: ((uint32 *)&x0)[2] = in10
+# asm 1: movl <in10=int64#5d,8+<x0=stack128#4
+# asm 2: movl <in10=%r8d,8+<x0=48(%rsp)
+movl %r8d,8+48(%rsp)
+
+# qhasm: ((uint32 *)&x0)[3] = in15
+# asm 1: movl <in15=int64#7d,12+<x0=stack128#4
+# asm 2: movl <in15=%eax,12+<x0=48(%rsp)
+movl %eax,12+48(%rsp)
+
+# qhasm: unsigned<? bytes - 256
+# asm 1: cmp $256,<bytes=int64#6
+# asm 2: cmp $256,<bytes=%r9
+cmp $256,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto bytesbetween1and255 if unsigned<
+jb ._bytesbetween1and255
+
+# qhasm: z0 = x0
+# asm 1: movdqa <x0=stack128#4,>z0=int6464#1
+# asm 2: movdqa <x0=48(%rsp),>z0=%xmm0
+movdqa 48(%rsp),%xmm0
+
+# qhasm: z5 = z0[1,1,1,1]
+# asm 1: pshufd $0x55,<z0=int6464#1,>z5=int6464#2
+# asm 2: pshufd $0x55,<z0=%xmm0,>z5=%xmm1
+pshufd $0x55,%xmm0,%xmm1
+
+# qhasm: z10 = z0[2,2,2,2]
+# asm 1: pshufd $0xaa,<z0=int6464#1,>z10=int6464#3
+# asm 2: pshufd $0xaa,<z0=%xmm0,>z10=%xmm2
+pshufd $0xaa,%xmm0,%xmm2
+
+# qhasm: z15 = z0[3,3,3,3]
+# asm 1: pshufd $0xff,<z0=int6464#1,>z15=int6464#4
+# asm 2: pshufd $0xff,<z0=%xmm0,>z15=%xmm3
+pshufd $0xff,%xmm0,%xmm3
+
+# qhasm: z0 = z0[0,0,0,0]
+# asm 1: pshufd $0x00,<z0=int6464#1,>z0=int6464#1
+# asm 2: pshufd $0x00,<z0=%xmm0,>z0=%xmm0
+pshufd $0x00,%xmm0,%xmm0
+
+# qhasm: orig5 = z5
+# asm 1: movdqa <z5=int6464#2,>orig5=stack128#5
+# asm 2: movdqa <z5=%xmm1,>orig5=64(%rsp)
+movdqa %xmm1,64(%rsp)
+
+# qhasm: orig10 = z10
+# asm 1: movdqa <z10=int6464#3,>orig10=stack128#6
+# asm 2: movdqa <z10=%xmm2,>orig10=80(%rsp)
+movdqa %xmm2,80(%rsp)
+
+# qhasm: orig15 = z15
+# asm 1: movdqa <z15=int6464#4,>orig15=stack128#7
+# asm 2: movdqa <z15=%xmm3,>orig15=96(%rsp)
+movdqa %xmm3,96(%rsp)
+
+# qhasm: orig0 = z0
+# asm 1: movdqa <z0=int6464#1,>orig0=stack128#8
+# asm 2: movdqa <z0=%xmm0,>orig0=112(%rsp)
+movdqa %xmm0,112(%rsp)
+
+# qhasm: z1 = x1
+# asm 1: movdqa <x1=stack128#1,>z1=int6464#1
+# asm 2: movdqa <x1=0(%rsp),>z1=%xmm0
+movdqa 0(%rsp),%xmm0
+
+# qhasm: z6 = z1[2,2,2,2]
+# asm 1: pshufd $0xaa,<z1=int6464#1,>z6=int6464#2
+# asm 2: pshufd $0xaa,<z1=%xmm0,>z6=%xmm1
+pshufd $0xaa,%xmm0,%xmm1
+
+# qhasm: z11 = z1[3,3,3,3]
+# asm 1: pshufd $0xff,<z1=int6464#1,>z11=int6464#3
+# asm 2: pshufd $0xff,<z1=%xmm0,>z11=%xmm2
+pshufd $0xff,%xmm0,%xmm2
+
+# qhasm: z12 = z1[0,0,0,0]
+# asm 1: pshufd $0x00,<z1=int6464#1,>z12=int6464#4
+# asm 2: pshufd $0x00,<z1=%xmm0,>z12=%xmm3
+pshufd $0x00,%xmm0,%xmm3
+
+# qhasm: z1 = z1[1,1,1,1]
+# asm 1: pshufd $0x55,<z1=int6464#1,>z1=int6464#1
+# asm 2: pshufd $0x55,<z1=%xmm0,>z1=%xmm0
+pshufd $0x55,%xmm0,%xmm0
+
+# qhasm: orig6 = z6
+# asm 1: movdqa <z6=int6464#2,>orig6=stack128#9
+# asm 2: movdqa <z6=%xmm1,>orig6=128(%rsp)
+movdqa %xmm1,128(%rsp)
+
+# qhasm: orig11 = z11
+# asm 1: movdqa <z11=int6464#3,>orig11=stack128#10
+# asm 2: movdqa <z11=%xmm2,>orig11=144(%rsp)
+movdqa %xmm2,144(%rsp)
+
+# qhasm: orig12 = z12
+# asm 1: movdqa <z12=int6464#4,>orig12=stack128#11
+# asm 2: movdqa <z12=%xmm3,>orig12=160(%rsp)
+movdqa %xmm3,160(%rsp)
+
+# qhasm: orig1 = z1
+# asm 1: movdqa <z1=int6464#1,>orig1=stack128#12
+# asm 2: movdqa <z1=%xmm0,>orig1=176(%rsp)
+movdqa %xmm0,176(%rsp)
+
+# qhasm: z2 = x2
+# asm 1: movdqa <x2=stack128#2,>z2=int6464#1
+# asm 2: movdqa <x2=16(%rsp),>z2=%xmm0
+movdqa 16(%rsp),%xmm0
+
+# qhasm: z7 = z2[3,3,3,3]
+# asm 1: pshufd $0xff,<z2=int6464#1,>z7=int6464#2
+# asm 2: pshufd $0xff,<z2=%xmm0,>z7=%xmm1
+pshufd $0xff,%xmm0,%xmm1
+
+# qhasm: z13 = z2[1,1,1,1]
+# asm 1: pshufd $0x55,<z2=int6464#1,>z13=int6464#3
+# asm 2: pshufd $0x55,<z2=%xmm0,>z13=%xmm2
+pshufd $0x55,%xmm0,%xmm2
+
+# qhasm: z2 = z2[2,2,2,2]
+# asm 1: pshufd $0xaa,<z2=int6464#1,>z2=int6464#1
+# asm 2: pshufd $0xaa,<z2=%xmm0,>z2=%xmm0
+pshufd $0xaa,%xmm0,%xmm0
+
+# qhasm: orig7 = z7
+# asm 1: movdqa <z7=int6464#2,>orig7=stack128#13
+# asm 2: movdqa <z7=%xmm1,>orig7=192(%rsp)
+movdqa %xmm1,192(%rsp)
+
+# qhasm: orig13 = z13
+# asm 1: movdqa <z13=int6464#3,>orig13=stack128#14
+# asm 2: movdqa <z13=%xmm2,>orig13=208(%rsp)
+movdqa %xmm2,208(%rsp)
+
+# qhasm: orig2 = z2
+# asm 1: movdqa <z2=int6464#1,>orig2=stack128#15
+# asm 2: movdqa <z2=%xmm0,>orig2=224(%rsp)
+movdqa %xmm0,224(%rsp)
+
+# qhasm: z3 = x3
+# asm 1: movdqa <x3=stack128#3,>z3=int6464#1
+# asm 2: movdqa <x3=32(%rsp),>z3=%xmm0
+movdqa 32(%rsp),%xmm0
+
+# qhasm: z4 = z3[0,0,0,0]
+# asm 1: pshufd $0x00,<z3=int6464#1,>z4=int6464#2
+# asm 2: pshufd $0x00,<z3=%xmm0,>z4=%xmm1
+pshufd $0x00,%xmm0,%xmm1
+
+# qhasm: z14 = z3[2,2,2,2]
+# asm 1: pshufd $0xaa,<z3=int6464#1,>z14=int6464#3
+# asm 2: pshufd $0xaa,<z3=%xmm0,>z14=%xmm2
+pshufd $0xaa,%xmm0,%xmm2
+
+# qhasm: z3 = z3[3,3,3,3]
+# asm 1: pshufd $0xff,<z3=int6464#1,>z3=int6464#1
+# asm 2: pshufd $0xff,<z3=%xmm0,>z3=%xmm0
+pshufd $0xff,%xmm0,%xmm0
+
+# qhasm: orig4 = z4
+# asm 1: movdqa <z4=int6464#2,>orig4=stack128#16
+# asm 2: movdqa <z4=%xmm1,>orig4=240(%rsp)
+movdqa %xmm1,240(%rsp)
+
+# qhasm: orig14 = z14
+# asm 1: movdqa <z14=int6464#3,>orig14=stack128#17
+# asm 2: movdqa <z14=%xmm2,>orig14=256(%rsp)
+movdqa %xmm2,256(%rsp)
+
+# qhasm: orig3 = z3
+# asm 1: movdqa <z3=int6464#1,>orig3=stack128#18
+# asm 2: movdqa <z3=%xmm0,>orig3=272(%rsp)
+movdqa %xmm0,272(%rsp)
+
+# qhasm: bytesatleast256:
+._bytesatleast256:
+
+# qhasm: in8 = ((uint32 *)&x2)[0]
+# asm 1: movl <x2=stack128#2,>in8=int64#3d
+# asm 2: movl <x2=16(%rsp),>in8=%edx
+movl 16(%rsp),%edx
+
+# qhasm: in9 = ((uint32 *)&x3)[1]
+# asm 1: movl 4+<x3=stack128#3,>in9=int64#4d
+# asm 2: movl 4+<x3=32(%rsp),>in9=%ecx
+movl 4+32(%rsp),%ecx
+
+# qhasm: ((uint32 *) &orig8)[0] = in8
+# asm 1: movl <in8=int64#3d,>orig8=stack128#19
+# asm 2: movl <in8=%edx,>orig8=288(%rsp)
+movl %edx,288(%rsp)
+
+# qhasm: ((uint32 *) &orig9)[0] = in9
+# asm 1: movl <in9=int64#4d,>orig9=stack128#20
+# asm 2: movl <in9=%ecx,>orig9=304(%rsp)
+movl %ecx,304(%rsp)
+
+# qhasm: in8 += 1
+# asm 1: add $1,<in8=int64#3
+# asm 2: add $1,<in8=%rdx
+add $1,%rdx
+
+# qhasm: in9 <<= 32
+# asm 1: shl $32,<in9=int64#4
+# asm 2: shl $32,<in9=%rcx
+shl $32,%rcx
+
+# qhasm: in8 += in9
+# asm 1: add <in9=int64#4,<in8=int64#3
+# asm 2: add <in9=%rcx,<in8=%rdx
+add %rcx,%rdx
+
+# qhasm: in9 = in8
+# asm 1: mov <in8=int64#3,>in9=int64#4
+# asm 2: mov <in8=%rdx,>in9=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint64) in9 >>= 32
+# asm 1: shr $32,<in9=int64#4
+# asm 2: shr $32,<in9=%rcx
+shr $32,%rcx
+
+# qhasm: ((uint32 *) &orig8)[1] = in8
+# asm 1: movl <in8=int64#3d,4+<orig8=stack128#19
+# asm 2: movl <in8=%edx,4+<orig8=288(%rsp)
+movl %edx,4+288(%rsp)
+
+# qhasm: ((uint32 *) &orig9)[1] = in9
+# asm 1: movl <in9=int64#4d,4+<orig9=stack128#20
+# asm 2: movl <in9=%ecx,4+<orig9=304(%rsp)
+movl %ecx,4+304(%rsp)
+
+# qhasm: in8 += 1
+# asm 1: add $1,<in8=int64#3
+# asm 2: add $1,<in8=%rdx
+add $1,%rdx
+
+# qhasm: in9 <<= 32
+# asm 1: shl $32,<in9=int64#4
+# asm 2: shl $32,<in9=%rcx
+shl $32,%rcx
+
+# qhasm: in8 += in9
+# asm 1: add <in9=int64#4,<in8=int64#3
+# asm 2: add <in9=%rcx,<in8=%rdx
+add %rcx,%rdx
+
+# qhasm: in9 = in8
+# asm 1: mov <in8=int64#3,>in9=int64#4
+# asm 2: mov <in8=%rdx,>in9=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint64) in9 >>= 32
+# asm 1: shr $32,<in9=int64#4
+# asm 2: shr $32,<in9=%rcx
+shr $32,%rcx
+
+# qhasm: ((uint32 *) &orig8)[2] = in8
+# asm 1: movl <in8=int64#3d,8+<orig8=stack128#19
+# asm 2: movl <in8=%edx,8+<orig8=288(%rsp)
+movl %edx,8+288(%rsp)
+
+# qhasm: ((uint32 *) &orig9)[2] = in9
+# asm 1: movl <in9=int64#4d,8+<orig9=stack128#20
+# asm 2: movl <in9=%ecx,8+<orig9=304(%rsp)
+movl %ecx,8+304(%rsp)
+
+# qhasm: in8 += 1
+# asm 1: add $1,<in8=int64#3
+# asm 2: add $1,<in8=%rdx
+add $1,%rdx
+
+# qhasm: in9 <<= 32
+# asm 1: shl $32,<in9=int64#4
+# asm 2: shl $32,<in9=%rcx
+shl $32,%rcx
+
+# qhasm: in8 += in9
+# asm 1: add <in9=int64#4,<in8=int64#3
+# asm 2: add <in9=%rcx,<in8=%rdx
+add %rcx,%rdx
+
+# qhasm: in9 = in8
+# asm 1: mov <in8=int64#3,>in9=int64#4
+# asm 2: mov <in8=%rdx,>in9=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint64) in9 >>= 32
+# asm 1: shr $32,<in9=int64#4
+# asm 2: shr $32,<in9=%rcx
+shr $32,%rcx
+
+# qhasm: ((uint32 *) &orig8)[3] = in8
+# asm 1: movl <in8=int64#3d,12+<orig8=stack128#19
+# asm 2: movl <in8=%edx,12+<orig8=288(%rsp)
+movl %edx,12+288(%rsp)
+
+# qhasm: ((uint32 *) &orig9)[3] = in9
+# asm 1: movl <in9=int64#4d,12+<orig9=stack128#20
+# asm 2: movl <in9=%ecx,12+<orig9=304(%rsp)
+movl %ecx,12+304(%rsp)
+
+# qhasm: in8 += 1
+# asm 1: add $1,<in8=int64#3
+# asm 2: add $1,<in8=%rdx
+add $1,%rdx
+
+# qhasm: in9 <<= 32
+# asm 1: shl $32,<in9=int64#4
+# asm 2: shl $32,<in9=%rcx
+shl $32,%rcx
+
+# qhasm: in8 += in9
+# asm 1: add <in9=int64#4,<in8=int64#3
+# asm 2: add <in9=%rcx,<in8=%rdx
+add %rcx,%rdx
+
+# qhasm: in9 = in8
+# asm 1: mov <in8=int64#3,>in9=int64#4
+# asm 2: mov <in8=%rdx,>in9=%rcx
+mov %rdx,%rcx
+
+# qhasm: (uint64) in9 >>= 32
+# asm 1: shr $32,<in9=int64#4
+# asm 2: shr $32,<in9=%rcx
+shr $32,%rcx
+
+# qhasm: ((uint32 *)&x2)[0] = in8
+# asm 1: movl <in8=int64#3d,>x2=stack128#2
+# asm 2: movl <in8=%edx,>x2=16(%rsp)
+movl %edx,16(%rsp)
+
+# qhasm: ((uint32 *)&x3)[1] = in9
+# asm 1: movl <in9=int64#4d,4+<x3=stack128#3
+# asm 2: movl <in9=%ecx,4+<x3=32(%rsp)
+movl %ecx,4+32(%rsp)
+
+# qhasm: bytes_backup = bytes
+# asm 1: movq <bytes=int64#6,>bytes_backup=stack64#8
+# asm 2: movq <bytes=%r9,>bytes_backup=408(%rsp)
+movq %r9,408(%rsp)
+
+# qhasm: i = 12
+# asm 1: mov $12,>i=int64#3
+# asm 2: mov $12,>i=%rdx
+mov $12,%rdx
+
+# qhasm: z5 = orig5
+# asm 1: movdqa <orig5=stack128#5,>z5=int6464#1
+# asm 2: movdqa <orig5=64(%rsp),>z5=%xmm0
+movdqa 64(%rsp),%xmm0
+
+# qhasm: z10 = orig10
+# asm 1: movdqa <orig10=stack128#6,>z10=int6464#2
+# asm 2: movdqa <orig10=80(%rsp),>z10=%xmm1
+movdqa 80(%rsp),%xmm1
+
+# qhasm: z15 = orig15
+# asm 1: movdqa <orig15=stack128#7,>z15=int6464#3
+# asm 2: movdqa <orig15=96(%rsp),>z15=%xmm2
+movdqa 96(%rsp),%xmm2
+
+# qhasm: z14 = orig14
+# asm 1: movdqa <orig14=stack128#17,>z14=int6464#4
+# asm 2: movdqa <orig14=256(%rsp),>z14=%xmm3
+movdqa 256(%rsp),%xmm3
+
+# qhasm: z3 = orig3
+# asm 1: movdqa <orig3=stack128#18,>z3=int6464#5
+# asm 2: movdqa <orig3=272(%rsp),>z3=%xmm4
+movdqa 272(%rsp),%xmm4
+
+# qhasm: z6 = orig6
+# asm 1: movdqa <orig6=stack128#9,>z6=int6464#6
+# asm 2: movdqa <orig6=128(%rsp),>z6=%xmm5
+movdqa 128(%rsp),%xmm5
+
+# qhasm: z11 = orig11
+# asm 1: movdqa <orig11=stack128#10,>z11=int6464#7
+# asm 2: movdqa <orig11=144(%rsp),>z11=%xmm6
+movdqa 144(%rsp),%xmm6
+
+# qhasm: z1 = orig1
+# asm 1: movdqa <orig1=stack128#12,>z1=int6464#8
+# asm 2: movdqa <orig1=176(%rsp),>z1=%xmm7
+movdqa 176(%rsp),%xmm7
+
+# qhasm: z7 = orig7
+# asm 1: movdqa <orig7=stack128#13,>z7=int6464#9
+# asm 2: movdqa <orig7=192(%rsp),>z7=%xmm8
+movdqa 192(%rsp),%xmm8
+
+# qhasm: z13 = orig13
+# asm 1: movdqa <orig13=stack128#14,>z13=int6464#10
+# asm 2: movdqa <orig13=208(%rsp),>z13=%xmm9
+movdqa 208(%rsp),%xmm9
+
+# qhasm: z2 = orig2
+# asm 1: movdqa <orig2=stack128#15,>z2=int6464#11
+# asm 2: movdqa <orig2=224(%rsp),>z2=%xmm10
+movdqa 224(%rsp),%xmm10
+
+# qhasm: z9 = orig9
+# asm 1: movdqa <orig9=stack128#20,>z9=int6464#12
+# asm 2: movdqa <orig9=304(%rsp),>z9=%xmm11
+movdqa 304(%rsp),%xmm11
+
+# qhasm: z0 = orig0
+# asm 1: movdqa <orig0=stack128#8,>z0=int6464#13
+# asm 2: movdqa <orig0=112(%rsp),>z0=%xmm12
+movdqa 112(%rsp),%xmm12
+
+# qhasm: z12 = orig12
+# asm 1: movdqa <orig12=stack128#11,>z12=int6464#14
+# asm 2: movdqa <orig12=160(%rsp),>z12=%xmm13
+movdqa 160(%rsp),%xmm13
+
+# qhasm: z4 = orig4
+# asm 1: movdqa <orig4=stack128#16,>z4=int6464#15
+# asm 2: movdqa <orig4=240(%rsp),>z4=%xmm14
+movdqa 240(%rsp),%xmm14
+
+# qhasm: z8 = orig8
+# asm 1: movdqa <orig8=stack128#19,>z8=int6464#16
+# asm 2: movdqa <orig8=288(%rsp),>z8=%xmm15
+movdqa 288(%rsp),%xmm15
+
+# qhasm: mainloop1:
+._mainloop1:
+
+# qhasm: z10_stack = z10
+# asm 1: movdqa <z10=int6464#2,>z10_stack=stack128#21
+# asm 2: movdqa <z10=%xmm1,>z10_stack=320(%rsp)
+movdqa %xmm1,320(%rsp)
+
+# qhasm: z15_stack = z15
+# asm 1: movdqa <z15=int6464#3,>z15_stack=stack128#22
+# asm 2: movdqa <z15=%xmm2,>z15_stack=336(%rsp)
+movdqa %xmm2,336(%rsp)
+
+# qhasm: y4 = z12
+# asm 1: movdqa <z12=int6464#14,>y4=int6464#2
+# asm 2: movdqa <z12=%xmm13,>y4=%xmm1
+movdqa %xmm13,%xmm1
+
+# qhasm: uint32323232 y4 += z0
+# asm 1: paddd <z0=int6464#13,<y4=int6464#2
+# asm 2: paddd <z0=%xmm12,<y4=%xmm1
+paddd %xmm12,%xmm1
+
+# qhasm: r4 = y4
+# asm 1: movdqa <y4=int6464#2,>r4=int6464#3
+# asm 2: movdqa <y4=%xmm1,>r4=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y4 <<= 7
+# asm 1: pslld $7,<y4=int6464#2
+# asm 2: pslld $7,<y4=%xmm1
+pslld $7,%xmm1
+
+# qhasm: z4 ^= y4
+# asm 1: pxor <y4=int6464#2,<z4=int6464#15
+# asm 2: pxor <y4=%xmm1,<z4=%xmm14
+pxor %xmm1,%xmm14
+
+# qhasm: uint32323232 r4 >>= 25
+# asm 1: psrld $25,<r4=int6464#3
+# asm 2: psrld $25,<r4=%xmm2
+psrld $25,%xmm2
+
+# qhasm: z4 ^= r4
+# asm 1: pxor <r4=int6464#3,<z4=int6464#15
+# asm 2: pxor <r4=%xmm2,<z4=%xmm14
+pxor %xmm2,%xmm14
+
+# qhasm: y9 = z1
+# asm 1: movdqa <z1=int6464#8,>y9=int6464#2
+# asm 2: movdqa <z1=%xmm7,>y9=%xmm1
+movdqa %xmm7,%xmm1
+
+# qhasm: uint32323232 y9 += z5
+# asm 1: paddd <z5=int6464#1,<y9=int6464#2
+# asm 2: paddd <z5=%xmm0,<y9=%xmm1
+paddd %xmm0,%xmm1
+
+# qhasm: r9 = y9
+# asm 1: movdqa <y9=int6464#2,>r9=int6464#3
+# asm 2: movdqa <y9=%xmm1,>r9=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y9 <<= 7
+# asm 1: pslld $7,<y9=int6464#2
+# asm 2: pslld $7,<y9=%xmm1
+pslld $7,%xmm1
+
+# qhasm: z9 ^= y9
+# asm 1: pxor <y9=int6464#2,<z9=int6464#12
+# asm 2: pxor <y9=%xmm1,<z9=%xmm11
+pxor %xmm1,%xmm11
+
+# qhasm: uint32323232 r9 >>= 25
+# asm 1: psrld $25,<r9=int6464#3
+# asm 2: psrld $25,<r9=%xmm2
+psrld $25,%xmm2
+
+# qhasm: z9 ^= r9
+# asm 1: pxor <r9=int6464#3,<z9=int6464#12
+# asm 2: pxor <r9=%xmm2,<z9=%xmm11
+pxor %xmm2,%xmm11
+
+# qhasm: y8 = z0
+# asm 1: movdqa <z0=int6464#13,>y8=int6464#2
+# asm 2: movdqa <z0=%xmm12,>y8=%xmm1
+movdqa %xmm12,%xmm1
+
+# qhasm: uint32323232 y8 += z4
+# asm 1: paddd <z4=int6464#15,<y8=int6464#2
+# asm 2: paddd <z4=%xmm14,<y8=%xmm1
+paddd %xmm14,%xmm1
+
+# qhasm: r8 = y8
+# asm 1: movdqa <y8=int6464#2,>r8=int6464#3
+# asm 2: movdqa <y8=%xmm1,>r8=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y8 <<= 9
+# asm 1: pslld $9,<y8=int6464#2
+# asm 2: pslld $9,<y8=%xmm1
+pslld $9,%xmm1
+
+# qhasm: z8 ^= y8
+# asm 1: pxor <y8=int6464#2,<z8=int6464#16
+# asm 2: pxor <y8=%xmm1,<z8=%xmm15
+pxor %xmm1,%xmm15
+
+# qhasm: uint32323232 r8 >>= 23
+# asm 1: psrld $23,<r8=int6464#3
+# asm 2: psrld $23,<r8=%xmm2
+psrld $23,%xmm2
+
+# qhasm: z8 ^= r8
+# asm 1: pxor <r8=int6464#3,<z8=int6464#16
+# asm 2: pxor <r8=%xmm2,<z8=%xmm15
+pxor %xmm2,%xmm15
+
+# qhasm: y13 = z5
+# asm 1: movdqa <z5=int6464#1,>y13=int6464#2
+# asm 2: movdqa <z5=%xmm0,>y13=%xmm1
+movdqa %xmm0,%xmm1
+
+# qhasm: uint32323232 y13 += z9
+# asm 1: paddd <z9=int6464#12,<y13=int6464#2
+# asm 2: paddd <z9=%xmm11,<y13=%xmm1
+paddd %xmm11,%xmm1
+
+# qhasm: r13 = y13
+# asm 1: movdqa <y13=int6464#2,>r13=int6464#3
+# asm 2: movdqa <y13=%xmm1,>r13=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y13 <<= 9
+# asm 1: pslld $9,<y13=int6464#2
+# asm 2: pslld $9,<y13=%xmm1
+pslld $9,%xmm1
+
+# qhasm: z13 ^= y13
+# asm 1: pxor <y13=int6464#2,<z13=int6464#10
+# asm 2: pxor <y13=%xmm1,<z13=%xmm9
+pxor %xmm1,%xmm9
+
+# qhasm: uint32323232 r13 >>= 23
+# asm 1: psrld $23,<r13=int6464#3
+# asm 2: psrld $23,<r13=%xmm2
+psrld $23,%xmm2
+
+# qhasm: z13 ^= r13
+# asm 1: pxor <r13=int6464#3,<z13=int6464#10
+# asm 2: pxor <r13=%xmm2,<z13=%xmm9
+pxor %xmm2,%xmm9
+
+# qhasm: y12 = z4
+# asm 1: movdqa <z4=int6464#15,>y12=int6464#2
+# asm 2: movdqa <z4=%xmm14,>y12=%xmm1
+movdqa %xmm14,%xmm1
+
+# qhasm: uint32323232 y12 += z8
+# asm 1: paddd <z8=int6464#16,<y12=int6464#2
+# asm 2: paddd <z8=%xmm15,<y12=%xmm1
+paddd %xmm15,%xmm1
+
+# qhasm: r12 = y12
+# asm 1: movdqa <y12=int6464#2,>r12=int6464#3
+# asm 2: movdqa <y12=%xmm1,>r12=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y12 <<= 13
+# asm 1: pslld $13,<y12=int6464#2
+# asm 2: pslld $13,<y12=%xmm1
+pslld $13,%xmm1
+
+# qhasm: z12 ^= y12
+# asm 1: pxor <y12=int6464#2,<z12=int6464#14
+# asm 2: pxor <y12=%xmm1,<z12=%xmm13
+pxor %xmm1,%xmm13
+
+# qhasm: uint32323232 r12 >>= 19
+# asm 1: psrld $19,<r12=int6464#3
+# asm 2: psrld $19,<r12=%xmm2
+psrld $19,%xmm2
+
+# qhasm: z12 ^= r12
+# asm 1: pxor <r12=int6464#3,<z12=int6464#14
+# asm 2: pxor <r12=%xmm2,<z12=%xmm13
+pxor %xmm2,%xmm13
+
+# qhasm: y1 = z9
+# asm 1: movdqa <z9=int6464#12,>y1=int6464#2
+# asm 2: movdqa <z9=%xmm11,>y1=%xmm1
+movdqa %xmm11,%xmm1
+
+# qhasm: uint32323232 y1 += z13
+# asm 1: paddd <z13=int6464#10,<y1=int6464#2
+# asm 2: paddd <z13=%xmm9,<y1=%xmm1
+paddd %xmm9,%xmm1
+
+# qhasm: r1 = y1
+# asm 1: movdqa <y1=int6464#2,>r1=int6464#3
+# asm 2: movdqa <y1=%xmm1,>r1=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y1 <<= 13
+# asm 1: pslld $13,<y1=int6464#2
+# asm 2: pslld $13,<y1=%xmm1
+pslld $13,%xmm1
+
+# qhasm: z1 ^= y1
+# asm 1: pxor <y1=int6464#2,<z1=int6464#8
+# asm 2: pxor <y1=%xmm1,<z1=%xmm7
+pxor %xmm1,%xmm7
+
+# qhasm: uint32323232 r1 >>= 19
+# asm 1: psrld $19,<r1=int6464#3
+# asm 2: psrld $19,<r1=%xmm2
+psrld $19,%xmm2
+
+# qhasm: z1 ^= r1
+# asm 1: pxor <r1=int6464#3,<z1=int6464#8
+# asm 2: pxor <r1=%xmm2,<z1=%xmm7
+pxor %xmm2,%xmm7
+
+# qhasm: y0 = z8
+# asm 1: movdqa <z8=int6464#16,>y0=int6464#2
+# asm 2: movdqa <z8=%xmm15,>y0=%xmm1
+movdqa %xmm15,%xmm1
+
+# qhasm: uint32323232 y0 += z12
+# asm 1: paddd <z12=int6464#14,<y0=int6464#2
+# asm 2: paddd <z12=%xmm13,<y0=%xmm1
+paddd %xmm13,%xmm1
+
+# qhasm: r0 = y0
+# asm 1: movdqa <y0=int6464#2,>r0=int6464#3
+# asm 2: movdqa <y0=%xmm1,>r0=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y0 <<= 18
+# asm 1: pslld $18,<y0=int6464#2
+# asm 2: pslld $18,<y0=%xmm1
+pslld $18,%xmm1
+
+# qhasm: z0 ^= y0
+# asm 1: pxor <y0=int6464#2,<z0=int6464#13
+# asm 2: pxor <y0=%xmm1,<z0=%xmm12
+pxor %xmm1,%xmm12
+
+# qhasm: uint32323232 r0 >>= 14
+# asm 1: psrld $14,<r0=int6464#3
+# asm 2: psrld $14,<r0=%xmm2
+psrld $14,%xmm2
+
+# qhasm: z0 ^= r0
+# asm 1: pxor <r0=int6464#3,<z0=int6464#13
+# asm 2: pxor <r0=%xmm2,<z0=%xmm12
+pxor %xmm2,%xmm12
+
+# qhasm: z10 = z10_stack
+# asm 1: movdqa <z10_stack=stack128#21,>z10=int6464#2
+# asm 2: movdqa <z10_stack=320(%rsp),>z10=%xmm1
+movdqa 320(%rsp),%xmm1
+
+# qhasm: z0_stack = z0
+# asm 1: movdqa <z0=int6464#13,>z0_stack=stack128#21
+# asm 2: movdqa <z0=%xmm12,>z0_stack=320(%rsp)
+movdqa %xmm12,320(%rsp)
+
+# qhasm: y5 = z13
+# asm 1: movdqa <z13=int6464#10,>y5=int6464#3
+# asm 2: movdqa <z13=%xmm9,>y5=%xmm2
+movdqa %xmm9,%xmm2
+
+# qhasm: uint32323232 y5 += z1
+# asm 1: paddd <z1=int6464#8,<y5=int6464#3
+# asm 2: paddd <z1=%xmm7,<y5=%xmm2
+paddd %xmm7,%xmm2
+
+# qhasm: r5 = y5
+# asm 1: movdqa <y5=int6464#3,>r5=int6464#13
+# asm 2: movdqa <y5=%xmm2,>r5=%xmm12
+movdqa %xmm2,%xmm12
+
+# qhasm: uint32323232 y5 <<= 18
+# asm 1: pslld $18,<y5=int6464#3
+# asm 2: pslld $18,<y5=%xmm2
+pslld $18,%xmm2
+
+# qhasm: z5 ^= y5
+# asm 1: pxor <y5=int6464#3,<z5=int6464#1
+# asm 2: pxor <y5=%xmm2,<z5=%xmm0
+pxor %xmm2,%xmm0
+
+# qhasm: uint32323232 r5 >>= 14
+# asm 1: psrld $14,<r5=int6464#13
+# asm 2: psrld $14,<r5=%xmm12
+psrld $14,%xmm12
+
+# qhasm: z5 ^= r5
+# asm 1: pxor <r5=int6464#13,<z5=int6464#1
+# asm 2: pxor <r5=%xmm12,<z5=%xmm0
+pxor %xmm12,%xmm0
+
+# qhasm: y14 = z6
+# asm 1: movdqa <z6=int6464#6,>y14=int6464#3
+# asm 2: movdqa <z6=%xmm5,>y14=%xmm2
+movdqa %xmm5,%xmm2
+
+# qhasm: uint32323232 y14 += z10
+# asm 1: paddd <z10=int6464#2,<y14=int6464#3
+# asm 2: paddd <z10=%xmm1,<y14=%xmm2
+paddd %xmm1,%xmm2
+
+# qhasm: r14 = y14
+# asm 1: movdqa <y14=int6464#3,>r14=int6464#13
+# asm 2: movdqa <y14=%xmm2,>r14=%xmm12
+movdqa %xmm2,%xmm12
+
+# qhasm: uint32323232 y14 <<= 7
+# asm 1: pslld $7,<y14=int6464#3
+# asm 2: pslld $7,<y14=%xmm2
+pslld $7,%xmm2
+
+# qhasm: z14 ^= y14
+# asm 1: pxor <y14=int6464#3,<z14=int6464#4
+# asm 2: pxor <y14=%xmm2,<z14=%xmm3
+pxor %xmm2,%xmm3
+
+# qhasm: uint32323232 r14 >>= 25
+# asm 1: psrld $25,<r14=int6464#13
+# asm 2: psrld $25,<r14=%xmm12
+psrld $25,%xmm12
+
+# qhasm: z14 ^= r14
+# asm 1: pxor <r14=int6464#13,<z14=int6464#4
+# asm 2: pxor <r14=%xmm12,<z14=%xmm3
+pxor %xmm12,%xmm3
+
+# qhasm: z15 = z15_stack
+# asm 1: movdqa <z15_stack=stack128#22,>z15=int6464#3
+# asm 2: movdqa <z15_stack=336(%rsp),>z15=%xmm2
+movdqa 336(%rsp),%xmm2
+
+# qhasm: z5_stack = z5
+# asm 1: movdqa <z5=int6464#1,>z5_stack=stack128#22
+# asm 2: movdqa <z5=%xmm0,>z5_stack=336(%rsp)
+movdqa %xmm0,336(%rsp)
+
+# qhasm: y3 = z11
+# asm 1: movdqa <z11=int6464#7,>y3=int6464#1
+# asm 2: movdqa <z11=%xmm6,>y3=%xmm0
+movdqa %xmm6,%xmm0
+
+# qhasm: uint32323232 y3 += z15
+# asm 1: paddd <z15=int6464#3,<y3=int6464#1
+# asm 2: paddd <z15=%xmm2,<y3=%xmm0
+paddd %xmm2,%xmm0
+
+# qhasm: r3 = y3
+# asm 1: movdqa <y3=int6464#1,>r3=int6464#13
+# asm 2: movdqa <y3=%xmm0,>r3=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y3 <<= 7
+# asm 1: pslld $7,<y3=int6464#1
+# asm 2: pslld $7,<y3=%xmm0
+pslld $7,%xmm0
+
+# qhasm: z3 ^= y3
+# asm 1: pxor <y3=int6464#1,<z3=int6464#5
+# asm 2: pxor <y3=%xmm0,<z3=%xmm4
+pxor %xmm0,%xmm4
+
+# qhasm: uint32323232 r3 >>= 25
+# asm 1: psrld $25,<r3=int6464#13
+# asm 2: psrld $25,<r3=%xmm12
+psrld $25,%xmm12
+
+# qhasm: z3 ^= r3
+# asm 1: pxor <r3=int6464#13,<z3=int6464#5
+# asm 2: pxor <r3=%xmm12,<z3=%xmm4
+pxor %xmm12,%xmm4
+
+# qhasm: y2 = z10
+# asm 1: movdqa <z10=int6464#2,>y2=int6464#1
+# asm 2: movdqa <z10=%xmm1,>y2=%xmm0
+movdqa %xmm1,%xmm0
+
+# qhasm: uint32323232 y2 += z14
+# asm 1: paddd <z14=int6464#4,<y2=int6464#1
+# asm 2: paddd <z14=%xmm3,<y2=%xmm0
+paddd %xmm3,%xmm0
+
+# qhasm: r2 = y2
+# asm 1: movdqa <y2=int6464#1,>r2=int6464#13
+# asm 2: movdqa <y2=%xmm0,>r2=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y2 <<= 9
+# asm 1: pslld $9,<y2=int6464#1
+# asm 2: pslld $9,<y2=%xmm0
+pslld $9,%xmm0
+
+# qhasm: z2 ^= y2
+# asm 1: pxor <y2=int6464#1,<z2=int6464#11
+# asm 2: pxor <y2=%xmm0,<z2=%xmm10
+pxor %xmm0,%xmm10
+
+# qhasm: uint32323232 r2 >>= 23
+# asm 1: psrld $23,<r2=int6464#13
+# asm 2: psrld $23,<r2=%xmm12
+psrld $23,%xmm12
+
+# qhasm: z2 ^= r2
+# asm 1: pxor <r2=int6464#13,<z2=int6464#11
+# asm 2: pxor <r2=%xmm12,<z2=%xmm10
+pxor %xmm12,%xmm10
+
+# qhasm: y7 = z15
+# asm 1: movdqa <z15=int6464#3,>y7=int6464#1
+# asm 2: movdqa <z15=%xmm2,>y7=%xmm0
+movdqa %xmm2,%xmm0
+
+# qhasm: uint32323232 y7 += z3
+# asm 1: paddd <z3=int6464#5,<y7=int6464#1
+# asm 2: paddd <z3=%xmm4,<y7=%xmm0
+paddd %xmm4,%xmm0
+
+# qhasm: r7 = y7
+# asm 1: movdqa <y7=int6464#1,>r7=int6464#13
+# asm 2: movdqa <y7=%xmm0,>r7=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y7 <<= 9
+# asm 1: pslld $9,<y7=int6464#1
+# asm 2: pslld $9,<y7=%xmm0
+pslld $9,%xmm0
+
+# qhasm: z7 ^= y7
+# asm 1: pxor <y7=int6464#1,<z7=int6464#9
+# asm 2: pxor <y7=%xmm0,<z7=%xmm8
+pxor %xmm0,%xmm8
+
+# qhasm: uint32323232 r7 >>= 23
+# asm 1: psrld $23,<r7=int6464#13
+# asm 2: psrld $23,<r7=%xmm12
+psrld $23,%xmm12
+
+# qhasm: z7 ^= r7
+# asm 1: pxor <r7=int6464#13,<z7=int6464#9
+# asm 2: pxor <r7=%xmm12,<z7=%xmm8
+pxor %xmm12,%xmm8
+
+# qhasm: y6 = z14
+# asm 1: movdqa <z14=int6464#4,>y6=int6464#1
+# asm 2: movdqa <z14=%xmm3,>y6=%xmm0
+movdqa %xmm3,%xmm0
+
+# qhasm: uint32323232 y6 += z2
+# asm 1: paddd <z2=int6464#11,<y6=int6464#1
+# asm 2: paddd <z2=%xmm10,<y6=%xmm0
+paddd %xmm10,%xmm0
+
+# qhasm: r6 = y6
+# asm 1: movdqa <y6=int6464#1,>r6=int6464#13
+# asm 2: movdqa <y6=%xmm0,>r6=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y6 <<= 13
+# asm 1: pslld $13,<y6=int6464#1
+# asm 2: pslld $13,<y6=%xmm0
+pslld $13,%xmm0
+
+# qhasm: z6 ^= y6
+# asm 1: pxor <y6=int6464#1,<z6=int6464#6
+# asm 2: pxor <y6=%xmm0,<z6=%xmm5
+pxor %xmm0,%xmm5
+
+# qhasm: uint32323232 r6 >>= 19
+# asm 1: psrld $19,<r6=int6464#13
+# asm 2: psrld $19,<r6=%xmm12
+psrld $19,%xmm12
+
+# qhasm: z6 ^= r6
+# asm 1: pxor <r6=int6464#13,<z6=int6464#6
+# asm 2: pxor <r6=%xmm12,<z6=%xmm5
+pxor %xmm12,%xmm5
+
+# qhasm: y11 = z3
+# asm 1: movdqa <z3=int6464#5,>y11=int6464#1
+# asm 2: movdqa <z3=%xmm4,>y11=%xmm0
+movdqa %xmm4,%xmm0
+
+# qhasm: uint32323232 y11 += z7
+# asm 1: paddd <z7=int6464#9,<y11=int6464#1
+# asm 2: paddd <z7=%xmm8,<y11=%xmm0
+paddd %xmm8,%xmm0
+
+# qhasm: r11 = y11
+# asm 1: movdqa <y11=int6464#1,>r11=int6464#13
+# asm 2: movdqa <y11=%xmm0,>r11=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y11 <<= 13
+# asm 1: pslld $13,<y11=int6464#1
+# asm 2: pslld $13,<y11=%xmm0
+pslld $13,%xmm0
+
+# qhasm: z11 ^= y11
+# asm 1: pxor <y11=int6464#1,<z11=int6464#7
+# asm 2: pxor <y11=%xmm0,<z11=%xmm6
+pxor %xmm0,%xmm6
+
+# qhasm: uint32323232 r11 >>= 19
+# asm 1: psrld $19,<r11=int6464#13
+# asm 2: psrld $19,<r11=%xmm12
+psrld $19,%xmm12
+
+# qhasm: z11 ^= r11
+# asm 1: pxor <r11=int6464#13,<z11=int6464#7
+# asm 2: pxor <r11=%xmm12,<z11=%xmm6
+pxor %xmm12,%xmm6
+
+# qhasm: y10 = z2
+# asm 1: movdqa <z2=int6464#11,>y10=int6464#1
+# asm 2: movdqa <z2=%xmm10,>y10=%xmm0
+movdqa %xmm10,%xmm0
+
+# qhasm: uint32323232 y10 += z6
+# asm 1: paddd <z6=int6464#6,<y10=int6464#1
+# asm 2: paddd <z6=%xmm5,<y10=%xmm0
+paddd %xmm5,%xmm0
+
+# qhasm: r10 = y10
+# asm 1: movdqa <y10=int6464#1,>r10=int6464#13
+# asm 2: movdqa <y10=%xmm0,>r10=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y10 <<= 18
+# asm 1: pslld $18,<y10=int6464#1
+# asm 2: pslld $18,<y10=%xmm0
+pslld $18,%xmm0
+
+# qhasm: z10 ^= y10
+# asm 1: pxor <y10=int6464#1,<z10=int6464#2
+# asm 2: pxor <y10=%xmm0,<z10=%xmm1
+pxor %xmm0,%xmm1
+
+# qhasm: uint32323232 r10 >>= 14
+# asm 1: psrld $14,<r10=int6464#13
+# asm 2: psrld $14,<r10=%xmm12
+psrld $14,%xmm12
+
+# qhasm: z10 ^= r10
+# asm 1: pxor <r10=int6464#13,<z10=int6464#2
+# asm 2: pxor <r10=%xmm12,<z10=%xmm1
+pxor %xmm12,%xmm1
+
+# qhasm: z0 = z0_stack
+# asm 1: movdqa <z0_stack=stack128#21,>z0=int6464#1
+# asm 2: movdqa <z0_stack=320(%rsp),>z0=%xmm0
+movdqa 320(%rsp),%xmm0
+
+# qhasm: z10_stack = z10
+# asm 1: movdqa <z10=int6464#2,>z10_stack=stack128#21
+# asm 2: movdqa <z10=%xmm1,>z10_stack=320(%rsp)
+movdqa %xmm1,320(%rsp)
+
+# qhasm: y1 = z3
+# asm 1: movdqa <z3=int6464#5,>y1=int6464#2
+# asm 2: movdqa <z3=%xmm4,>y1=%xmm1
+movdqa %xmm4,%xmm1
+
+# qhasm: uint32323232 y1 += z0
+# asm 1: paddd <z0=int6464#1,<y1=int6464#2
+# asm 2: paddd <z0=%xmm0,<y1=%xmm1
+paddd %xmm0,%xmm1
+
+# qhasm: r1 = y1
+# asm 1: movdqa <y1=int6464#2,>r1=int6464#13
+# asm 2: movdqa <y1=%xmm1,>r1=%xmm12
+movdqa %xmm1,%xmm12
+
+# qhasm: uint32323232 y1 <<= 7
+# asm 1: pslld $7,<y1=int6464#2
+# asm 2: pslld $7,<y1=%xmm1
+pslld $7,%xmm1
+
+# qhasm: z1 ^= y1
+# asm 1: pxor <y1=int6464#2,<z1=int6464#8
+# asm 2: pxor <y1=%xmm1,<z1=%xmm7
+pxor %xmm1,%xmm7
+
+# qhasm: uint32323232 r1 >>= 25
+# asm 1: psrld $25,<r1=int6464#13
+# asm 2: psrld $25,<r1=%xmm12
+psrld $25,%xmm12
+
+# qhasm: z1 ^= r1
+# asm 1: pxor <r1=int6464#13,<z1=int6464#8
+# asm 2: pxor <r1=%xmm12,<z1=%xmm7
+pxor %xmm12,%xmm7
+
+# qhasm: y15 = z7
+# asm 1: movdqa <z7=int6464#9,>y15=int6464#2
+# asm 2: movdqa <z7=%xmm8,>y15=%xmm1
+movdqa %xmm8,%xmm1
+
+# qhasm: uint32323232 y15 += z11
+# asm 1: paddd <z11=int6464#7,<y15=int6464#2
+# asm 2: paddd <z11=%xmm6,<y15=%xmm1
+paddd %xmm6,%xmm1
+
+# qhasm: r15 = y15
+# asm 1: movdqa <y15=int6464#2,>r15=int6464#13
+# asm 2: movdqa <y15=%xmm1,>r15=%xmm12
+movdqa %xmm1,%xmm12
+
+# qhasm: uint32323232 y15 <<= 18
+# asm 1: pslld $18,<y15=int6464#2
+# asm 2: pslld $18,<y15=%xmm1
+pslld $18,%xmm1
+
+# qhasm: z15 ^= y15
+# asm 1: pxor <y15=int6464#2,<z15=int6464#3
+# asm 2: pxor <y15=%xmm1,<z15=%xmm2
+pxor %xmm1,%xmm2
+
+# qhasm: uint32323232 r15 >>= 14
+# asm 1: psrld $14,<r15=int6464#13
+# asm 2: psrld $14,<r15=%xmm12
+psrld $14,%xmm12
+
+# qhasm: z15 ^= r15
+# asm 1: pxor <r15=int6464#13,<z15=int6464#3
+# asm 2: pxor <r15=%xmm12,<z15=%xmm2
+pxor %xmm12,%xmm2
+
+# qhasm: z5 = z5_stack
+# asm 1: movdqa <z5_stack=stack128#22,>z5=int6464#13
+# asm 2: movdqa <z5_stack=336(%rsp),>z5=%xmm12
+movdqa 336(%rsp),%xmm12
+
+# qhasm: z15_stack = z15
+# asm 1: movdqa <z15=int6464#3,>z15_stack=stack128#22
+# asm 2: movdqa <z15=%xmm2,>z15_stack=336(%rsp)
+movdqa %xmm2,336(%rsp)
+
+# qhasm: y6 = z4
+# asm 1: movdqa <z4=int6464#15,>y6=int6464#2
+# asm 2: movdqa <z4=%xmm14,>y6=%xmm1
+movdqa %xmm14,%xmm1
+
+# qhasm: uint32323232 y6 += z5
+# asm 1: paddd <z5=int6464#13,<y6=int6464#2
+# asm 2: paddd <z5=%xmm12,<y6=%xmm1
+paddd %xmm12,%xmm1
+
+# qhasm: r6 = y6
+# asm 1: movdqa <y6=int6464#2,>r6=int6464#3
+# asm 2: movdqa <y6=%xmm1,>r6=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y6 <<= 7
+# asm 1: pslld $7,<y6=int6464#2
+# asm 2: pslld $7,<y6=%xmm1
+pslld $7,%xmm1
+
+# qhasm: z6 ^= y6
+# asm 1: pxor <y6=int6464#2,<z6=int6464#6
+# asm 2: pxor <y6=%xmm1,<z6=%xmm5
+pxor %xmm1,%xmm5
+
+# qhasm: uint32323232 r6 >>= 25
+# asm 1: psrld $25,<r6=int6464#3
+# asm 2: psrld $25,<r6=%xmm2
+psrld $25,%xmm2
+
+# qhasm: z6 ^= r6
+# asm 1: pxor <r6=int6464#3,<z6=int6464#6
+# asm 2: pxor <r6=%xmm2,<z6=%xmm5
+pxor %xmm2,%xmm5
+
+# qhasm: y2 = z0
+# asm 1: movdqa <z0=int6464#1,>y2=int6464#2
+# asm 2: movdqa <z0=%xmm0,>y2=%xmm1
+movdqa %xmm0,%xmm1
+
+# qhasm: uint32323232 y2 += z1
+# asm 1: paddd <z1=int6464#8,<y2=int6464#2
+# asm 2: paddd <z1=%xmm7,<y2=%xmm1
+paddd %xmm7,%xmm1
+
+# qhasm: r2 = y2
+# asm 1: movdqa <y2=int6464#2,>r2=int6464#3
+# asm 2: movdqa <y2=%xmm1,>r2=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y2 <<= 9
+# asm 1: pslld $9,<y2=int6464#2
+# asm 2: pslld $9,<y2=%xmm1
+pslld $9,%xmm1
+
+# qhasm: z2 ^= y2
+# asm 1: pxor <y2=int6464#2,<z2=int6464#11
+# asm 2: pxor <y2=%xmm1,<z2=%xmm10
+pxor %xmm1,%xmm10
+
+# qhasm: uint32323232 r2 >>= 23
+# asm 1: psrld $23,<r2=int6464#3
+# asm 2: psrld $23,<r2=%xmm2
+psrld $23,%xmm2
+
+# qhasm: z2 ^= r2
+# asm 1: pxor <r2=int6464#3,<z2=int6464#11
+# asm 2: pxor <r2=%xmm2,<z2=%xmm10
+pxor %xmm2,%xmm10
+
+# qhasm: y7 = z5
+# asm 1: movdqa <z5=int6464#13,>y7=int6464#2
+# asm 2: movdqa <z5=%xmm12,>y7=%xmm1
+movdqa %xmm12,%xmm1
+
+# qhasm: uint32323232 y7 += z6
+# asm 1: paddd <z6=int6464#6,<y7=int6464#2
+# asm 2: paddd <z6=%xmm5,<y7=%xmm1
+paddd %xmm5,%xmm1
+
+# qhasm: r7 = y7
+# asm 1: movdqa <y7=int6464#2,>r7=int6464#3
+# asm 2: movdqa <y7=%xmm1,>r7=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y7 <<= 9
+# asm 1: pslld $9,<y7=int6464#2
+# asm 2: pslld $9,<y7=%xmm1
+pslld $9,%xmm1
+
+# qhasm: z7 ^= y7
+# asm 1: pxor <y7=int6464#2,<z7=int6464#9
+# asm 2: pxor <y7=%xmm1,<z7=%xmm8
+pxor %xmm1,%xmm8
+
+# qhasm: uint32323232 r7 >>= 23
+# asm 1: psrld $23,<r7=int6464#3
+# asm 2: psrld $23,<r7=%xmm2
+psrld $23,%xmm2
+
+# qhasm: z7 ^= r7
+# asm 1: pxor <r7=int6464#3,<z7=int6464#9
+# asm 2: pxor <r7=%xmm2,<z7=%xmm8
+pxor %xmm2,%xmm8
+
+# qhasm: y3 = z1
+# asm 1: movdqa <z1=int6464#8,>y3=int6464#2
+# asm 2: movdqa <z1=%xmm7,>y3=%xmm1
+movdqa %xmm7,%xmm1
+
+# qhasm: uint32323232 y3 += z2
+# asm 1: paddd <z2=int6464#11,<y3=int6464#2
+# asm 2: paddd <z2=%xmm10,<y3=%xmm1
+paddd %xmm10,%xmm1
+
+# qhasm: r3 = y3
+# asm 1: movdqa <y3=int6464#2,>r3=int6464#3
+# asm 2: movdqa <y3=%xmm1,>r3=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y3 <<= 13
+# asm 1: pslld $13,<y3=int6464#2
+# asm 2: pslld $13,<y3=%xmm1
+pslld $13,%xmm1
+
+# qhasm: z3 ^= y3
+# asm 1: pxor <y3=int6464#2,<z3=int6464#5
+# asm 2: pxor <y3=%xmm1,<z3=%xmm4
+pxor %xmm1,%xmm4
+
+# qhasm: uint32323232 r3 >>= 19
+# asm 1: psrld $19,<r3=int6464#3
+# asm 2: psrld $19,<r3=%xmm2
+psrld $19,%xmm2
+
+# qhasm: z3 ^= r3
+# asm 1: pxor <r3=int6464#3,<z3=int6464#5
+# asm 2: pxor <r3=%xmm2,<z3=%xmm4
+pxor %xmm2,%xmm4
+
+# qhasm: y4 = z6
+# asm 1: movdqa <z6=int6464#6,>y4=int6464#2
+# asm 2: movdqa <z6=%xmm5,>y4=%xmm1
+movdqa %xmm5,%xmm1
+
+# qhasm: uint32323232 y4 += z7
+# asm 1: paddd <z7=int6464#9,<y4=int6464#2
+# asm 2: paddd <z7=%xmm8,<y4=%xmm1
+paddd %xmm8,%xmm1
+
+# qhasm: r4 = y4
+# asm 1: movdqa <y4=int6464#2,>r4=int6464#3
+# asm 2: movdqa <y4=%xmm1,>r4=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y4 <<= 13
+# asm 1: pslld $13,<y4=int6464#2
+# asm 2: pslld $13,<y4=%xmm1
+pslld $13,%xmm1
+
+# qhasm: z4 ^= y4
+# asm 1: pxor <y4=int6464#2,<z4=int6464#15
+# asm 2: pxor <y4=%xmm1,<z4=%xmm14
+pxor %xmm1,%xmm14
+
+# qhasm: uint32323232 r4 >>= 19
+# asm 1: psrld $19,<r4=int6464#3
+# asm 2: psrld $19,<r4=%xmm2
+psrld $19,%xmm2
+
+# qhasm: z4 ^= r4
+# asm 1: pxor <r4=int6464#3,<z4=int6464#15
+# asm 2: pxor <r4=%xmm2,<z4=%xmm14
+pxor %xmm2,%xmm14
+
+# qhasm: y0 = z2
+# asm 1: movdqa <z2=int6464#11,>y0=int6464#2
+# asm 2: movdqa <z2=%xmm10,>y0=%xmm1
+movdqa %xmm10,%xmm1
+
+# qhasm: uint32323232 y0 += z3
+# asm 1: paddd <z3=int6464#5,<y0=int6464#2
+# asm 2: paddd <z3=%xmm4,<y0=%xmm1
+paddd %xmm4,%xmm1
+
+# qhasm: r0 = y0
+# asm 1: movdqa <y0=int6464#2,>r0=int6464#3
+# asm 2: movdqa <y0=%xmm1,>r0=%xmm2
+movdqa %xmm1,%xmm2
+
+# qhasm: uint32323232 y0 <<= 18
+# asm 1: pslld $18,<y0=int6464#2
+# asm 2: pslld $18,<y0=%xmm1
+pslld $18,%xmm1
+
+# qhasm: z0 ^= y0
+# asm 1: pxor <y0=int6464#2,<z0=int6464#1
+# asm 2: pxor <y0=%xmm1,<z0=%xmm0
+pxor %xmm1,%xmm0
+
+# qhasm: uint32323232 r0 >>= 14
+# asm 1: psrld $14,<r0=int6464#3
+# asm 2: psrld $14,<r0=%xmm2
+psrld $14,%xmm2
+
+# qhasm: z0 ^= r0
+# asm 1: pxor <r0=int6464#3,<z0=int6464#1
+# asm 2: pxor <r0=%xmm2,<z0=%xmm0
+pxor %xmm2,%xmm0
+
+# qhasm: z10 = z10_stack
+# asm 1: movdqa <z10_stack=stack128#21,>z10=int6464#2
+# asm 2: movdqa <z10_stack=320(%rsp),>z10=%xmm1
+movdqa 320(%rsp),%xmm1
+
+# qhasm: z0_stack = z0
+# asm 1: movdqa <z0=int6464#1,>z0_stack=stack128#21
+# asm 2: movdqa <z0=%xmm0,>z0_stack=320(%rsp)
+movdqa %xmm0,320(%rsp)
+
+# qhasm: y5 = z7
+# asm 1: movdqa <z7=int6464#9,>y5=int6464#1
+# asm 2: movdqa <z7=%xmm8,>y5=%xmm0
+movdqa %xmm8,%xmm0
+
+# qhasm: uint32323232 y5 += z4
+# asm 1: paddd <z4=int6464#15,<y5=int6464#1
+# asm 2: paddd <z4=%xmm14,<y5=%xmm0
+paddd %xmm14,%xmm0
+
+# qhasm: r5 = y5
+# asm 1: movdqa <y5=int6464#1,>r5=int6464#3
+# asm 2: movdqa <y5=%xmm0,>r5=%xmm2
+movdqa %xmm0,%xmm2
+
+# qhasm: uint32323232 y5 <<= 18
+# asm 1: pslld $18,<y5=int6464#1
+# asm 2: pslld $18,<y5=%xmm0
+pslld $18,%xmm0
+
+# qhasm: z5 ^= y5
+# asm 1: pxor <y5=int6464#1,<z5=int6464#13
+# asm 2: pxor <y5=%xmm0,<z5=%xmm12
+pxor %xmm0,%xmm12
+
+# qhasm: uint32323232 r5 >>= 14
+# asm 1: psrld $14,<r5=int6464#3
+# asm 2: psrld $14,<r5=%xmm2
+psrld $14,%xmm2
+
+# qhasm: z5 ^= r5
+# asm 1: pxor <r5=int6464#3,<z5=int6464#13
+# asm 2: pxor <r5=%xmm2,<z5=%xmm12
+pxor %xmm2,%xmm12
+
+# qhasm: y11 = z9
+# asm 1: movdqa <z9=int6464#12,>y11=int6464#1
+# asm 2: movdqa <z9=%xmm11,>y11=%xmm0
+movdqa %xmm11,%xmm0
+
+# qhasm: uint32323232 y11 += z10
+# asm 1: paddd <z10=int6464#2,<y11=int6464#1
+# asm 2: paddd <z10=%xmm1,<y11=%xmm0
+paddd %xmm1,%xmm0
+
+# qhasm: r11 = y11
+# asm 1: movdqa <y11=int6464#1,>r11=int6464#3
+# asm 2: movdqa <y11=%xmm0,>r11=%xmm2
+movdqa %xmm0,%xmm2
+
+# qhasm: uint32323232 y11 <<= 7
+# asm 1: pslld $7,<y11=int6464#1
+# asm 2: pslld $7,<y11=%xmm0
+pslld $7,%xmm0
+
+# qhasm: z11 ^= y11
+# asm 1: pxor <y11=int6464#1,<z11=int6464#7
+# asm 2: pxor <y11=%xmm0,<z11=%xmm6
+pxor %xmm0,%xmm6
+
+# qhasm: uint32323232 r11 >>= 25
+# asm 1: psrld $25,<r11=int6464#3
+# asm 2: psrld $25,<r11=%xmm2
+psrld $25,%xmm2
+
+# qhasm: z11 ^= r11
+# asm 1: pxor <r11=int6464#3,<z11=int6464#7
+# asm 2: pxor <r11=%xmm2,<z11=%xmm6
+pxor %xmm2,%xmm6
+
+# qhasm: z15 = z15_stack
+# asm 1: movdqa <z15_stack=stack128#22,>z15=int6464#3
+# asm 2: movdqa <z15_stack=336(%rsp),>z15=%xmm2
+movdqa 336(%rsp),%xmm2
+
+# qhasm: z5_stack = z5
+# asm 1: movdqa <z5=int6464#13,>z5_stack=stack128#22
+# asm 2: movdqa <z5=%xmm12,>z5_stack=336(%rsp)
+movdqa %xmm12,336(%rsp)
+
+# qhasm: y12 = z14
+# asm 1: movdqa <z14=int6464#4,>y12=int6464#1
+# asm 2: movdqa <z14=%xmm3,>y12=%xmm0
+movdqa %xmm3,%xmm0
+
+# qhasm: uint32323232 y12 += z15
+# asm 1: paddd <z15=int6464#3,<y12=int6464#1
+# asm 2: paddd <z15=%xmm2,<y12=%xmm0
+paddd %xmm2,%xmm0
+
+# qhasm: r12 = y12
+# asm 1: movdqa <y12=int6464#1,>r12=int6464#13
+# asm 2: movdqa <y12=%xmm0,>r12=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y12 <<= 7
+# asm 1: pslld $7,<y12=int6464#1
+# asm 2: pslld $7,<y12=%xmm0
+pslld $7,%xmm0
+
+# qhasm: z12 ^= y12
+# asm 1: pxor <y12=int6464#1,<z12=int6464#14
+# asm 2: pxor <y12=%xmm0,<z12=%xmm13
+pxor %xmm0,%xmm13
+
+# qhasm: uint32323232 r12 >>= 25
+# asm 1: psrld $25,<r12=int6464#13
+# asm 2: psrld $25,<r12=%xmm12
+psrld $25,%xmm12
+
+# qhasm: z12 ^= r12
+# asm 1: pxor <r12=int6464#13,<z12=int6464#14
+# asm 2: pxor <r12=%xmm12,<z12=%xmm13
+pxor %xmm12,%xmm13
+
+# qhasm: y8 = z10
+# asm 1: movdqa <z10=int6464#2,>y8=int6464#1
+# asm 2: movdqa <z10=%xmm1,>y8=%xmm0
+movdqa %xmm1,%xmm0
+
+# qhasm: uint32323232 y8 += z11
+# asm 1: paddd <z11=int6464#7,<y8=int6464#1
+# asm 2: paddd <z11=%xmm6,<y8=%xmm0
+paddd %xmm6,%xmm0
+
+# qhasm: r8 = y8
+# asm 1: movdqa <y8=int6464#1,>r8=int6464#13
+# asm 2: movdqa <y8=%xmm0,>r8=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y8 <<= 9
+# asm 1: pslld $9,<y8=int6464#1
+# asm 2: pslld $9,<y8=%xmm0
+pslld $9,%xmm0
+
+# qhasm: z8 ^= y8
+# asm 1: pxor <y8=int6464#1,<z8=int6464#16
+# asm 2: pxor <y8=%xmm0,<z8=%xmm15
+pxor %xmm0,%xmm15
+
+# qhasm: uint32323232 r8 >>= 23
+# asm 1: psrld $23,<r8=int6464#13
+# asm 2: psrld $23,<r8=%xmm12
+psrld $23,%xmm12
+
+# qhasm: z8 ^= r8
+# asm 1: pxor <r8=int6464#13,<z8=int6464#16
+# asm 2: pxor <r8=%xmm12,<z8=%xmm15
+pxor %xmm12,%xmm15
+
+# qhasm: y13 = z15
+# asm 1: movdqa <z15=int6464#3,>y13=int6464#1
+# asm 2: movdqa <z15=%xmm2,>y13=%xmm0
+movdqa %xmm2,%xmm0
+
+# qhasm: uint32323232 y13 += z12
+# asm 1: paddd <z12=int6464#14,<y13=int6464#1
+# asm 2: paddd <z12=%xmm13,<y13=%xmm0
+paddd %xmm13,%xmm0
+
+# qhasm: r13 = y13
+# asm 1: movdqa <y13=int6464#1,>r13=int6464#13
+# asm 2: movdqa <y13=%xmm0,>r13=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y13 <<= 9
+# asm 1: pslld $9,<y13=int6464#1
+# asm 2: pslld $9,<y13=%xmm0
+pslld $9,%xmm0
+
+# qhasm: z13 ^= y13
+# asm 1: pxor <y13=int6464#1,<z13=int6464#10
+# asm 2: pxor <y13=%xmm0,<z13=%xmm9
+pxor %xmm0,%xmm9
+
+# qhasm: uint32323232 r13 >>= 23
+# asm 1: psrld $23,<r13=int6464#13
+# asm 2: psrld $23,<r13=%xmm12
+psrld $23,%xmm12
+
+# qhasm: z13 ^= r13
+# asm 1: pxor <r13=int6464#13,<z13=int6464#10
+# asm 2: pxor <r13=%xmm12,<z13=%xmm9
+pxor %xmm12,%xmm9
+
+# qhasm: y9 = z11
+# asm 1: movdqa <z11=int6464#7,>y9=int6464#1
+# asm 2: movdqa <z11=%xmm6,>y9=%xmm0
+movdqa %xmm6,%xmm0
+
+# qhasm: uint32323232 y9 += z8
+# asm 1: paddd <z8=int6464#16,<y9=int6464#1
+# asm 2: paddd <z8=%xmm15,<y9=%xmm0
+paddd %xmm15,%xmm0
+
+# qhasm: r9 = y9
+# asm 1: movdqa <y9=int6464#1,>r9=int6464#13
+# asm 2: movdqa <y9=%xmm0,>r9=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y9 <<= 13
+# asm 1: pslld $13,<y9=int6464#1
+# asm 2: pslld $13,<y9=%xmm0
+pslld $13,%xmm0
+
+# qhasm: z9 ^= y9
+# asm 1: pxor <y9=int6464#1,<z9=int6464#12
+# asm 2: pxor <y9=%xmm0,<z9=%xmm11
+pxor %xmm0,%xmm11
+
+# qhasm: uint32323232 r9 >>= 19
+# asm 1: psrld $19,<r9=int6464#13
+# asm 2: psrld $19,<r9=%xmm12
+psrld $19,%xmm12
+
+# qhasm: z9 ^= r9
+# asm 1: pxor <r9=int6464#13,<z9=int6464#12
+# asm 2: pxor <r9=%xmm12,<z9=%xmm11
+pxor %xmm12,%xmm11
+
+# qhasm: y14 = z12
+# asm 1: movdqa <z12=int6464#14,>y14=int6464#1
+# asm 2: movdqa <z12=%xmm13,>y14=%xmm0
+movdqa %xmm13,%xmm0
+
+# qhasm: uint32323232 y14 += z13
+# asm 1: paddd <z13=int6464#10,<y14=int6464#1
+# asm 2: paddd <z13=%xmm9,<y14=%xmm0
+paddd %xmm9,%xmm0
+
+# qhasm: r14 = y14
+# asm 1: movdqa <y14=int6464#1,>r14=int6464#13
+# asm 2: movdqa <y14=%xmm0,>r14=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y14 <<= 13
+# asm 1: pslld $13,<y14=int6464#1
+# asm 2: pslld $13,<y14=%xmm0
+pslld $13,%xmm0
+
+# qhasm: z14 ^= y14
+# asm 1: pxor <y14=int6464#1,<z14=int6464#4
+# asm 2: pxor <y14=%xmm0,<z14=%xmm3
+pxor %xmm0,%xmm3
+
+# qhasm: uint32323232 r14 >>= 19
+# asm 1: psrld $19,<r14=int6464#13
+# asm 2: psrld $19,<r14=%xmm12
+psrld $19,%xmm12
+
+# qhasm: z14 ^= r14
+# asm 1: pxor <r14=int6464#13,<z14=int6464#4
+# asm 2: pxor <r14=%xmm12,<z14=%xmm3
+pxor %xmm12,%xmm3
+
+# qhasm: y10 = z8
+# asm 1: movdqa <z8=int6464#16,>y10=int6464#1
+# asm 2: movdqa <z8=%xmm15,>y10=%xmm0
+movdqa %xmm15,%xmm0
+
+# qhasm: uint32323232 y10 += z9
+# asm 1: paddd <z9=int6464#12,<y10=int6464#1
+# asm 2: paddd <z9=%xmm11,<y10=%xmm0
+paddd %xmm11,%xmm0
+
+# qhasm: r10 = y10
+# asm 1: movdqa <y10=int6464#1,>r10=int6464#13
+# asm 2: movdqa <y10=%xmm0,>r10=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y10 <<= 18
+# asm 1: pslld $18,<y10=int6464#1
+# asm 2: pslld $18,<y10=%xmm0
+pslld $18,%xmm0
+
+# qhasm: z10 ^= y10
+# asm 1: pxor <y10=int6464#1,<z10=int6464#2
+# asm 2: pxor <y10=%xmm0,<z10=%xmm1
+pxor %xmm0,%xmm1
+
+# qhasm: uint32323232 r10 >>= 14
+# asm 1: psrld $14,<r10=int6464#13
+# asm 2: psrld $14,<r10=%xmm12
+psrld $14,%xmm12
+
+# qhasm: z10 ^= r10
+# asm 1: pxor <r10=int6464#13,<z10=int6464#2
+# asm 2: pxor <r10=%xmm12,<z10=%xmm1
+pxor %xmm12,%xmm1
+
+# qhasm: y15 = z13
+# asm 1: movdqa <z13=int6464#10,>y15=int6464#1
+# asm 2: movdqa <z13=%xmm9,>y15=%xmm0
+movdqa %xmm9,%xmm0
+
+# qhasm: uint32323232 y15 += z14
+# asm 1: paddd <z14=int6464#4,<y15=int6464#1
+# asm 2: paddd <z14=%xmm3,<y15=%xmm0
+paddd %xmm3,%xmm0
+
+# qhasm: r15 = y15
+# asm 1: movdqa <y15=int6464#1,>r15=int6464#13
+# asm 2: movdqa <y15=%xmm0,>r15=%xmm12
+movdqa %xmm0,%xmm12
+
+# qhasm: uint32323232 y15 <<= 18
+# asm 1: pslld $18,<y15=int6464#1
+# asm 2: pslld $18,<y15=%xmm0
+pslld $18,%xmm0
+
+# qhasm: z15 ^= y15
+# asm 1: pxor <y15=int6464#1,<z15=int6464#3
+# asm 2: pxor <y15=%xmm0,<z15=%xmm2
+pxor %xmm0,%xmm2
+
+# qhasm: uint32323232 r15 >>= 14
+# asm 1: psrld $14,<r15=int6464#13
+# asm 2: psrld $14,<r15=%xmm12
+psrld $14,%xmm12
+
+# qhasm: z15 ^= r15
+# asm 1: pxor <r15=int6464#13,<z15=int6464#3
+# asm 2: pxor <r15=%xmm12,<z15=%xmm2
+pxor %xmm12,%xmm2
+
+# qhasm: z0 = z0_stack
+# asm 1: movdqa <z0_stack=stack128#21,>z0=int6464#13
+# asm 2: movdqa <z0_stack=320(%rsp),>z0=%xmm12
+movdqa 320(%rsp),%xmm12
+
+# qhasm: z5 = z5_stack
+# asm 1: movdqa <z5_stack=stack128#22,>z5=int6464#1
+# asm 2: movdqa <z5_stack=336(%rsp),>z5=%xmm0
+movdqa 336(%rsp),%xmm0
+
+# qhasm: unsigned>? i -= 2
+# asm 1: sub $2,<i=int64#3
+# asm 2: sub $2,<i=%rdx
+sub $2,%rdx
+# comment:fp stack unchanged by jump
+
+# qhasm: goto mainloop1 if unsigned>
+ja ._mainloop1
+
+# qhasm: uint32323232 z0 += orig0
+# asm 1: paddd <orig0=stack128#8,<z0=int6464#13
+# asm 2: paddd <orig0=112(%rsp),<z0=%xmm12
+paddd 112(%rsp),%xmm12
+
+# qhasm: uint32323232 z1 += orig1
+# asm 1: paddd <orig1=stack128#12,<z1=int6464#8
+# asm 2: paddd <orig1=176(%rsp),<z1=%xmm7
+paddd 176(%rsp),%xmm7
+
+# qhasm: uint32323232 z2 += orig2
+# asm 1: paddd <orig2=stack128#15,<z2=int6464#11
+# asm 2: paddd <orig2=224(%rsp),<z2=%xmm10
+paddd 224(%rsp),%xmm10
+
+# qhasm: uint32323232 z3 += orig3
+# asm 1: paddd <orig3=stack128#18,<z3=int6464#5
+# asm 2: paddd <orig3=272(%rsp),<z3=%xmm4
+paddd 272(%rsp),%xmm4
+
+# qhasm: in0 = z0
+# asm 1: movd <z0=int6464#13,>in0=int64#3
+# asm 2: movd <z0=%xmm12,>in0=%rdx
+movd %xmm12,%rdx
+
+# qhasm: in1 = z1
+# asm 1: movd <z1=int6464#8,>in1=int64#4
+# asm 2: movd <z1=%xmm7,>in1=%rcx
+movd %xmm7,%rcx
+
+# qhasm: in2 = z2
+# asm 1: movd <z2=int6464#11,>in2=int64#5
+# asm 2: movd <z2=%xmm10,>in2=%r8
+movd %xmm10,%r8
+
+# qhasm: in3 = z3
+# asm 1: movd <z3=int6464#5,>in3=int64#6
+# asm 2: movd <z3=%xmm4,>in3=%r9
+movd %xmm4,%r9
+
+# qhasm: z0 <<<= 96
+# asm 1: pshufd $0x39,<z0=int6464#13,<z0=int6464#13
+# asm 2: pshufd $0x39,<z0=%xmm12,<z0=%xmm12
+pshufd $0x39,%xmm12,%xmm12
+
+# qhasm: z1 <<<= 96
+# asm 1: pshufd $0x39,<z1=int6464#8,<z1=int6464#8
+# asm 2: pshufd $0x39,<z1=%xmm7,<z1=%xmm7
+pshufd $0x39,%xmm7,%xmm7
+
+# qhasm: z2 <<<= 96
+# asm 1: pshufd $0x39,<z2=int6464#11,<z2=int6464#11
+# asm 2: pshufd $0x39,<z2=%xmm10,<z2=%xmm10
+pshufd $0x39,%xmm10,%xmm10
+
+# qhasm: z3 <<<= 96
+# asm 1: pshufd $0x39,<z3=int6464#5,<z3=int6464#5
+# asm 2: pshufd $0x39,<z3=%xmm4,<z3=%xmm4
+pshufd $0x39,%xmm4,%xmm4
+
+# qhasm: (uint32) in0 ^= *(uint32 *) (m + 0)
+# asm 1: xorl 0(<m=int64#2),<in0=int64#3d
+# asm 2: xorl 0(<m=%rsi),<in0=%edx
+xorl 0(%rsi),%edx
+
+# qhasm: (uint32) in1 ^= *(uint32 *) (m + 4)
+# asm 1: xorl 4(<m=int64#2),<in1=int64#4d
+# asm 2: xorl 4(<m=%rsi),<in1=%ecx
+xorl 4(%rsi),%ecx
+
+# qhasm: (uint32) in2 ^= *(uint32 *) (m + 8)
+# asm 1: xorl 8(<m=int64#2),<in2=int64#5d
+# asm 2: xorl 8(<m=%rsi),<in2=%r8d
+xorl 8(%rsi),%r8d
+
+# qhasm: (uint32) in3 ^= *(uint32 *) (m + 12)
+# asm 1: xorl 12(<m=int64#2),<in3=int64#6d
+# asm 2: xorl 12(<m=%rsi),<in3=%r9d
+xorl 12(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 0) = in0
+# asm 1: movl <in0=int64#3d,0(<out=int64#1)
+# asm 2: movl <in0=%edx,0(<out=%rdi)
+movl %edx,0(%rdi)
+
+# qhasm: *(uint32 *) (out + 4) = in1
+# asm 1: movl <in1=int64#4d,4(<out=int64#1)
+# asm 2: movl <in1=%ecx,4(<out=%rdi)
+movl %ecx,4(%rdi)
+
+# qhasm: *(uint32 *) (out + 8) = in2
+# asm 1: movl <in2=int64#5d,8(<out=int64#1)
+# asm 2: movl <in2=%r8d,8(<out=%rdi)
+movl %r8d,8(%rdi)
+
+# qhasm: *(uint32 *) (out + 12) = in3
+# asm 1: movl <in3=int64#6d,12(<out=int64#1)
+# asm 2: movl <in3=%r9d,12(<out=%rdi)
+movl %r9d,12(%rdi)
+
+# qhasm: in0 = z0
+# asm 1: movd <z0=int6464#13,>in0=int64#3
+# asm 2: movd <z0=%xmm12,>in0=%rdx
+movd %xmm12,%rdx
+
+# qhasm: in1 = z1
+# asm 1: movd <z1=int6464#8,>in1=int64#4
+# asm 2: movd <z1=%xmm7,>in1=%rcx
+movd %xmm7,%rcx
+
+# qhasm: in2 = z2
+# asm 1: movd <z2=int6464#11,>in2=int64#5
+# asm 2: movd <z2=%xmm10,>in2=%r8
+movd %xmm10,%r8
+
+# qhasm: in3 = z3
+# asm 1: movd <z3=int6464#5,>in3=int64#6
+# asm 2: movd <z3=%xmm4,>in3=%r9
+movd %xmm4,%r9
+
+# qhasm: z0 <<<= 96
+# asm 1: pshufd $0x39,<z0=int6464#13,<z0=int6464#13
+# asm 2: pshufd $0x39,<z0=%xmm12,<z0=%xmm12
+pshufd $0x39,%xmm12,%xmm12
+
+# qhasm: z1 <<<= 96
+# asm 1: pshufd $0x39,<z1=int6464#8,<z1=int6464#8
+# asm 2: pshufd $0x39,<z1=%xmm7,<z1=%xmm7
+pshufd $0x39,%xmm7,%xmm7
+
+# qhasm: z2 <<<= 96
+# asm 1: pshufd $0x39,<z2=int6464#11,<z2=int6464#11
+# asm 2: pshufd $0x39,<z2=%xmm10,<z2=%xmm10
+pshufd $0x39,%xmm10,%xmm10
+
+# qhasm: z3 <<<= 96
+# asm 1: pshufd $0x39,<z3=int6464#5,<z3=int6464#5
+# asm 2: pshufd $0x39,<z3=%xmm4,<z3=%xmm4
+pshufd $0x39,%xmm4,%xmm4
+
+# qhasm: (uint32) in0 ^= *(uint32 *) (m + 64)
+# asm 1: xorl 64(<m=int64#2),<in0=int64#3d
+# asm 2: xorl 64(<m=%rsi),<in0=%edx
+xorl 64(%rsi),%edx
+
+# qhasm: (uint32) in1 ^= *(uint32 *) (m + 68)
+# asm 1: xorl 68(<m=int64#2),<in1=int64#4d
+# asm 2: xorl 68(<m=%rsi),<in1=%ecx
+xorl 68(%rsi),%ecx
+
+# qhasm: (uint32) in2 ^= *(uint32 *) (m + 72)
+# asm 1: xorl 72(<m=int64#2),<in2=int64#5d
+# asm 2: xorl 72(<m=%rsi),<in2=%r8d
+xorl 72(%rsi),%r8d
+
+# qhasm: (uint32) in3 ^= *(uint32 *) (m + 76)
+# asm 1: xorl 76(<m=int64#2),<in3=int64#6d
+# asm 2: xorl 76(<m=%rsi),<in3=%r9d
+xorl 76(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 64) = in0
+# asm 1: movl <in0=int64#3d,64(<out=int64#1)
+# asm 2: movl <in0=%edx,64(<out=%rdi)
+movl %edx,64(%rdi)
+
+# qhasm: *(uint32 *) (out + 68) = in1
+# asm 1: movl <in1=int64#4d,68(<out=int64#1)
+# asm 2: movl <in1=%ecx,68(<out=%rdi)
+movl %ecx,68(%rdi)
+
+# qhasm: *(uint32 *) (out + 72) = in2
+# asm 1: movl <in2=int64#5d,72(<out=int64#1)
+# asm 2: movl <in2=%r8d,72(<out=%rdi)
+movl %r8d,72(%rdi)
+
+# qhasm: *(uint32 *) (out + 76) = in3
+# asm 1: movl <in3=int64#6d,76(<out=int64#1)
+# asm 2: movl <in3=%r9d,76(<out=%rdi)
+movl %r9d,76(%rdi)
+
+# qhasm: in0 = z0
+# asm 1: movd <z0=int6464#13,>in0=int64#3
+# asm 2: movd <z0=%xmm12,>in0=%rdx
+movd %xmm12,%rdx
+
+# qhasm: in1 = z1
+# asm 1: movd <z1=int6464#8,>in1=int64#4
+# asm 2: movd <z1=%xmm7,>in1=%rcx
+movd %xmm7,%rcx
+
+# qhasm: in2 = z2
+# asm 1: movd <z2=int6464#11,>in2=int64#5
+# asm 2: movd <z2=%xmm10,>in2=%r8
+movd %xmm10,%r8
+
+# qhasm: in3 = z3
+# asm 1: movd <z3=int6464#5,>in3=int64#6
+# asm 2: movd <z3=%xmm4,>in3=%r9
+movd %xmm4,%r9
+
+# qhasm: z0 <<<= 96
+# asm 1: pshufd $0x39,<z0=int6464#13,<z0=int6464#13
+# asm 2: pshufd $0x39,<z0=%xmm12,<z0=%xmm12
+pshufd $0x39,%xmm12,%xmm12
+
+# qhasm: z1 <<<= 96
+# asm 1: pshufd $0x39,<z1=int6464#8,<z1=int6464#8
+# asm 2: pshufd $0x39,<z1=%xmm7,<z1=%xmm7
+pshufd $0x39,%xmm7,%xmm7
+
+# qhasm: z2 <<<= 96
+# asm 1: pshufd $0x39,<z2=int6464#11,<z2=int6464#11
+# asm 2: pshufd $0x39,<z2=%xmm10,<z2=%xmm10
+pshufd $0x39,%xmm10,%xmm10
+
+# qhasm: z3 <<<= 96
+# asm 1: pshufd $0x39,<z3=int6464#5,<z3=int6464#5
+# asm 2: pshufd $0x39,<z3=%xmm4,<z3=%xmm4
+pshufd $0x39,%xmm4,%xmm4
+
+# qhasm: (uint32) in0 ^= *(uint32 *) (m + 128)
+# asm 1: xorl 128(<m=int64#2),<in0=int64#3d
+# asm 2: xorl 128(<m=%rsi),<in0=%edx
+xorl 128(%rsi),%edx
+
+# qhasm: (uint32) in1 ^= *(uint32 *) (m + 132)
+# asm 1: xorl 132(<m=int64#2),<in1=int64#4d
+# asm 2: xorl 132(<m=%rsi),<in1=%ecx
+xorl 132(%rsi),%ecx
+
+# qhasm: (uint32) in2 ^= *(uint32 *) (m + 136)
+# asm 1: xorl 136(<m=int64#2),<in2=int64#5d
+# asm 2: xorl 136(<m=%rsi),<in2=%r8d
+xorl 136(%rsi),%r8d
+
+# qhasm: (uint32) in3 ^= *(uint32 *) (m + 140)
+# asm 1: xorl 140(<m=int64#2),<in3=int64#6d
+# asm 2: xorl 140(<m=%rsi),<in3=%r9d
+xorl 140(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 128) = in0
+# asm 1: movl <in0=int64#3d,128(<out=int64#1)
+# asm 2: movl <in0=%edx,128(<out=%rdi)
+movl %edx,128(%rdi)
+
+# qhasm: *(uint32 *) (out + 132) = in1
+# asm 1: movl <in1=int64#4d,132(<out=int64#1)
+# asm 2: movl <in1=%ecx,132(<out=%rdi)
+movl %ecx,132(%rdi)
+
+# qhasm: *(uint32 *) (out + 136) = in2
+# asm 1: movl <in2=int64#5d,136(<out=int64#1)
+# asm 2: movl <in2=%r8d,136(<out=%rdi)
+movl %r8d,136(%rdi)
+
+# qhasm: *(uint32 *) (out + 140) = in3
+# asm 1: movl <in3=int64#6d,140(<out=int64#1)
+# asm 2: movl <in3=%r9d,140(<out=%rdi)
+movl %r9d,140(%rdi)
+
+# qhasm: in0 = z0
+# asm 1: movd <z0=int6464#13,>in0=int64#3
+# asm 2: movd <z0=%xmm12,>in0=%rdx
+movd %xmm12,%rdx
+
+# qhasm: in1 = z1
+# asm 1: movd <z1=int6464#8,>in1=int64#4
+# asm 2: movd <z1=%xmm7,>in1=%rcx
+movd %xmm7,%rcx
+
+# qhasm: in2 = z2
+# asm 1: movd <z2=int6464#11,>in2=int64#5
+# asm 2: movd <z2=%xmm10,>in2=%r8
+movd %xmm10,%r8
+
+# qhasm: in3 = z3
+# asm 1: movd <z3=int6464#5,>in3=int64#6
+# asm 2: movd <z3=%xmm4,>in3=%r9
+movd %xmm4,%r9
+
+# qhasm: (uint32) in0 ^= *(uint32 *) (m + 192)
+# asm 1: xorl 192(<m=int64#2),<in0=int64#3d
+# asm 2: xorl 192(<m=%rsi),<in0=%edx
+xorl 192(%rsi),%edx
+
+# qhasm: (uint32) in1 ^= *(uint32 *) (m + 196)
+# asm 1: xorl 196(<m=int64#2),<in1=int64#4d
+# asm 2: xorl 196(<m=%rsi),<in1=%ecx
+xorl 196(%rsi),%ecx
+
+# qhasm: (uint32) in2 ^= *(uint32 *) (m + 200)
+# asm 1: xorl 200(<m=int64#2),<in2=int64#5d
+# asm 2: xorl 200(<m=%rsi),<in2=%r8d
+xorl 200(%rsi),%r8d
+
+# qhasm: (uint32) in3 ^= *(uint32 *) (m + 204)
+# asm 1: xorl 204(<m=int64#2),<in3=int64#6d
+# asm 2: xorl 204(<m=%rsi),<in3=%r9d
+xorl 204(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 192) = in0
+# asm 1: movl <in0=int64#3d,192(<out=int64#1)
+# asm 2: movl <in0=%edx,192(<out=%rdi)
+movl %edx,192(%rdi)
+
+# qhasm: *(uint32 *) (out + 196) = in1
+# asm 1: movl <in1=int64#4d,196(<out=int64#1)
+# asm 2: movl <in1=%ecx,196(<out=%rdi)
+movl %ecx,196(%rdi)
+
+# qhasm: *(uint32 *) (out + 200) = in2
+# asm 1: movl <in2=int64#5d,200(<out=int64#1)
+# asm 2: movl <in2=%r8d,200(<out=%rdi)
+movl %r8d,200(%rdi)
+
+# qhasm: *(uint32 *) (out + 204) = in3
+# asm 1: movl <in3=int64#6d,204(<out=int64#1)
+# asm 2: movl <in3=%r9d,204(<out=%rdi)
+movl %r9d,204(%rdi)
+
+# qhasm: uint32323232 z4 += orig4
+# asm 1: paddd <orig4=stack128#16,<z4=int6464#15
+# asm 2: paddd <orig4=240(%rsp),<z4=%xmm14
+paddd 240(%rsp),%xmm14
+
+# qhasm: uint32323232 z5 += orig5
+# asm 1: paddd <orig5=stack128#5,<z5=int6464#1
+# asm 2: paddd <orig5=64(%rsp),<z5=%xmm0
+paddd 64(%rsp),%xmm0
+
+# qhasm: uint32323232 z6 += orig6
+# asm 1: paddd <orig6=stack128#9,<z6=int6464#6
+# asm 2: paddd <orig6=128(%rsp),<z6=%xmm5
+paddd 128(%rsp),%xmm5
+
+# qhasm: uint32323232 z7 += orig7
+# asm 1: paddd <orig7=stack128#13,<z7=int6464#9
+# asm 2: paddd <orig7=192(%rsp),<z7=%xmm8
+paddd 192(%rsp),%xmm8
+
+# qhasm: in4 = z4
+# asm 1: movd <z4=int6464#15,>in4=int64#3
+# asm 2: movd <z4=%xmm14,>in4=%rdx
+movd %xmm14,%rdx
+
+# qhasm: in5 = z5
+# asm 1: movd <z5=int6464#1,>in5=int64#4
+# asm 2: movd <z5=%xmm0,>in5=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in6 = z6
+# asm 1: movd <z6=int6464#6,>in6=int64#5
+# asm 2: movd <z6=%xmm5,>in6=%r8
+movd %xmm5,%r8
+
+# qhasm: in7 = z7
+# asm 1: movd <z7=int6464#9,>in7=int64#6
+# asm 2: movd <z7=%xmm8,>in7=%r9
+movd %xmm8,%r9
+
+# qhasm: z4 <<<= 96
+# asm 1: pshufd $0x39,<z4=int6464#15,<z4=int6464#15
+# asm 2: pshufd $0x39,<z4=%xmm14,<z4=%xmm14
+pshufd $0x39,%xmm14,%xmm14
+
+# qhasm: z5 <<<= 96
+# asm 1: pshufd $0x39,<z5=int6464#1,<z5=int6464#1
+# asm 2: pshufd $0x39,<z5=%xmm0,<z5=%xmm0
+pshufd $0x39,%xmm0,%xmm0
+
+# qhasm: z6 <<<= 96
+# asm 1: pshufd $0x39,<z6=int6464#6,<z6=int6464#6
+# asm 2: pshufd $0x39,<z6=%xmm5,<z6=%xmm5
+pshufd $0x39,%xmm5,%xmm5
+
+# qhasm: z7 <<<= 96
+# asm 1: pshufd $0x39,<z7=int6464#9,<z7=int6464#9
+# asm 2: pshufd $0x39,<z7=%xmm8,<z7=%xmm8
+pshufd $0x39,%xmm8,%xmm8
+
+# qhasm: (uint32) in4 ^= *(uint32 *) (m + 16)
+# asm 1: xorl 16(<m=int64#2),<in4=int64#3d
+# asm 2: xorl 16(<m=%rsi),<in4=%edx
+xorl 16(%rsi),%edx
+
+# qhasm: (uint32) in5 ^= *(uint32 *) (m + 20)
+# asm 1: xorl 20(<m=int64#2),<in5=int64#4d
+# asm 2: xorl 20(<m=%rsi),<in5=%ecx
+xorl 20(%rsi),%ecx
+
+# qhasm: (uint32) in6 ^= *(uint32 *) (m + 24)
+# asm 1: xorl 24(<m=int64#2),<in6=int64#5d
+# asm 2: xorl 24(<m=%rsi),<in6=%r8d
+xorl 24(%rsi),%r8d
+
+# qhasm: (uint32) in7 ^= *(uint32 *) (m + 28)
+# asm 1: xorl 28(<m=int64#2),<in7=int64#6d
+# asm 2: xorl 28(<m=%rsi),<in7=%r9d
+xorl 28(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 16) = in4
+# asm 1: movl <in4=int64#3d,16(<out=int64#1)
+# asm 2: movl <in4=%edx,16(<out=%rdi)
+movl %edx,16(%rdi)
+
+# qhasm: *(uint32 *) (out + 20) = in5
+# asm 1: movl <in5=int64#4d,20(<out=int64#1)
+# asm 2: movl <in5=%ecx,20(<out=%rdi)
+movl %ecx,20(%rdi)
+
+# qhasm: *(uint32 *) (out + 24) = in6
+# asm 1: movl <in6=int64#5d,24(<out=int64#1)
+# asm 2: movl <in6=%r8d,24(<out=%rdi)
+movl %r8d,24(%rdi)
+
+# qhasm: *(uint32 *) (out + 28) = in7
+# asm 1: movl <in7=int64#6d,28(<out=int64#1)
+# asm 2: movl <in7=%r9d,28(<out=%rdi)
+movl %r9d,28(%rdi)
+
+# qhasm: in4 = z4
+# asm 1: movd <z4=int6464#15,>in4=int64#3
+# asm 2: movd <z4=%xmm14,>in4=%rdx
+movd %xmm14,%rdx
+
+# qhasm: in5 = z5
+# asm 1: movd <z5=int6464#1,>in5=int64#4
+# asm 2: movd <z5=%xmm0,>in5=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in6 = z6
+# asm 1: movd <z6=int6464#6,>in6=int64#5
+# asm 2: movd <z6=%xmm5,>in6=%r8
+movd %xmm5,%r8
+
+# qhasm: in7 = z7
+# asm 1: movd <z7=int6464#9,>in7=int64#6
+# asm 2: movd <z7=%xmm8,>in7=%r9
+movd %xmm8,%r9
+
+# qhasm: z4 <<<= 96
+# asm 1: pshufd $0x39,<z4=int6464#15,<z4=int6464#15
+# asm 2: pshufd $0x39,<z4=%xmm14,<z4=%xmm14
+pshufd $0x39,%xmm14,%xmm14
+
+# qhasm: z5 <<<= 96
+# asm 1: pshufd $0x39,<z5=int6464#1,<z5=int6464#1
+# asm 2: pshufd $0x39,<z5=%xmm0,<z5=%xmm0
+pshufd $0x39,%xmm0,%xmm0
+
+# qhasm: z6 <<<= 96
+# asm 1: pshufd $0x39,<z6=int6464#6,<z6=int6464#6
+# asm 2: pshufd $0x39,<z6=%xmm5,<z6=%xmm5
+pshufd $0x39,%xmm5,%xmm5
+
+# qhasm: z7 <<<= 96
+# asm 1: pshufd $0x39,<z7=int6464#9,<z7=int6464#9
+# asm 2: pshufd $0x39,<z7=%xmm8,<z7=%xmm8
+pshufd $0x39,%xmm8,%xmm8
+
+# qhasm: (uint32) in4 ^= *(uint32 *) (m + 80)
+# asm 1: xorl 80(<m=int64#2),<in4=int64#3d
+# asm 2: xorl 80(<m=%rsi),<in4=%edx
+xorl 80(%rsi),%edx
+
+# qhasm: (uint32) in5 ^= *(uint32 *) (m + 84)
+# asm 1: xorl 84(<m=int64#2),<in5=int64#4d
+# asm 2: xorl 84(<m=%rsi),<in5=%ecx
+xorl 84(%rsi),%ecx
+
+# qhasm: (uint32) in6 ^= *(uint32 *) (m + 88)
+# asm 1: xorl 88(<m=int64#2),<in6=int64#5d
+# asm 2: xorl 88(<m=%rsi),<in6=%r8d
+xorl 88(%rsi),%r8d
+
+# qhasm: (uint32) in7 ^= *(uint32 *) (m + 92)
+# asm 1: xorl 92(<m=int64#2),<in7=int64#6d
+# asm 2: xorl 92(<m=%rsi),<in7=%r9d
+xorl 92(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 80) = in4
+# asm 1: movl <in4=int64#3d,80(<out=int64#1)
+# asm 2: movl <in4=%edx,80(<out=%rdi)
+movl %edx,80(%rdi)
+
+# qhasm: *(uint32 *) (out + 84) = in5
+# asm 1: movl <in5=int64#4d,84(<out=int64#1)
+# asm 2: movl <in5=%ecx,84(<out=%rdi)
+movl %ecx,84(%rdi)
+
+# qhasm: *(uint32 *) (out + 88) = in6
+# asm 1: movl <in6=int64#5d,88(<out=int64#1)
+# asm 2: movl <in6=%r8d,88(<out=%rdi)
+movl %r8d,88(%rdi)
+
+# qhasm: *(uint32 *) (out + 92) = in7
+# asm 1: movl <in7=int64#6d,92(<out=int64#1)
+# asm 2: movl <in7=%r9d,92(<out=%rdi)
+movl %r9d,92(%rdi)
+
+# qhasm: in4 = z4
+# asm 1: movd <z4=int6464#15,>in4=int64#3
+# asm 2: movd <z4=%xmm14,>in4=%rdx
+movd %xmm14,%rdx
+
+# qhasm: in5 = z5
+# asm 1: movd <z5=int6464#1,>in5=int64#4
+# asm 2: movd <z5=%xmm0,>in5=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in6 = z6
+# asm 1: movd <z6=int6464#6,>in6=int64#5
+# asm 2: movd <z6=%xmm5,>in6=%r8
+movd %xmm5,%r8
+
+# qhasm: in7 = z7
+# asm 1: movd <z7=int6464#9,>in7=int64#6
+# asm 2: movd <z7=%xmm8,>in7=%r9
+movd %xmm8,%r9
+
+# qhasm: z4 <<<= 96
+# asm 1: pshufd $0x39,<z4=int6464#15,<z4=int6464#15
+# asm 2: pshufd $0x39,<z4=%xmm14,<z4=%xmm14
+pshufd $0x39,%xmm14,%xmm14
+
+# qhasm: z5 <<<= 96
+# asm 1: pshufd $0x39,<z5=int6464#1,<z5=int6464#1
+# asm 2: pshufd $0x39,<z5=%xmm0,<z5=%xmm0
+pshufd $0x39,%xmm0,%xmm0
+
+# qhasm: z6 <<<= 96
+# asm 1: pshufd $0x39,<z6=int6464#6,<z6=int6464#6
+# asm 2: pshufd $0x39,<z6=%xmm5,<z6=%xmm5
+pshufd $0x39,%xmm5,%xmm5
+
+# qhasm: z7 <<<= 96
+# asm 1: pshufd $0x39,<z7=int6464#9,<z7=int6464#9
+# asm 2: pshufd $0x39,<z7=%xmm8,<z7=%xmm8
+pshufd $0x39,%xmm8,%xmm8
+
+# qhasm: (uint32) in4 ^= *(uint32 *) (m + 144)
+# asm 1: xorl 144(<m=int64#2),<in4=int64#3d
+# asm 2: xorl 144(<m=%rsi),<in4=%edx
+xorl 144(%rsi),%edx
+
+# qhasm: (uint32) in5 ^= *(uint32 *) (m + 148)
+# asm 1: xorl 148(<m=int64#2),<in5=int64#4d
+# asm 2: xorl 148(<m=%rsi),<in5=%ecx
+xorl 148(%rsi),%ecx
+
+# qhasm: (uint32) in6 ^= *(uint32 *) (m + 152)
+# asm 1: xorl 152(<m=int64#2),<in6=int64#5d
+# asm 2: xorl 152(<m=%rsi),<in6=%r8d
+xorl 152(%rsi),%r8d
+
+# qhasm: (uint32) in7 ^= *(uint32 *) (m + 156)
+# asm 1: xorl 156(<m=int64#2),<in7=int64#6d
+# asm 2: xorl 156(<m=%rsi),<in7=%r9d
+xorl 156(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 144) = in4
+# asm 1: movl <in4=int64#3d,144(<out=int64#1)
+# asm 2: movl <in4=%edx,144(<out=%rdi)
+movl %edx,144(%rdi)
+
+# qhasm: *(uint32 *) (out + 148) = in5
+# asm 1: movl <in5=int64#4d,148(<out=int64#1)
+# asm 2: movl <in5=%ecx,148(<out=%rdi)
+movl %ecx,148(%rdi)
+
+# qhasm: *(uint32 *) (out + 152) = in6
+# asm 1: movl <in6=int64#5d,152(<out=int64#1)
+# asm 2: movl <in6=%r8d,152(<out=%rdi)
+movl %r8d,152(%rdi)
+
+# qhasm: *(uint32 *) (out + 156) = in7
+# asm 1: movl <in7=int64#6d,156(<out=int64#1)
+# asm 2: movl <in7=%r9d,156(<out=%rdi)
+movl %r9d,156(%rdi)
+
+# qhasm: in4 = z4
+# asm 1: movd <z4=int6464#15,>in4=int64#3
+# asm 2: movd <z4=%xmm14,>in4=%rdx
+movd %xmm14,%rdx
+
+# qhasm: in5 = z5
+# asm 1: movd <z5=int6464#1,>in5=int64#4
+# asm 2: movd <z5=%xmm0,>in5=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in6 = z6
+# asm 1: movd <z6=int6464#6,>in6=int64#5
+# asm 2: movd <z6=%xmm5,>in6=%r8
+movd %xmm5,%r8
+
+# qhasm: in7 = z7
+# asm 1: movd <z7=int6464#9,>in7=int64#6
+# asm 2: movd <z7=%xmm8,>in7=%r9
+movd %xmm8,%r9
+
+# qhasm: (uint32) in4 ^= *(uint32 *) (m + 208)
+# asm 1: xorl 208(<m=int64#2),<in4=int64#3d
+# asm 2: xorl 208(<m=%rsi),<in4=%edx
+xorl 208(%rsi),%edx
+
+# qhasm: (uint32) in5 ^= *(uint32 *) (m + 212)
+# asm 1: xorl 212(<m=int64#2),<in5=int64#4d
+# asm 2: xorl 212(<m=%rsi),<in5=%ecx
+xorl 212(%rsi),%ecx
+
+# qhasm: (uint32) in6 ^= *(uint32 *) (m + 216)
+# asm 1: xorl 216(<m=int64#2),<in6=int64#5d
+# asm 2: xorl 216(<m=%rsi),<in6=%r8d
+xorl 216(%rsi),%r8d
+
+# qhasm: (uint32) in7 ^= *(uint32 *) (m + 220)
+# asm 1: xorl 220(<m=int64#2),<in7=int64#6d
+# asm 2: xorl 220(<m=%rsi),<in7=%r9d
+xorl 220(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 208) = in4
+# asm 1: movl <in4=int64#3d,208(<out=int64#1)
+# asm 2: movl <in4=%edx,208(<out=%rdi)
+movl %edx,208(%rdi)
+
+# qhasm: *(uint32 *) (out + 212) = in5
+# asm 1: movl <in5=int64#4d,212(<out=int64#1)
+# asm 2: movl <in5=%ecx,212(<out=%rdi)
+movl %ecx,212(%rdi)
+
+# qhasm: *(uint32 *) (out + 216) = in6
+# asm 1: movl <in6=int64#5d,216(<out=int64#1)
+# asm 2: movl <in6=%r8d,216(<out=%rdi)
+movl %r8d,216(%rdi)
+
+# qhasm: *(uint32 *) (out + 220) = in7
+# asm 1: movl <in7=int64#6d,220(<out=int64#1)
+# asm 2: movl <in7=%r9d,220(<out=%rdi)
+movl %r9d,220(%rdi)
+
+# qhasm: uint32323232 z8 += orig8
+# asm 1: paddd <orig8=stack128#19,<z8=int6464#16
+# asm 2: paddd <orig8=288(%rsp),<z8=%xmm15
+paddd 288(%rsp),%xmm15
+
+# qhasm: uint32323232 z9 += orig9
+# asm 1: paddd <orig9=stack128#20,<z9=int6464#12
+# asm 2: paddd <orig9=304(%rsp),<z9=%xmm11
+paddd 304(%rsp),%xmm11
+
+# qhasm: uint32323232 z10 += orig10
+# asm 1: paddd <orig10=stack128#6,<z10=int6464#2
+# asm 2: paddd <orig10=80(%rsp),<z10=%xmm1
+paddd 80(%rsp),%xmm1
+
+# qhasm: uint32323232 z11 += orig11
+# asm 1: paddd <orig11=stack128#10,<z11=int6464#7
+# asm 2: paddd <orig11=144(%rsp),<z11=%xmm6
+paddd 144(%rsp),%xmm6
+
+# qhasm: in8 = z8
+# asm 1: movd <z8=int6464#16,>in8=int64#3
+# asm 2: movd <z8=%xmm15,>in8=%rdx
+movd %xmm15,%rdx
+
+# qhasm: in9 = z9
+# asm 1: movd <z9=int6464#12,>in9=int64#4
+# asm 2: movd <z9=%xmm11,>in9=%rcx
+movd %xmm11,%rcx
+
+# qhasm: in10 = z10
+# asm 1: movd <z10=int6464#2,>in10=int64#5
+# asm 2: movd <z10=%xmm1,>in10=%r8
+movd %xmm1,%r8
+
+# qhasm: in11 = z11
+# asm 1: movd <z11=int6464#7,>in11=int64#6
+# asm 2: movd <z11=%xmm6,>in11=%r9
+movd %xmm6,%r9
+
+# qhasm: z8 <<<= 96
+# asm 1: pshufd $0x39,<z8=int6464#16,<z8=int6464#16
+# asm 2: pshufd $0x39,<z8=%xmm15,<z8=%xmm15
+pshufd $0x39,%xmm15,%xmm15
+
+# qhasm: z9 <<<= 96
+# asm 1: pshufd $0x39,<z9=int6464#12,<z9=int6464#12
+# asm 2: pshufd $0x39,<z9=%xmm11,<z9=%xmm11
+pshufd $0x39,%xmm11,%xmm11
+
+# qhasm: z10 <<<= 96
+# asm 1: pshufd $0x39,<z10=int6464#2,<z10=int6464#2
+# asm 2: pshufd $0x39,<z10=%xmm1,<z10=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: z11 <<<= 96
+# asm 1: pshufd $0x39,<z11=int6464#7,<z11=int6464#7
+# asm 2: pshufd $0x39,<z11=%xmm6,<z11=%xmm6
+pshufd $0x39,%xmm6,%xmm6
+
+# qhasm: (uint32) in8 ^= *(uint32 *) (m + 32)
+# asm 1: xorl 32(<m=int64#2),<in8=int64#3d
+# asm 2: xorl 32(<m=%rsi),<in8=%edx
+xorl 32(%rsi),%edx
+
+# qhasm: (uint32) in9 ^= *(uint32 *) (m + 36)
+# asm 1: xorl 36(<m=int64#2),<in9=int64#4d
+# asm 2: xorl 36(<m=%rsi),<in9=%ecx
+xorl 36(%rsi),%ecx
+
+# qhasm: (uint32) in10 ^= *(uint32 *) (m + 40)
+# asm 1: xorl 40(<m=int64#2),<in10=int64#5d
+# asm 2: xorl 40(<m=%rsi),<in10=%r8d
+xorl 40(%rsi),%r8d
+
+# qhasm: (uint32) in11 ^= *(uint32 *) (m + 44)
+# asm 1: xorl 44(<m=int64#2),<in11=int64#6d
+# asm 2: xorl 44(<m=%rsi),<in11=%r9d
+xorl 44(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 32) = in8
+# asm 1: movl <in8=int64#3d,32(<out=int64#1)
+# asm 2: movl <in8=%edx,32(<out=%rdi)
+movl %edx,32(%rdi)
+
+# qhasm: *(uint32 *) (out + 36) = in9
+# asm 1: movl <in9=int64#4d,36(<out=int64#1)
+# asm 2: movl <in9=%ecx,36(<out=%rdi)
+movl %ecx,36(%rdi)
+
+# qhasm: *(uint32 *) (out + 40) = in10
+# asm 1: movl <in10=int64#5d,40(<out=int64#1)
+# asm 2: movl <in10=%r8d,40(<out=%rdi)
+movl %r8d,40(%rdi)
+
+# qhasm: *(uint32 *) (out + 44) = in11
+# asm 1: movl <in11=int64#6d,44(<out=int64#1)
+# asm 2: movl <in11=%r9d,44(<out=%rdi)
+movl %r9d,44(%rdi)
+
+# qhasm: in8 = z8
+# asm 1: movd <z8=int6464#16,>in8=int64#3
+# asm 2: movd <z8=%xmm15,>in8=%rdx
+movd %xmm15,%rdx
+
+# qhasm: in9 = z9
+# asm 1: movd <z9=int6464#12,>in9=int64#4
+# asm 2: movd <z9=%xmm11,>in9=%rcx
+movd %xmm11,%rcx
+
+# qhasm: in10 = z10
+# asm 1: movd <z10=int6464#2,>in10=int64#5
+# asm 2: movd <z10=%xmm1,>in10=%r8
+movd %xmm1,%r8
+
+# qhasm: in11 = z11
+# asm 1: movd <z11=int6464#7,>in11=int64#6
+# asm 2: movd <z11=%xmm6,>in11=%r9
+movd %xmm6,%r9
+
+# qhasm: z8 <<<= 96
+# asm 1: pshufd $0x39,<z8=int6464#16,<z8=int6464#16
+# asm 2: pshufd $0x39,<z8=%xmm15,<z8=%xmm15
+pshufd $0x39,%xmm15,%xmm15
+
+# qhasm: z9 <<<= 96
+# asm 1: pshufd $0x39,<z9=int6464#12,<z9=int6464#12
+# asm 2: pshufd $0x39,<z9=%xmm11,<z9=%xmm11
+pshufd $0x39,%xmm11,%xmm11
+
+# qhasm: z10 <<<= 96
+# asm 1: pshufd $0x39,<z10=int6464#2,<z10=int6464#2
+# asm 2: pshufd $0x39,<z10=%xmm1,<z10=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: z11 <<<= 96
+# asm 1: pshufd $0x39,<z11=int6464#7,<z11=int6464#7
+# asm 2: pshufd $0x39,<z11=%xmm6,<z11=%xmm6
+pshufd $0x39,%xmm6,%xmm6
+
+# qhasm: (uint32) in8 ^= *(uint32 *) (m + 96)
+# asm 1: xorl 96(<m=int64#2),<in8=int64#3d
+# asm 2: xorl 96(<m=%rsi),<in8=%edx
+xorl 96(%rsi),%edx
+
+# qhasm: (uint32) in9 ^= *(uint32 *) (m + 100)
+# asm 1: xorl 100(<m=int64#2),<in9=int64#4d
+# asm 2: xorl 100(<m=%rsi),<in9=%ecx
+xorl 100(%rsi),%ecx
+
+# qhasm: (uint32) in10 ^= *(uint32 *) (m + 104)
+# asm 1: xorl 104(<m=int64#2),<in10=int64#5d
+# asm 2: xorl 104(<m=%rsi),<in10=%r8d
+xorl 104(%rsi),%r8d
+
+# qhasm: (uint32) in11 ^= *(uint32 *) (m + 108)
+# asm 1: xorl 108(<m=int64#2),<in11=int64#6d
+# asm 2: xorl 108(<m=%rsi),<in11=%r9d
+xorl 108(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 96) = in8
+# asm 1: movl <in8=int64#3d,96(<out=int64#1)
+# asm 2: movl <in8=%edx,96(<out=%rdi)
+movl %edx,96(%rdi)
+
+# qhasm: *(uint32 *) (out + 100) = in9
+# asm 1: movl <in9=int64#4d,100(<out=int64#1)
+# asm 2: movl <in9=%ecx,100(<out=%rdi)
+movl %ecx,100(%rdi)
+
+# qhasm: *(uint32 *) (out + 104) = in10
+# asm 1: movl <in10=int64#5d,104(<out=int64#1)
+# asm 2: movl <in10=%r8d,104(<out=%rdi)
+movl %r8d,104(%rdi)
+
+# qhasm: *(uint32 *) (out + 108) = in11
+# asm 1: movl <in11=int64#6d,108(<out=int64#1)
+# asm 2: movl <in11=%r9d,108(<out=%rdi)
+movl %r9d,108(%rdi)
+
+# qhasm: in8 = z8
+# asm 1: movd <z8=int6464#16,>in8=int64#3
+# asm 2: movd <z8=%xmm15,>in8=%rdx
+movd %xmm15,%rdx
+
+# qhasm: in9 = z9
+# asm 1: movd <z9=int6464#12,>in9=int64#4
+# asm 2: movd <z9=%xmm11,>in9=%rcx
+movd %xmm11,%rcx
+
+# qhasm: in10 = z10
+# asm 1: movd <z10=int6464#2,>in10=int64#5
+# asm 2: movd <z10=%xmm1,>in10=%r8
+movd %xmm1,%r8
+
+# qhasm: in11 = z11
+# asm 1: movd <z11=int6464#7,>in11=int64#6
+# asm 2: movd <z11=%xmm6,>in11=%r9
+movd %xmm6,%r9
+
+# qhasm: z8 <<<= 96
+# asm 1: pshufd $0x39,<z8=int6464#16,<z8=int6464#16
+# asm 2: pshufd $0x39,<z8=%xmm15,<z8=%xmm15
+pshufd $0x39,%xmm15,%xmm15
+
+# qhasm: z9 <<<= 96
+# asm 1: pshufd $0x39,<z9=int6464#12,<z9=int6464#12
+# asm 2: pshufd $0x39,<z9=%xmm11,<z9=%xmm11
+pshufd $0x39,%xmm11,%xmm11
+
+# qhasm: z10 <<<= 96
+# asm 1: pshufd $0x39,<z10=int6464#2,<z10=int6464#2
+# asm 2: pshufd $0x39,<z10=%xmm1,<z10=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: z11 <<<= 96
+# asm 1: pshufd $0x39,<z11=int6464#7,<z11=int6464#7
+# asm 2: pshufd $0x39,<z11=%xmm6,<z11=%xmm6
+pshufd $0x39,%xmm6,%xmm6
+
+# qhasm: (uint32) in8 ^= *(uint32 *) (m + 160)
+# asm 1: xorl 160(<m=int64#2),<in8=int64#3d
+# asm 2: xorl 160(<m=%rsi),<in8=%edx
+xorl 160(%rsi),%edx
+
+# qhasm: (uint32) in9 ^= *(uint32 *) (m + 164)
+# asm 1: xorl 164(<m=int64#2),<in9=int64#4d
+# asm 2: xorl 164(<m=%rsi),<in9=%ecx
+xorl 164(%rsi),%ecx
+
+# qhasm: (uint32) in10 ^= *(uint32 *) (m + 168)
+# asm 1: xorl 168(<m=int64#2),<in10=int64#5d
+# asm 2: xorl 168(<m=%rsi),<in10=%r8d
+xorl 168(%rsi),%r8d
+
+# qhasm: (uint32) in11 ^= *(uint32 *) (m + 172)
+# asm 1: xorl 172(<m=int64#2),<in11=int64#6d
+# asm 2: xorl 172(<m=%rsi),<in11=%r9d
+xorl 172(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 160) = in8
+# asm 1: movl <in8=int64#3d,160(<out=int64#1)
+# asm 2: movl <in8=%edx,160(<out=%rdi)
+movl %edx,160(%rdi)
+
+# qhasm: *(uint32 *) (out + 164) = in9
+# asm 1: movl <in9=int64#4d,164(<out=int64#1)
+# asm 2: movl <in9=%ecx,164(<out=%rdi)
+movl %ecx,164(%rdi)
+
+# qhasm: *(uint32 *) (out + 168) = in10
+# asm 1: movl <in10=int64#5d,168(<out=int64#1)
+# asm 2: movl <in10=%r8d,168(<out=%rdi)
+movl %r8d,168(%rdi)
+
+# qhasm: *(uint32 *) (out + 172) = in11
+# asm 1: movl <in11=int64#6d,172(<out=int64#1)
+# asm 2: movl <in11=%r9d,172(<out=%rdi)
+movl %r9d,172(%rdi)
+
+# qhasm: in8 = z8
+# asm 1: movd <z8=int6464#16,>in8=int64#3
+# asm 2: movd <z8=%xmm15,>in8=%rdx
+movd %xmm15,%rdx
+
+# qhasm: in9 = z9
+# asm 1: movd <z9=int6464#12,>in9=int64#4
+# asm 2: movd <z9=%xmm11,>in9=%rcx
+movd %xmm11,%rcx
+
+# qhasm: in10 = z10
+# asm 1: movd <z10=int6464#2,>in10=int64#5
+# asm 2: movd <z10=%xmm1,>in10=%r8
+movd %xmm1,%r8
+
+# qhasm: in11 = z11
+# asm 1: movd <z11=int6464#7,>in11=int64#6
+# asm 2: movd <z11=%xmm6,>in11=%r9
+movd %xmm6,%r9
+
+# qhasm: (uint32) in8 ^= *(uint32 *) (m + 224)
+# asm 1: xorl 224(<m=int64#2),<in8=int64#3d
+# asm 2: xorl 224(<m=%rsi),<in8=%edx
+xorl 224(%rsi),%edx
+
+# qhasm: (uint32) in9 ^= *(uint32 *) (m + 228)
+# asm 1: xorl 228(<m=int64#2),<in9=int64#4d
+# asm 2: xorl 228(<m=%rsi),<in9=%ecx
+xorl 228(%rsi),%ecx
+
+# qhasm: (uint32) in10 ^= *(uint32 *) (m + 232)
+# asm 1: xorl 232(<m=int64#2),<in10=int64#5d
+# asm 2: xorl 232(<m=%rsi),<in10=%r8d
+xorl 232(%rsi),%r8d
+
+# qhasm: (uint32) in11 ^= *(uint32 *) (m + 236)
+# asm 1: xorl 236(<m=int64#2),<in11=int64#6d
+# asm 2: xorl 236(<m=%rsi),<in11=%r9d
+xorl 236(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 224) = in8
+# asm 1: movl <in8=int64#3d,224(<out=int64#1)
+# asm 2: movl <in8=%edx,224(<out=%rdi)
+movl %edx,224(%rdi)
+
+# qhasm: *(uint32 *) (out + 228) = in9
+# asm 1: movl <in9=int64#4d,228(<out=int64#1)
+# asm 2: movl <in9=%ecx,228(<out=%rdi)
+movl %ecx,228(%rdi)
+
+# qhasm: *(uint32 *) (out + 232) = in10
+# asm 1: movl <in10=int64#5d,232(<out=int64#1)
+# asm 2: movl <in10=%r8d,232(<out=%rdi)
+movl %r8d,232(%rdi)
+
+# qhasm: *(uint32 *) (out + 236) = in11
+# asm 1: movl <in11=int64#6d,236(<out=int64#1)
+# asm 2: movl <in11=%r9d,236(<out=%rdi)
+movl %r9d,236(%rdi)
+
+# qhasm: uint32323232 z12 += orig12
+# asm 1: paddd <orig12=stack128#11,<z12=int6464#14
+# asm 2: paddd <orig12=160(%rsp),<z12=%xmm13
+paddd 160(%rsp),%xmm13
+
+# qhasm: uint32323232 z13 += orig13
+# asm 1: paddd <orig13=stack128#14,<z13=int6464#10
+# asm 2: paddd <orig13=208(%rsp),<z13=%xmm9
+paddd 208(%rsp),%xmm9
+
+# qhasm: uint32323232 z14 += orig14
+# asm 1: paddd <orig14=stack128#17,<z14=int6464#4
+# asm 2: paddd <orig14=256(%rsp),<z14=%xmm3
+paddd 256(%rsp),%xmm3
+
+# qhasm: uint32323232 z15 += orig15
+# asm 1: paddd <orig15=stack128#7,<z15=int6464#3
+# asm 2: paddd <orig15=96(%rsp),<z15=%xmm2
+paddd 96(%rsp),%xmm2
+
+# qhasm: in12 = z12
+# asm 1: movd <z12=int6464#14,>in12=int64#3
+# asm 2: movd <z12=%xmm13,>in12=%rdx
+movd %xmm13,%rdx
+
+# qhasm: in13 = z13
+# asm 1: movd <z13=int6464#10,>in13=int64#4
+# asm 2: movd <z13=%xmm9,>in13=%rcx
+movd %xmm9,%rcx
+
+# qhasm: in14 = z14
+# asm 1: movd <z14=int6464#4,>in14=int64#5
+# asm 2: movd <z14=%xmm3,>in14=%r8
+movd %xmm3,%r8
+
+# qhasm: in15 = z15
+# asm 1: movd <z15=int6464#3,>in15=int64#6
+# asm 2: movd <z15=%xmm2,>in15=%r9
+movd %xmm2,%r9
+
+# qhasm: z12 <<<= 96
+# asm 1: pshufd $0x39,<z12=int6464#14,<z12=int6464#14
+# asm 2: pshufd $0x39,<z12=%xmm13,<z12=%xmm13
+pshufd $0x39,%xmm13,%xmm13
+
+# qhasm: z13 <<<= 96
+# asm 1: pshufd $0x39,<z13=int6464#10,<z13=int6464#10
+# asm 2: pshufd $0x39,<z13=%xmm9,<z13=%xmm9
+pshufd $0x39,%xmm9,%xmm9
+
+# qhasm: z14 <<<= 96
+# asm 1: pshufd $0x39,<z14=int6464#4,<z14=int6464#4
+# asm 2: pshufd $0x39,<z14=%xmm3,<z14=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: z15 <<<= 96
+# asm 1: pshufd $0x39,<z15=int6464#3,<z15=int6464#3
+# asm 2: pshufd $0x39,<z15=%xmm2,<z15=%xmm2
+pshufd $0x39,%xmm2,%xmm2
+
+# qhasm: (uint32) in12 ^= *(uint32 *) (m + 48)
+# asm 1: xorl 48(<m=int64#2),<in12=int64#3d
+# asm 2: xorl 48(<m=%rsi),<in12=%edx
+xorl 48(%rsi),%edx
+
+# qhasm: (uint32) in13 ^= *(uint32 *) (m + 52)
+# asm 1: xorl 52(<m=int64#2),<in13=int64#4d
+# asm 2: xorl 52(<m=%rsi),<in13=%ecx
+xorl 52(%rsi),%ecx
+
+# qhasm: (uint32) in14 ^= *(uint32 *) (m + 56)
+# asm 1: xorl 56(<m=int64#2),<in14=int64#5d
+# asm 2: xorl 56(<m=%rsi),<in14=%r8d
+xorl 56(%rsi),%r8d
+
+# qhasm: (uint32) in15 ^= *(uint32 *) (m + 60)
+# asm 1: xorl 60(<m=int64#2),<in15=int64#6d
+# asm 2: xorl 60(<m=%rsi),<in15=%r9d
+xorl 60(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 48) = in12
+# asm 1: movl <in12=int64#3d,48(<out=int64#1)
+# asm 2: movl <in12=%edx,48(<out=%rdi)
+movl %edx,48(%rdi)
+
+# qhasm: *(uint32 *) (out + 52) = in13
+# asm 1: movl <in13=int64#4d,52(<out=int64#1)
+# asm 2: movl <in13=%ecx,52(<out=%rdi)
+movl %ecx,52(%rdi)
+
+# qhasm: *(uint32 *) (out + 56) = in14
+# asm 1: movl <in14=int64#5d,56(<out=int64#1)
+# asm 2: movl <in14=%r8d,56(<out=%rdi)
+movl %r8d,56(%rdi)
+
+# qhasm: *(uint32 *) (out + 60) = in15
+# asm 1: movl <in15=int64#6d,60(<out=int64#1)
+# asm 2: movl <in15=%r9d,60(<out=%rdi)
+movl %r9d,60(%rdi)
+
+# qhasm: in12 = z12
+# asm 1: movd <z12=int6464#14,>in12=int64#3
+# asm 2: movd <z12=%xmm13,>in12=%rdx
+movd %xmm13,%rdx
+
+# qhasm: in13 = z13
+# asm 1: movd <z13=int6464#10,>in13=int64#4
+# asm 2: movd <z13=%xmm9,>in13=%rcx
+movd %xmm9,%rcx
+
+# qhasm: in14 = z14
+# asm 1: movd <z14=int6464#4,>in14=int64#5
+# asm 2: movd <z14=%xmm3,>in14=%r8
+movd %xmm3,%r8
+
+# qhasm: in15 = z15
+# asm 1: movd <z15=int6464#3,>in15=int64#6
+# asm 2: movd <z15=%xmm2,>in15=%r9
+movd %xmm2,%r9
+
+# qhasm: z12 <<<= 96
+# asm 1: pshufd $0x39,<z12=int6464#14,<z12=int6464#14
+# asm 2: pshufd $0x39,<z12=%xmm13,<z12=%xmm13
+pshufd $0x39,%xmm13,%xmm13
+
+# qhasm: z13 <<<= 96
+# asm 1: pshufd $0x39,<z13=int6464#10,<z13=int6464#10
+# asm 2: pshufd $0x39,<z13=%xmm9,<z13=%xmm9
+pshufd $0x39,%xmm9,%xmm9
+
+# qhasm: z14 <<<= 96
+# asm 1: pshufd $0x39,<z14=int6464#4,<z14=int6464#4
+# asm 2: pshufd $0x39,<z14=%xmm3,<z14=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: z15 <<<= 96
+# asm 1: pshufd $0x39,<z15=int6464#3,<z15=int6464#3
+# asm 2: pshufd $0x39,<z15=%xmm2,<z15=%xmm2
+pshufd $0x39,%xmm2,%xmm2
+
+# qhasm: (uint32) in12 ^= *(uint32 *) (m + 112)
+# asm 1: xorl 112(<m=int64#2),<in12=int64#3d
+# asm 2: xorl 112(<m=%rsi),<in12=%edx
+xorl 112(%rsi),%edx
+
+# qhasm: (uint32) in13 ^= *(uint32 *) (m + 116)
+# asm 1: xorl 116(<m=int64#2),<in13=int64#4d
+# asm 2: xorl 116(<m=%rsi),<in13=%ecx
+xorl 116(%rsi),%ecx
+
+# qhasm: (uint32) in14 ^= *(uint32 *) (m + 120)
+# asm 1: xorl 120(<m=int64#2),<in14=int64#5d
+# asm 2: xorl 120(<m=%rsi),<in14=%r8d
+xorl 120(%rsi),%r8d
+
+# qhasm: (uint32) in15 ^= *(uint32 *) (m + 124)
+# asm 1: xorl 124(<m=int64#2),<in15=int64#6d
+# asm 2: xorl 124(<m=%rsi),<in15=%r9d
+xorl 124(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 112) = in12
+# asm 1: movl <in12=int64#3d,112(<out=int64#1)
+# asm 2: movl <in12=%edx,112(<out=%rdi)
+movl %edx,112(%rdi)
+
+# qhasm: *(uint32 *) (out + 116) = in13
+# asm 1: movl <in13=int64#4d,116(<out=int64#1)
+# asm 2: movl <in13=%ecx,116(<out=%rdi)
+movl %ecx,116(%rdi)
+
+# qhasm: *(uint32 *) (out + 120) = in14
+# asm 1: movl <in14=int64#5d,120(<out=int64#1)
+# asm 2: movl <in14=%r8d,120(<out=%rdi)
+movl %r8d,120(%rdi)
+
+# qhasm: *(uint32 *) (out + 124) = in15
+# asm 1: movl <in15=int64#6d,124(<out=int64#1)
+# asm 2: movl <in15=%r9d,124(<out=%rdi)
+movl %r9d,124(%rdi)
+
+# qhasm: in12 = z12
+# asm 1: movd <z12=int6464#14,>in12=int64#3
+# asm 2: movd <z12=%xmm13,>in12=%rdx
+movd %xmm13,%rdx
+
+# qhasm: in13 = z13
+# asm 1: movd <z13=int6464#10,>in13=int64#4
+# asm 2: movd <z13=%xmm9,>in13=%rcx
+movd %xmm9,%rcx
+
+# qhasm: in14 = z14
+# asm 1: movd <z14=int6464#4,>in14=int64#5
+# asm 2: movd <z14=%xmm3,>in14=%r8
+movd %xmm3,%r8
+
+# qhasm: in15 = z15
+# asm 1: movd <z15=int6464#3,>in15=int64#6
+# asm 2: movd <z15=%xmm2,>in15=%r9
+movd %xmm2,%r9
+
+# qhasm: z12 <<<= 96
+# asm 1: pshufd $0x39,<z12=int6464#14,<z12=int6464#14
+# asm 2: pshufd $0x39,<z12=%xmm13,<z12=%xmm13
+pshufd $0x39,%xmm13,%xmm13
+
+# qhasm: z13 <<<= 96
+# asm 1: pshufd $0x39,<z13=int6464#10,<z13=int6464#10
+# asm 2: pshufd $0x39,<z13=%xmm9,<z13=%xmm9
+pshufd $0x39,%xmm9,%xmm9
+
+# qhasm: z14 <<<= 96
+# asm 1: pshufd $0x39,<z14=int6464#4,<z14=int6464#4
+# asm 2: pshufd $0x39,<z14=%xmm3,<z14=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: z15 <<<= 96
+# asm 1: pshufd $0x39,<z15=int6464#3,<z15=int6464#3
+# asm 2: pshufd $0x39,<z15=%xmm2,<z15=%xmm2
+pshufd $0x39,%xmm2,%xmm2
+
+# qhasm: (uint32) in12 ^= *(uint32 *) (m + 176)
+# asm 1: xorl 176(<m=int64#2),<in12=int64#3d
+# asm 2: xorl 176(<m=%rsi),<in12=%edx
+xorl 176(%rsi),%edx
+
+# qhasm: (uint32) in13 ^= *(uint32 *) (m + 180)
+# asm 1: xorl 180(<m=int64#2),<in13=int64#4d
+# asm 2: xorl 180(<m=%rsi),<in13=%ecx
+xorl 180(%rsi),%ecx
+
+# qhasm: (uint32) in14 ^= *(uint32 *) (m + 184)
+# asm 1: xorl 184(<m=int64#2),<in14=int64#5d
+# asm 2: xorl 184(<m=%rsi),<in14=%r8d
+xorl 184(%rsi),%r8d
+
+# qhasm: (uint32) in15 ^= *(uint32 *) (m + 188)
+# asm 1: xorl 188(<m=int64#2),<in15=int64#6d
+# asm 2: xorl 188(<m=%rsi),<in15=%r9d
+xorl 188(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 176) = in12
+# asm 1: movl <in12=int64#3d,176(<out=int64#1)
+# asm 2: movl <in12=%edx,176(<out=%rdi)
+movl %edx,176(%rdi)
+
+# qhasm: *(uint32 *) (out + 180) = in13
+# asm 1: movl <in13=int64#4d,180(<out=int64#1)
+# asm 2: movl <in13=%ecx,180(<out=%rdi)
+movl %ecx,180(%rdi)
+
+# qhasm: *(uint32 *) (out + 184) = in14
+# asm 1: movl <in14=int64#5d,184(<out=int64#1)
+# asm 2: movl <in14=%r8d,184(<out=%rdi)
+movl %r8d,184(%rdi)
+
+# qhasm: *(uint32 *) (out + 188) = in15
+# asm 1: movl <in15=int64#6d,188(<out=int64#1)
+# asm 2: movl <in15=%r9d,188(<out=%rdi)
+movl %r9d,188(%rdi)
+
+# qhasm: in12 = z12
+# asm 1: movd <z12=int6464#14,>in12=int64#3
+# asm 2: movd <z12=%xmm13,>in12=%rdx
+movd %xmm13,%rdx
+
+# qhasm: in13 = z13
+# asm 1: movd <z13=int6464#10,>in13=int64#4
+# asm 2: movd <z13=%xmm9,>in13=%rcx
+movd %xmm9,%rcx
+
+# qhasm: in14 = z14
+# asm 1: movd <z14=int6464#4,>in14=int64#5
+# asm 2: movd <z14=%xmm3,>in14=%r8
+movd %xmm3,%r8
+
+# qhasm: in15 = z15
+# asm 1: movd <z15=int6464#3,>in15=int64#6
+# asm 2: movd <z15=%xmm2,>in15=%r9
+movd %xmm2,%r9
+
+# qhasm: (uint32) in12 ^= *(uint32 *) (m + 240)
+# asm 1: xorl 240(<m=int64#2),<in12=int64#3d
+# asm 2: xorl 240(<m=%rsi),<in12=%edx
+xorl 240(%rsi),%edx
+
+# qhasm: (uint32) in13 ^= *(uint32 *) (m + 244)
+# asm 1: xorl 244(<m=int64#2),<in13=int64#4d
+# asm 2: xorl 244(<m=%rsi),<in13=%ecx
+xorl 244(%rsi),%ecx
+
+# qhasm: (uint32) in14 ^= *(uint32 *) (m + 248)
+# asm 1: xorl 248(<m=int64#2),<in14=int64#5d
+# asm 2: xorl 248(<m=%rsi),<in14=%r8d
+xorl 248(%rsi),%r8d
+
+# qhasm: (uint32) in15 ^= *(uint32 *) (m + 252)
+# asm 1: xorl 252(<m=int64#2),<in15=int64#6d
+# asm 2: xorl 252(<m=%rsi),<in15=%r9d
+xorl 252(%rsi),%r9d
+
+# qhasm: *(uint32 *) (out + 240) = in12
+# asm 1: movl <in12=int64#3d,240(<out=int64#1)
+# asm 2: movl <in12=%edx,240(<out=%rdi)
+movl %edx,240(%rdi)
+
+# qhasm: *(uint32 *) (out + 244) = in13
+# asm 1: movl <in13=int64#4d,244(<out=int64#1)
+# asm 2: movl <in13=%ecx,244(<out=%rdi)
+movl %ecx,244(%rdi)
+
+# qhasm: *(uint32 *) (out + 248) = in14
+# asm 1: movl <in14=int64#5d,248(<out=int64#1)
+# asm 2: movl <in14=%r8d,248(<out=%rdi)
+movl %r8d,248(%rdi)
+
+# qhasm: *(uint32 *) (out + 252) = in15
+# asm 1: movl <in15=int64#6d,252(<out=int64#1)
+# asm 2: movl <in15=%r9d,252(<out=%rdi)
+movl %r9d,252(%rdi)
+
+# qhasm: bytes = bytes_backup
+# asm 1: movq <bytes_backup=stack64#8,>bytes=int64#6
+# asm 2: movq <bytes_backup=408(%rsp),>bytes=%r9
+movq 408(%rsp),%r9
+
+# qhasm: bytes -= 256
+# asm 1: sub $256,<bytes=int64#6
+# asm 2: sub $256,<bytes=%r9
+sub $256,%r9
+
+# qhasm: m += 256
+# asm 1: add $256,<m=int64#2
+# asm 2: add $256,<m=%rsi
+add $256,%rsi
+
+# qhasm: out += 256
+# asm 1: add $256,<out=int64#1
+# asm 2: add $256,<out=%rdi
+add $256,%rdi
+
+# qhasm: unsigned<? bytes - 256
+# asm 1: cmp $256,<bytes=int64#6
+# asm 2: cmp $256,<bytes=%r9
+cmp $256,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto bytesatleast256 if !unsigned<
+jae ._bytesatleast256
+
+# qhasm: unsigned>? bytes - 0
+# asm 1: cmp $0,<bytes=int64#6
+# asm 2: cmp $0,<bytes=%r9
+cmp $0,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto done if !unsigned>
+jbe ._done
+# comment:fp stack unchanged by fallthrough
+
+# qhasm: bytesbetween1and255:
+._bytesbetween1and255:
+
+# qhasm: unsigned<? bytes - 64
+# asm 1: cmp $64,<bytes=int64#6
+# asm 2: cmp $64,<bytes=%r9
+cmp $64,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto nocopy if !unsigned<
+jae ._nocopy
+
+# qhasm: ctarget = out
+# asm 1: mov <out=int64#1,>ctarget=int64#3
+# asm 2: mov <out=%rdi,>ctarget=%rdx
+mov %rdi,%rdx
+
+# qhasm: out = &tmp
+# asm 1: leaq <tmp=stack512#1,>out=int64#1
+# asm 2: leaq <tmp=416(%rsp),>out=%rdi
+leaq 416(%rsp),%rdi
+
+# qhasm: i = bytes
+# asm 1: mov <bytes=int64#6,>i=int64#4
+# asm 2: mov <bytes=%r9,>i=%rcx
+mov %r9,%rcx
+
+# qhasm: while (i) { *out++ = *m++; --i }
+rep movsb
+
+# qhasm: out = &tmp
+# asm 1: leaq <tmp=stack512#1,>out=int64#1
+# asm 2: leaq <tmp=416(%rsp),>out=%rdi
+leaq 416(%rsp),%rdi
+
+# qhasm: m = &tmp
+# asm 1: leaq <tmp=stack512#1,>m=int64#2
+# asm 2: leaq <tmp=416(%rsp),>m=%rsi
+leaq 416(%rsp),%rsi
+# comment:fp stack unchanged by fallthrough
+
+# qhasm: nocopy:
+._nocopy:
+
+# qhasm: bytes_backup = bytes
+# asm 1: movq <bytes=int64#6,>bytes_backup=stack64#8
+# asm 2: movq <bytes=%r9,>bytes_backup=408(%rsp)
+movq %r9,408(%rsp)
+
+# qhasm: diag0 = x0
+# asm 1: movdqa <x0=stack128#4,>diag0=int6464#1
+# asm 2: movdqa <x0=48(%rsp),>diag0=%xmm0
+movdqa 48(%rsp),%xmm0
+
+# qhasm: diag1 = x1
+# asm 1: movdqa <x1=stack128#1,>diag1=int6464#2
+# asm 2: movdqa <x1=0(%rsp),>diag1=%xmm1
+movdqa 0(%rsp),%xmm1
+
+# qhasm: diag2 = x2
+# asm 1: movdqa <x2=stack128#2,>diag2=int6464#3
+# asm 2: movdqa <x2=16(%rsp),>diag2=%xmm2
+movdqa 16(%rsp),%xmm2
+
+# qhasm: diag3 = x3
+# asm 1: movdqa <x3=stack128#3,>diag3=int6464#4
+# asm 2: movdqa <x3=32(%rsp),>diag3=%xmm3
+movdqa 32(%rsp),%xmm3
+
+# qhasm: a0 = diag1
+# asm 1: movdqa <diag1=int6464#2,>a0=int6464#5
+# asm 2: movdqa <diag1=%xmm1,>a0=%xmm4
+movdqa %xmm1,%xmm4
+
+# qhasm: i = 12
+# asm 1: mov $12,>i=int64#4
+# asm 2: mov $12,>i=%rcx
+mov $12,%rcx
+
+# qhasm: mainloop2:
+._mainloop2:
+
+# qhasm: uint32323232 a0 += diag0
+# asm 1: paddd <diag0=int6464#1,<a0=int6464#5
+# asm 2: paddd <diag0=%xmm0,<a0=%xmm4
+paddd %xmm0,%xmm4
+
+# qhasm: a1 = diag0
+# asm 1: movdqa <diag0=int6464#1,>a1=int6464#6
+# asm 2: movdqa <diag0=%xmm0,>a1=%xmm5
+movdqa %xmm0,%xmm5
+
+# qhasm: b0 = a0
+# asm 1: movdqa <a0=int6464#5,>b0=int6464#7
+# asm 2: movdqa <a0=%xmm4,>b0=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a0 <<= 7
+# asm 1: pslld $7,<a0=int6464#5
+# asm 2: pslld $7,<a0=%xmm4
+pslld $7,%xmm4
+
+# qhasm: uint32323232 b0 >>= 25
+# asm 1: psrld $25,<b0=int6464#7
+# asm 2: psrld $25,<b0=%xmm6
+psrld $25,%xmm6
+
+# qhasm: diag3 ^= a0
+# asm 1: pxor <a0=int6464#5,<diag3=int6464#4
+# asm 2: pxor <a0=%xmm4,<diag3=%xmm3
+pxor %xmm4,%xmm3
+
+# qhasm: diag3 ^= b0
+# asm 1: pxor <b0=int6464#7,<diag3=int6464#4
+# asm 2: pxor <b0=%xmm6,<diag3=%xmm3
+pxor %xmm6,%xmm3
+
+# qhasm: uint32323232 a1 += diag3
+# asm 1: paddd <diag3=int6464#4,<a1=int6464#6
+# asm 2: paddd <diag3=%xmm3,<a1=%xmm5
+paddd %xmm3,%xmm5
+
+# qhasm: a2 = diag3
+# asm 1: movdqa <diag3=int6464#4,>a2=int6464#5
+# asm 2: movdqa <diag3=%xmm3,>a2=%xmm4
+movdqa %xmm3,%xmm4
+
+# qhasm: b1 = a1
+# asm 1: movdqa <a1=int6464#6,>b1=int6464#7
+# asm 2: movdqa <a1=%xmm5,>b1=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a1 <<= 9
+# asm 1: pslld $9,<a1=int6464#6
+# asm 2: pslld $9,<a1=%xmm5
+pslld $9,%xmm5
+
+# qhasm: uint32323232 b1 >>= 23
+# asm 1: psrld $23,<b1=int6464#7
+# asm 2: psrld $23,<b1=%xmm6
+psrld $23,%xmm6
+
+# qhasm: diag2 ^= a1
+# asm 1: pxor <a1=int6464#6,<diag2=int6464#3
+# asm 2: pxor <a1=%xmm5,<diag2=%xmm2
+pxor %xmm5,%xmm2
+
+# qhasm: diag3 <<<= 32
+# asm 1: pshufd $0x93,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x93,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x93,%xmm3,%xmm3
+
+# qhasm: diag2 ^= b1
+# asm 1: pxor <b1=int6464#7,<diag2=int6464#3
+# asm 2: pxor <b1=%xmm6,<diag2=%xmm2
+pxor %xmm6,%xmm2
+
+# qhasm: uint32323232 a2 += diag2
+# asm 1: paddd <diag2=int6464#3,<a2=int6464#5
+# asm 2: paddd <diag2=%xmm2,<a2=%xmm4
+paddd %xmm2,%xmm4
+
+# qhasm: a3 = diag2
+# asm 1: movdqa <diag2=int6464#3,>a3=int6464#6
+# asm 2: movdqa <diag2=%xmm2,>a3=%xmm5
+movdqa %xmm2,%xmm5
+
+# qhasm: b2 = a2
+# asm 1: movdqa <a2=int6464#5,>b2=int6464#7
+# asm 2: movdqa <a2=%xmm4,>b2=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a2 <<= 13
+# asm 1: pslld $13,<a2=int6464#5
+# asm 2: pslld $13,<a2=%xmm4
+pslld $13,%xmm4
+
+# qhasm: uint32323232 b2 >>= 19
+# asm 1: psrld $19,<b2=int6464#7
+# asm 2: psrld $19,<b2=%xmm6
+psrld $19,%xmm6
+
+# qhasm: diag1 ^= a2
+# asm 1: pxor <a2=int6464#5,<diag1=int6464#2
+# asm 2: pxor <a2=%xmm4,<diag1=%xmm1
+pxor %xmm4,%xmm1
+
+# qhasm: diag2 <<<= 64
+# asm 1: pshufd $0x4e,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x4e,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x4e,%xmm2,%xmm2
+
+# qhasm: diag1 ^= b2
+# asm 1: pxor <b2=int6464#7,<diag1=int6464#2
+# asm 2: pxor <b2=%xmm6,<diag1=%xmm1
+pxor %xmm6,%xmm1
+
+# qhasm: uint32323232 a3 += diag1
+# asm 1: paddd <diag1=int6464#2,<a3=int6464#6
+# asm 2: paddd <diag1=%xmm1,<a3=%xmm5
+paddd %xmm1,%xmm5
+
+# qhasm: a4 = diag3
+# asm 1: movdqa <diag3=int6464#4,>a4=int6464#5
+# asm 2: movdqa <diag3=%xmm3,>a4=%xmm4
+movdqa %xmm3,%xmm4
+
+# qhasm: b3 = a3
+# asm 1: movdqa <a3=int6464#6,>b3=int6464#7
+# asm 2: movdqa <a3=%xmm5,>b3=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a3 <<= 18
+# asm 1: pslld $18,<a3=int6464#6
+# asm 2: pslld $18,<a3=%xmm5
+pslld $18,%xmm5
+
+# qhasm: uint32323232 b3 >>= 14
+# asm 1: psrld $14,<b3=int6464#7
+# asm 2: psrld $14,<b3=%xmm6
+psrld $14,%xmm6
+
+# qhasm: diag0 ^= a3
+# asm 1: pxor <a3=int6464#6,<diag0=int6464#1
+# asm 2: pxor <a3=%xmm5,<diag0=%xmm0
+pxor %xmm5,%xmm0
+
+# qhasm: diag1 <<<= 96
+# asm 1: pshufd $0x39,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x39,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: diag0 ^= b3
+# asm 1: pxor <b3=int6464#7,<diag0=int6464#1
+# asm 2: pxor <b3=%xmm6,<diag0=%xmm0
+pxor %xmm6,%xmm0
+
+# qhasm: uint32323232 a4 += diag0
+# asm 1: paddd <diag0=int6464#1,<a4=int6464#5
+# asm 2: paddd <diag0=%xmm0,<a4=%xmm4
+paddd %xmm0,%xmm4
+
+# qhasm: a5 = diag0
+# asm 1: movdqa <diag0=int6464#1,>a5=int6464#6
+# asm 2: movdqa <diag0=%xmm0,>a5=%xmm5
+movdqa %xmm0,%xmm5
+
+# qhasm: b4 = a4
+# asm 1: movdqa <a4=int6464#5,>b4=int6464#7
+# asm 2: movdqa <a4=%xmm4,>b4=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a4 <<= 7
+# asm 1: pslld $7,<a4=int6464#5
+# asm 2: pslld $7,<a4=%xmm4
+pslld $7,%xmm4
+
+# qhasm: uint32323232 b4 >>= 25
+# asm 1: psrld $25,<b4=int6464#7
+# asm 2: psrld $25,<b4=%xmm6
+psrld $25,%xmm6
+
+# qhasm: diag1 ^= a4
+# asm 1: pxor <a4=int6464#5,<diag1=int6464#2
+# asm 2: pxor <a4=%xmm4,<diag1=%xmm1
+pxor %xmm4,%xmm1
+
+# qhasm: diag1 ^= b4
+# asm 1: pxor <b4=int6464#7,<diag1=int6464#2
+# asm 2: pxor <b4=%xmm6,<diag1=%xmm1
+pxor %xmm6,%xmm1
+
+# qhasm: uint32323232 a5 += diag1
+# asm 1: paddd <diag1=int6464#2,<a5=int6464#6
+# asm 2: paddd <diag1=%xmm1,<a5=%xmm5
+paddd %xmm1,%xmm5
+
+# qhasm: a6 = diag1
+# asm 1: movdqa <diag1=int6464#2,>a6=int6464#5
+# asm 2: movdqa <diag1=%xmm1,>a6=%xmm4
+movdqa %xmm1,%xmm4
+
+# qhasm: b5 = a5
+# asm 1: movdqa <a5=int6464#6,>b5=int6464#7
+# asm 2: movdqa <a5=%xmm5,>b5=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a5 <<= 9
+# asm 1: pslld $9,<a5=int6464#6
+# asm 2: pslld $9,<a5=%xmm5
+pslld $9,%xmm5
+
+# qhasm: uint32323232 b5 >>= 23
+# asm 1: psrld $23,<b5=int6464#7
+# asm 2: psrld $23,<b5=%xmm6
+psrld $23,%xmm6
+
+# qhasm: diag2 ^= a5
+# asm 1: pxor <a5=int6464#6,<diag2=int6464#3
+# asm 2: pxor <a5=%xmm5,<diag2=%xmm2
+pxor %xmm5,%xmm2
+
+# qhasm: diag1 <<<= 32
+# asm 1: pshufd $0x93,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x93,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x93,%xmm1,%xmm1
+
+# qhasm: diag2 ^= b5
+# asm 1: pxor <b5=int6464#7,<diag2=int6464#3
+# asm 2: pxor <b5=%xmm6,<diag2=%xmm2
+pxor %xmm6,%xmm2
+
+# qhasm: uint32323232 a6 += diag2
+# asm 1: paddd <diag2=int6464#3,<a6=int6464#5
+# asm 2: paddd <diag2=%xmm2,<a6=%xmm4
+paddd %xmm2,%xmm4
+
+# qhasm: a7 = diag2
+# asm 1: movdqa <diag2=int6464#3,>a7=int6464#6
+# asm 2: movdqa <diag2=%xmm2,>a7=%xmm5
+movdqa %xmm2,%xmm5
+
+# qhasm: b6 = a6
+# asm 1: movdqa <a6=int6464#5,>b6=int6464#7
+# asm 2: movdqa <a6=%xmm4,>b6=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a6 <<= 13
+# asm 1: pslld $13,<a6=int6464#5
+# asm 2: pslld $13,<a6=%xmm4
+pslld $13,%xmm4
+
+# qhasm: uint32323232 b6 >>= 19
+# asm 1: psrld $19,<b6=int6464#7
+# asm 2: psrld $19,<b6=%xmm6
+psrld $19,%xmm6
+
+# qhasm: diag3 ^= a6
+# asm 1: pxor <a6=int6464#5,<diag3=int6464#4
+# asm 2: pxor <a6=%xmm4,<diag3=%xmm3
+pxor %xmm4,%xmm3
+
+# qhasm: diag2 <<<= 64
+# asm 1: pshufd $0x4e,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x4e,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x4e,%xmm2,%xmm2
+
+# qhasm: diag3 ^= b6
+# asm 1: pxor <b6=int6464#7,<diag3=int6464#4
+# asm 2: pxor <b6=%xmm6,<diag3=%xmm3
+pxor %xmm6,%xmm3
+
+# qhasm: uint32323232 a7 += diag3
+# asm 1: paddd <diag3=int6464#4,<a7=int6464#6
+# asm 2: paddd <diag3=%xmm3,<a7=%xmm5
+paddd %xmm3,%xmm5
+
+# qhasm: a0 = diag1
+# asm 1: movdqa <diag1=int6464#2,>a0=int6464#5
+# asm 2: movdqa <diag1=%xmm1,>a0=%xmm4
+movdqa %xmm1,%xmm4
+
+# qhasm: b7 = a7
+# asm 1: movdqa <a7=int6464#6,>b7=int6464#7
+# asm 2: movdqa <a7=%xmm5,>b7=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a7 <<= 18
+# asm 1: pslld $18,<a7=int6464#6
+# asm 2: pslld $18,<a7=%xmm5
+pslld $18,%xmm5
+
+# qhasm: uint32323232 b7 >>= 14
+# asm 1: psrld $14,<b7=int6464#7
+# asm 2: psrld $14,<b7=%xmm6
+psrld $14,%xmm6
+
+# qhasm: diag0 ^= a7
+# asm 1: pxor <a7=int6464#6,<diag0=int6464#1
+# asm 2: pxor <a7=%xmm5,<diag0=%xmm0
+pxor %xmm5,%xmm0
+
+# qhasm: diag3 <<<= 96
+# asm 1: pshufd $0x39,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x39,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: diag0 ^= b7
+# asm 1: pxor <b7=int6464#7,<diag0=int6464#1
+# asm 2: pxor <b7=%xmm6,<diag0=%xmm0
+pxor %xmm6,%xmm0
+
+# qhasm: uint32323232 a0 += diag0
+# asm 1: paddd <diag0=int6464#1,<a0=int6464#5
+# asm 2: paddd <diag0=%xmm0,<a0=%xmm4
+paddd %xmm0,%xmm4
+
+# qhasm: a1 = diag0
+# asm 1: movdqa <diag0=int6464#1,>a1=int6464#6
+# asm 2: movdqa <diag0=%xmm0,>a1=%xmm5
+movdqa %xmm0,%xmm5
+
+# qhasm: b0 = a0
+# asm 1: movdqa <a0=int6464#5,>b0=int6464#7
+# asm 2: movdqa <a0=%xmm4,>b0=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a0 <<= 7
+# asm 1: pslld $7,<a0=int6464#5
+# asm 2: pslld $7,<a0=%xmm4
+pslld $7,%xmm4
+
+# qhasm: uint32323232 b0 >>= 25
+# asm 1: psrld $25,<b0=int6464#7
+# asm 2: psrld $25,<b0=%xmm6
+psrld $25,%xmm6
+
+# qhasm: diag3 ^= a0
+# asm 1: pxor <a0=int6464#5,<diag3=int6464#4
+# asm 2: pxor <a0=%xmm4,<diag3=%xmm3
+pxor %xmm4,%xmm3
+
+# qhasm: diag3 ^= b0
+# asm 1: pxor <b0=int6464#7,<diag3=int6464#4
+# asm 2: pxor <b0=%xmm6,<diag3=%xmm3
+pxor %xmm6,%xmm3
+
+# qhasm: uint32323232 a1 += diag3
+# asm 1: paddd <diag3=int6464#4,<a1=int6464#6
+# asm 2: paddd <diag3=%xmm3,<a1=%xmm5
+paddd %xmm3,%xmm5
+
+# qhasm: a2 = diag3
+# asm 1: movdqa <diag3=int6464#4,>a2=int6464#5
+# asm 2: movdqa <diag3=%xmm3,>a2=%xmm4
+movdqa %xmm3,%xmm4
+
+# qhasm: b1 = a1
+# asm 1: movdqa <a1=int6464#6,>b1=int6464#7
+# asm 2: movdqa <a1=%xmm5,>b1=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a1 <<= 9
+# asm 1: pslld $9,<a1=int6464#6
+# asm 2: pslld $9,<a1=%xmm5
+pslld $9,%xmm5
+
+# qhasm: uint32323232 b1 >>= 23
+# asm 1: psrld $23,<b1=int6464#7
+# asm 2: psrld $23,<b1=%xmm6
+psrld $23,%xmm6
+
+# qhasm: diag2 ^= a1
+# asm 1: pxor <a1=int6464#6,<diag2=int6464#3
+# asm 2: pxor <a1=%xmm5,<diag2=%xmm2
+pxor %xmm5,%xmm2
+
+# qhasm: diag3 <<<= 32
+# asm 1: pshufd $0x93,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x93,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x93,%xmm3,%xmm3
+
+# qhasm: diag2 ^= b1
+# asm 1: pxor <b1=int6464#7,<diag2=int6464#3
+# asm 2: pxor <b1=%xmm6,<diag2=%xmm2
+pxor %xmm6,%xmm2
+
+# qhasm: uint32323232 a2 += diag2
+# asm 1: paddd <diag2=int6464#3,<a2=int6464#5
+# asm 2: paddd <diag2=%xmm2,<a2=%xmm4
+paddd %xmm2,%xmm4
+
+# qhasm: a3 = diag2
+# asm 1: movdqa <diag2=int6464#3,>a3=int6464#6
+# asm 2: movdqa <diag2=%xmm2,>a3=%xmm5
+movdqa %xmm2,%xmm5
+
+# qhasm: b2 = a2
+# asm 1: movdqa <a2=int6464#5,>b2=int6464#7
+# asm 2: movdqa <a2=%xmm4,>b2=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a2 <<= 13
+# asm 1: pslld $13,<a2=int6464#5
+# asm 2: pslld $13,<a2=%xmm4
+pslld $13,%xmm4
+
+# qhasm: uint32323232 b2 >>= 19
+# asm 1: psrld $19,<b2=int6464#7
+# asm 2: psrld $19,<b2=%xmm6
+psrld $19,%xmm6
+
+# qhasm: diag1 ^= a2
+# asm 1: pxor <a2=int6464#5,<diag1=int6464#2
+# asm 2: pxor <a2=%xmm4,<diag1=%xmm1
+pxor %xmm4,%xmm1
+
+# qhasm: diag2 <<<= 64
+# asm 1: pshufd $0x4e,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x4e,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x4e,%xmm2,%xmm2
+
+# qhasm: diag1 ^= b2
+# asm 1: pxor <b2=int6464#7,<diag1=int6464#2
+# asm 2: pxor <b2=%xmm6,<diag1=%xmm1
+pxor %xmm6,%xmm1
+
+# qhasm: uint32323232 a3 += diag1
+# asm 1: paddd <diag1=int6464#2,<a3=int6464#6
+# asm 2: paddd <diag1=%xmm1,<a3=%xmm5
+paddd %xmm1,%xmm5
+
+# qhasm: a4 = diag3
+# asm 1: movdqa <diag3=int6464#4,>a4=int6464#5
+# asm 2: movdqa <diag3=%xmm3,>a4=%xmm4
+movdqa %xmm3,%xmm4
+
+# qhasm: b3 = a3
+# asm 1: movdqa <a3=int6464#6,>b3=int6464#7
+# asm 2: movdqa <a3=%xmm5,>b3=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a3 <<= 18
+# asm 1: pslld $18,<a3=int6464#6
+# asm 2: pslld $18,<a3=%xmm5
+pslld $18,%xmm5
+
+# qhasm: uint32323232 b3 >>= 14
+# asm 1: psrld $14,<b3=int6464#7
+# asm 2: psrld $14,<b3=%xmm6
+psrld $14,%xmm6
+
+# qhasm: diag0 ^= a3
+# asm 1: pxor <a3=int6464#6,<diag0=int6464#1
+# asm 2: pxor <a3=%xmm5,<diag0=%xmm0
+pxor %xmm5,%xmm0
+
+# qhasm: diag1 <<<= 96
+# asm 1: pshufd $0x39,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x39,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: diag0 ^= b3
+# asm 1: pxor <b3=int6464#7,<diag0=int6464#1
+# asm 2: pxor <b3=%xmm6,<diag0=%xmm0
+pxor %xmm6,%xmm0
+
+# qhasm: uint32323232 a4 += diag0
+# asm 1: paddd <diag0=int6464#1,<a4=int6464#5
+# asm 2: paddd <diag0=%xmm0,<a4=%xmm4
+paddd %xmm0,%xmm4
+
+# qhasm: a5 = diag0
+# asm 1: movdqa <diag0=int6464#1,>a5=int6464#6
+# asm 2: movdqa <diag0=%xmm0,>a5=%xmm5
+movdqa %xmm0,%xmm5
+
+# qhasm: b4 = a4
+# asm 1: movdqa <a4=int6464#5,>b4=int6464#7
+# asm 2: movdqa <a4=%xmm4,>b4=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a4 <<= 7
+# asm 1: pslld $7,<a4=int6464#5
+# asm 2: pslld $7,<a4=%xmm4
+pslld $7,%xmm4
+
+# qhasm: uint32323232 b4 >>= 25
+# asm 1: psrld $25,<b4=int6464#7
+# asm 2: psrld $25,<b4=%xmm6
+psrld $25,%xmm6
+
+# qhasm: diag1 ^= a4
+# asm 1: pxor <a4=int6464#5,<diag1=int6464#2
+# asm 2: pxor <a4=%xmm4,<diag1=%xmm1
+pxor %xmm4,%xmm1
+
+# qhasm: diag1 ^= b4
+# asm 1: pxor <b4=int6464#7,<diag1=int6464#2
+# asm 2: pxor <b4=%xmm6,<diag1=%xmm1
+pxor %xmm6,%xmm1
+
+# qhasm: uint32323232 a5 += diag1
+# asm 1: paddd <diag1=int6464#2,<a5=int6464#6
+# asm 2: paddd <diag1=%xmm1,<a5=%xmm5
+paddd %xmm1,%xmm5
+
+# qhasm: a6 = diag1
+# asm 1: movdqa <diag1=int6464#2,>a6=int6464#5
+# asm 2: movdqa <diag1=%xmm1,>a6=%xmm4
+movdqa %xmm1,%xmm4
+
+# qhasm: b5 = a5
+# asm 1: movdqa <a5=int6464#6,>b5=int6464#7
+# asm 2: movdqa <a5=%xmm5,>b5=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a5 <<= 9
+# asm 1: pslld $9,<a5=int6464#6
+# asm 2: pslld $9,<a5=%xmm5
+pslld $9,%xmm5
+
+# qhasm: uint32323232 b5 >>= 23
+# asm 1: psrld $23,<b5=int6464#7
+# asm 2: psrld $23,<b5=%xmm6
+psrld $23,%xmm6
+
+# qhasm: diag2 ^= a5
+# asm 1: pxor <a5=int6464#6,<diag2=int6464#3
+# asm 2: pxor <a5=%xmm5,<diag2=%xmm2
+pxor %xmm5,%xmm2
+
+# qhasm: diag1 <<<= 32
+# asm 1: pshufd $0x93,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x93,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x93,%xmm1,%xmm1
+
+# qhasm: diag2 ^= b5
+# asm 1: pxor <b5=int6464#7,<diag2=int6464#3
+# asm 2: pxor <b5=%xmm6,<diag2=%xmm2
+pxor %xmm6,%xmm2
+
+# qhasm: uint32323232 a6 += diag2
+# asm 1: paddd <diag2=int6464#3,<a6=int6464#5
+# asm 2: paddd <diag2=%xmm2,<a6=%xmm4
+paddd %xmm2,%xmm4
+
+# qhasm: a7 = diag2
+# asm 1: movdqa <diag2=int6464#3,>a7=int6464#6
+# asm 2: movdqa <diag2=%xmm2,>a7=%xmm5
+movdqa %xmm2,%xmm5
+
+# qhasm: b6 = a6
+# asm 1: movdqa <a6=int6464#5,>b6=int6464#7
+# asm 2: movdqa <a6=%xmm4,>b6=%xmm6
+movdqa %xmm4,%xmm6
+
+# qhasm: uint32323232 a6 <<= 13
+# asm 1: pslld $13,<a6=int6464#5
+# asm 2: pslld $13,<a6=%xmm4
+pslld $13,%xmm4
+
+# qhasm: uint32323232 b6 >>= 19
+# asm 1: psrld $19,<b6=int6464#7
+# asm 2: psrld $19,<b6=%xmm6
+psrld $19,%xmm6
+
+# qhasm: diag3 ^= a6
+# asm 1: pxor <a6=int6464#5,<diag3=int6464#4
+# asm 2: pxor <a6=%xmm4,<diag3=%xmm3
+pxor %xmm4,%xmm3
+
+# qhasm: diag2 <<<= 64
+# asm 1: pshufd $0x4e,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x4e,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x4e,%xmm2,%xmm2
+
+# qhasm: diag3 ^= b6
+# asm 1: pxor <b6=int6464#7,<diag3=int6464#4
+# asm 2: pxor <b6=%xmm6,<diag3=%xmm3
+pxor %xmm6,%xmm3
+
+# qhasm: unsigned>? i -= 4
+# asm 1: sub $4,<i=int64#4
+# asm 2: sub $4,<i=%rcx
+sub $4,%rcx
+
+# qhasm: uint32323232 a7 += diag3
+# asm 1: paddd <diag3=int6464#4,<a7=int6464#6
+# asm 2: paddd <diag3=%xmm3,<a7=%xmm5
+paddd %xmm3,%xmm5
+
+# qhasm: a0 = diag1
+# asm 1: movdqa <diag1=int6464#2,>a0=int6464#5
+# asm 2: movdqa <diag1=%xmm1,>a0=%xmm4
+movdqa %xmm1,%xmm4
+
+# qhasm: b7 = a7
+# asm 1: movdqa <a7=int6464#6,>b7=int6464#7
+# asm 2: movdqa <a7=%xmm5,>b7=%xmm6
+movdqa %xmm5,%xmm6
+
+# qhasm: uint32323232 a7 <<= 18
+# asm 1: pslld $18,<a7=int6464#6
+# asm 2: pslld $18,<a7=%xmm5
+pslld $18,%xmm5
+
+# qhasm: b0 = 0
+# asm 1: pxor >b0=int6464#8,>b0=int6464#8
+# asm 2: pxor >b0=%xmm7,>b0=%xmm7
+pxor %xmm7,%xmm7
+
+# qhasm: uint32323232 b7 >>= 14
+# asm 1: psrld $14,<b7=int6464#7
+# asm 2: psrld $14,<b7=%xmm6
+psrld $14,%xmm6
+
+# qhasm: diag0 ^= a7
+# asm 1: pxor <a7=int6464#6,<diag0=int6464#1
+# asm 2: pxor <a7=%xmm5,<diag0=%xmm0
+pxor %xmm5,%xmm0
+
+# qhasm: diag3 <<<= 96
+# asm 1: pshufd $0x39,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x39,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: diag0 ^= b7
+# asm 1: pxor <b7=int6464#7,<diag0=int6464#1
+# asm 2: pxor <b7=%xmm6,<diag0=%xmm0
+pxor %xmm6,%xmm0
+# comment:fp stack unchanged by jump
+
+# qhasm: goto mainloop2 if unsigned>
+ja ._mainloop2
+
+# qhasm: uint32323232 diag0 += x0
+# asm 1: paddd <x0=stack128#4,<diag0=int6464#1
+# asm 2: paddd <x0=48(%rsp),<diag0=%xmm0
+paddd 48(%rsp),%xmm0
+
+# qhasm: uint32323232 diag1 += x1
+# asm 1: paddd <x1=stack128#1,<diag1=int6464#2
+# asm 2: paddd <x1=0(%rsp),<diag1=%xmm1
+paddd 0(%rsp),%xmm1
+
+# qhasm: uint32323232 diag2 += x2
+# asm 1: paddd <x2=stack128#2,<diag2=int6464#3
+# asm 2: paddd <x2=16(%rsp),<diag2=%xmm2
+paddd 16(%rsp),%xmm2
+
+# qhasm: uint32323232 diag3 += x3
+# asm 1: paddd <x3=stack128#3,<diag3=int6464#4
+# asm 2: paddd <x3=32(%rsp),<diag3=%xmm3
+paddd 32(%rsp),%xmm3
+
+# qhasm: in0 = diag0
+# asm 1: movd <diag0=int6464#1,>in0=int64#4
+# asm 2: movd <diag0=%xmm0,>in0=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in12 = diag1
+# asm 1: movd <diag1=int6464#2,>in12=int64#5
+# asm 2: movd <diag1=%xmm1,>in12=%r8
+movd %xmm1,%r8
+
+# qhasm: in8 = diag2
+# asm 1: movd <diag2=int6464#3,>in8=int64#6
+# asm 2: movd <diag2=%xmm2,>in8=%r9
+movd %xmm2,%r9
+
+# qhasm: in4 = diag3
+# asm 1: movd <diag3=int6464#4,>in4=int64#7
+# asm 2: movd <diag3=%xmm3,>in4=%rax
+movd %xmm3,%rax
+
+# qhasm: diag0 <<<= 96
+# asm 1: pshufd $0x39,<diag0=int6464#1,<diag0=int6464#1
+# asm 2: pshufd $0x39,<diag0=%xmm0,<diag0=%xmm0
+pshufd $0x39,%xmm0,%xmm0
+
+# qhasm: diag1 <<<= 96
+# asm 1: pshufd $0x39,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x39,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: diag2 <<<= 96
+# asm 1: pshufd $0x39,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x39,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x39,%xmm2,%xmm2
+
+# qhasm: diag3 <<<= 96
+# asm 1: pshufd $0x39,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x39,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: (uint32) in0 ^= *(uint32 *) (m + 0)
+# asm 1: xorl 0(<m=int64#2),<in0=int64#4d
+# asm 2: xorl 0(<m=%rsi),<in0=%ecx
+xorl 0(%rsi),%ecx
+
+# qhasm: (uint32) in12 ^= *(uint32 *) (m + 48)
+# asm 1: xorl 48(<m=int64#2),<in12=int64#5d
+# asm 2: xorl 48(<m=%rsi),<in12=%r8d
+xorl 48(%rsi),%r8d
+
+# qhasm: (uint32) in8 ^= *(uint32 *) (m + 32)
+# asm 1: xorl 32(<m=int64#2),<in8=int64#6d
+# asm 2: xorl 32(<m=%rsi),<in8=%r9d
+xorl 32(%rsi),%r9d
+
+# qhasm: (uint32) in4 ^= *(uint32 *) (m + 16)
+# asm 1: xorl 16(<m=int64#2),<in4=int64#7d
+# asm 2: xorl 16(<m=%rsi),<in4=%eax
+xorl 16(%rsi),%eax
+
+# qhasm: *(uint32 *) (out + 0) = in0
+# asm 1: movl <in0=int64#4d,0(<out=int64#1)
+# asm 2: movl <in0=%ecx,0(<out=%rdi)
+movl %ecx,0(%rdi)
+
+# qhasm: *(uint32 *) (out + 48) = in12
+# asm 1: movl <in12=int64#5d,48(<out=int64#1)
+# asm 2: movl <in12=%r8d,48(<out=%rdi)
+movl %r8d,48(%rdi)
+
+# qhasm: *(uint32 *) (out + 32) = in8
+# asm 1: movl <in8=int64#6d,32(<out=int64#1)
+# asm 2: movl <in8=%r9d,32(<out=%rdi)
+movl %r9d,32(%rdi)
+
+# qhasm: *(uint32 *) (out + 16) = in4
+# asm 1: movl <in4=int64#7d,16(<out=int64#1)
+# asm 2: movl <in4=%eax,16(<out=%rdi)
+movl %eax,16(%rdi)
+
+# qhasm: in5 = diag0
+# asm 1: movd <diag0=int6464#1,>in5=int64#4
+# asm 2: movd <diag0=%xmm0,>in5=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in1 = diag1
+# asm 1: movd <diag1=int6464#2,>in1=int64#5
+# asm 2: movd <diag1=%xmm1,>in1=%r8
+movd %xmm1,%r8
+
+# qhasm: in13 = diag2
+# asm 1: movd <diag2=int6464#3,>in13=int64#6
+# asm 2: movd <diag2=%xmm2,>in13=%r9
+movd %xmm2,%r9
+
+# qhasm: in9 = diag3
+# asm 1: movd <diag3=int6464#4,>in9=int64#7
+# asm 2: movd <diag3=%xmm3,>in9=%rax
+movd %xmm3,%rax
+
+# qhasm: diag0 <<<= 96
+# asm 1: pshufd $0x39,<diag0=int6464#1,<diag0=int6464#1
+# asm 2: pshufd $0x39,<diag0=%xmm0,<diag0=%xmm0
+pshufd $0x39,%xmm0,%xmm0
+
+# qhasm: diag1 <<<= 96
+# asm 1: pshufd $0x39,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x39,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: diag2 <<<= 96
+# asm 1: pshufd $0x39,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x39,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x39,%xmm2,%xmm2
+
+# qhasm: diag3 <<<= 96
+# asm 1: pshufd $0x39,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x39,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: (uint32) in5 ^= *(uint32 *) (m + 20)
+# asm 1: xorl 20(<m=int64#2),<in5=int64#4d
+# asm 2: xorl 20(<m=%rsi),<in5=%ecx
+xorl 20(%rsi),%ecx
+
+# qhasm: (uint32) in1 ^= *(uint32 *) (m + 4)
+# asm 1: xorl 4(<m=int64#2),<in1=int64#5d
+# asm 2: xorl 4(<m=%rsi),<in1=%r8d
+xorl 4(%rsi),%r8d
+
+# qhasm: (uint32) in13 ^= *(uint32 *) (m + 52)
+# asm 1: xorl 52(<m=int64#2),<in13=int64#6d
+# asm 2: xorl 52(<m=%rsi),<in13=%r9d
+xorl 52(%rsi),%r9d
+
+# qhasm: (uint32) in9 ^= *(uint32 *) (m + 36)
+# asm 1: xorl 36(<m=int64#2),<in9=int64#7d
+# asm 2: xorl 36(<m=%rsi),<in9=%eax
+xorl 36(%rsi),%eax
+
+# qhasm: *(uint32 *) (out + 20) = in5
+# asm 1: movl <in5=int64#4d,20(<out=int64#1)
+# asm 2: movl <in5=%ecx,20(<out=%rdi)
+movl %ecx,20(%rdi)
+
+# qhasm: *(uint32 *) (out + 4) = in1
+# asm 1: movl <in1=int64#5d,4(<out=int64#1)
+# asm 2: movl <in1=%r8d,4(<out=%rdi)
+movl %r8d,4(%rdi)
+
+# qhasm: *(uint32 *) (out + 52) = in13
+# asm 1: movl <in13=int64#6d,52(<out=int64#1)
+# asm 2: movl <in13=%r9d,52(<out=%rdi)
+movl %r9d,52(%rdi)
+
+# qhasm: *(uint32 *) (out + 36) = in9
+# asm 1: movl <in9=int64#7d,36(<out=int64#1)
+# asm 2: movl <in9=%eax,36(<out=%rdi)
+movl %eax,36(%rdi)
+
+# qhasm: in10 = diag0
+# asm 1: movd <diag0=int6464#1,>in10=int64#4
+# asm 2: movd <diag0=%xmm0,>in10=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in6 = diag1
+# asm 1: movd <diag1=int6464#2,>in6=int64#5
+# asm 2: movd <diag1=%xmm1,>in6=%r8
+movd %xmm1,%r8
+
+# qhasm: in2 = diag2
+# asm 1: movd <diag2=int6464#3,>in2=int64#6
+# asm 2: movd <diag2=%xmm2,>in2=%r9
+movd %xmm2,%r9
+
+# qhasm: in14 = diag3
+# asm 1: movd <diag3=int6464#4,>in14=int64#7
+# asm 2: movd <diag3=%xmm3,>in14=%rax
+movd %xmm3,%rax
+
+# qhasm: diag0 <<<= 96
+# asm 1: pshufd $0x39,<diag0=int6464#1,<diag0=int6464#1
+# asm 2: pshufd $0x39,<diag0=%xmm0,<diag0=%xmm0
+pshufd $0x39,%xmm0,%xmm0
+
+# qhasm: diag1 <<<= 96
+# asm 1: pshufd $0x39,<diag1=int6464#2,<diag1=int6464#2
+# asm 2: pshufd $0x39,<diag1=%xmm1,<diag1=%xmm1
+pshufd $0x39,%xmm1,%xmm1
+
+# qhasm: diag2 <<<= 96
+# asm 1: pshufd $0x39,<diag2=int6464#3,<diag2=int6464#3
+# asm 2: pshufd $0x39,<diag2=%xmm2,<diag2=%xmm2
+pshufd $0x39,%xmm2,%xmm2
+
+# qhasm: diag3 <<<= 96
+# asm 1: pshufd $0x39,<diag3=int6464#4,<diag3=int6464#4
+# asm 2: pshufd $0x39,<diag3=%xmm3,<diag3=%xmm3
+pshufd $0x39,%xmm3,%xmm3
+
+# qhasm: (uint32) in10 ^= *(uint32 *) (m + 40)
+# asm 1: xorl 40(<m=int64#2),<in10=int64#4d
+# asm 2: xorl 40(<m=%rsi),<in10=%ecx
+xorl 40(%rsi),%ecx
+
+# qhasm: (uint32) in6 ^= *(uint32 *) (m + 24)
+# asm 1: xorl 24(<m=int64#2),<in6=int64#5d
+# asm 2: xorl 24(<m=%rsi),<in6=%r8d
+xorl 24(%rsi),%r8d
+
+# qhasm: (uint32) in2 ^= *(uint32 *) (m + 8)
+# asm 1: xorl 8(<m=int64#2),<in2=int64#6d
+# asm 2: xorl 8(<m=%rsi),<in2=%r9d
+xorl 8(%rsi),%r9d
+
+# qhasm: (uint32) in14 ^= *(uint32 *) (m + 56)
+# asm 1: xorl 56(<m=int64#2),<in14=int64#7d
+# asm 2: xorl 56(<m=%rsi),<in14=%eax
+xorl 56(%rsi),%eax
+
+# qhasm: *(uint32 *) (out + 40) = in10
+# asm 1: movl <in10=int64#4d,40(<out=int64#1)
+# asm 2: movl <in10=%ecx,40(<out=%rdi)
+movl %ecx,40(%rdi)
+
+# qhasm: *(uint32 *) (out + 24) = in6
+# asm 1: movl <in6=int64#5d,24(<out=int64#1)
+# asm 2: movl <in6=%r8d,24(<out=%rdi)
+movl %r8d,24(%rdi)
+
+# qhasm: *(uint32 *) (out + 8) = in2
+# asm 1: movl <in2=int64#6d,8(<out=int64#1)
+# asm 2: movl <in2=%r9d,8(<out=%rdi)
+movl %r9d,8(%rdi)
+
+# qhasm: *(uint32 *) (out + 56) = in14
+# asm 1: movl <in14=int64#7d,56(<out=int64#1)
+# asm 2: movl <in14=%eax,56(<out=%rdi)
+movl %eax,56(%rdi)
+
+# qhasm: in15 = diag0
+# asm 1: movd <diag0=int6464#1,>in15=int64#4
+# asm 2: movd <diag0=%xmm0,>in15=%rcx
+movd %xmm0,%rcx
+
+# qhasm: in11 = diag1
+# asm 1: movd <diag1=int6464#2,>in11=int64#5
+# asm 2: movd <diag1=%xmm1,>in11=%r8
+movd %xmm1,%r8
+
+# qhasm: in7 = diag2
+# asm 1: movd <diag2=int6464#3,>in7=int64#6
+# asm 2: movd <diag2=%xmm2,>in7=%r9
+movd %xmm2,%r9
+
+# qhasm: in3 = diag3
+# asm 1: movd <diag3=int6464#4,>in3=int64#7
+# asm 2: movd <diag3=%xmm3,>in3=%rax
+movd %xmm3,%rax
+
+# qhasm: (uint32) in15 ^= *(uint32 *) (m + 60)
+# asm 1: xorl 60(<m=int64#2),<in15=int64#4d
+# asm 2: xorl 60(<m=%rsi),<in15=%ecx
+xorl 60(%rsi),%ecx
+
+# qhasm: (uint32) in11 ^= *(uint32 *) (m + 44)
+# asm 1: xorl 44(<m=int64#2),<in11=int64#5d
+# asm 2: xorl 44(<m=%rsi),<in11=%r8d
+xorl 44(%rsi),%r8d
+
+# qhasm: (uint32) in7 ^= *(uint32 *) (m + 28)
+# asm 1: xorl 28(<m=int64#2),<in7=int64#6d
+# asm 2: xorl 28(<m=%rsi),<in7=%r9d
+xorl 28(%rsi),%r9d
+
+# qhasm: (uint32) in3 ^= *(uint32 *) (m + 12)
+# asm 1: xorl 12(<m=int64#2),<in3=int64#7d
+# asm 2: xorl 12(<m=%rsi),<in3=%eax
+xorl 12(%rsi),%eax
+
+# qhasm: *(uint32 *) (out + 60) = in15
+# asm 1: movl <in15=int64#4d,60(<out=int64#1)
+# asm 2: movl <in15=%ecx,60(<out=%rdi)
+movl %ecx,60(%rdi)
+
+# qhasm: *(uint32 *) (out + 44) = in11
+# asm 1: movl <in11=int64#5d,44(<out=int64#1)
+# asm 2: movl <in11=%r8d,44(<out=%rdi)
+movl %r8d,44(%rdi)
+
+# qhasm: *(uint32 *) (out + 28) = in7
+# asm 1: movl <in7=int64#6d,28(<out=int64#1)
+# asm 2: movl <in7=%r9d,28(<out=%rdi)
+movl %r9d,28(%rdi)
+
+# qhasm: *(uint32 *) (out + 12) = in3
+# asm 1: movl <in3=int64#7d,12(<out=int64#1)
+# asm 2: movl <in3=%eax,12(<out=%rdi)
+movl %eax,12(%rdi)
+
+# qhasm: bytes = bytes_backup
+# asm 1: movq <bytes_backup=stack64#8,>bytes=int64#6
+# asm 2: movq <bytes_backup=408(%rsp),>bytes=%r9
+movq 408(%rsp),%r9
+
+# qhasm: in8 = ((uint32 *)&x2)[0]
+# asm 1: movl <x2=stack128#2,>in8=int64#4d
+# asm 2: movl <x2=16(%rsp),>in8=%ecx
+movl 16(%rsp),%ecx
+
+# qhasm: in9 = ((uint32 *)&x3)[1]
+# asm 1: movl 4+<x3=stack128#3,>in9=int64#5d
+# asm 2: movl 4+<x3=32(%rsp),>in9=%r8d
+movl 4+32(%rsp),%r8d
+
+# qhasm: in8 += 1
+# asm 1: add $1,<in8=int64#4
+# asm 2: add $1,<in8=%rcx
+add $1,%rcx
+
+# qhasm: in9 <<= 32
+# asm 1: shl $32,<in9=int64#5
+# asm 2: shl $32,<in9=%r8
+shl $32,%r8
+
+# qhasm: in8 += in9
+# asm 1: add <in9=int64#5,<in8=int64#4
+# asm 2: add <in9=%r8,<in8=%rcx
+add %r8,%rcx
+
+# qhasm: in9 = in8
+# asm 1: mov <in8=int64#4,>in9=int64#5
+# asm 2: mov <in8=%rcx,>in9=%r8
+mov %rcx,%r8
+
+# qhasm: (uint64) in9 >>= 32
+# asm 1: shr $32,<in9=int64#5
+# asm 2: shr $32,<in9=%r8
+shr $32,%r8
+
+# qhasm: ((uint32 *)&x2)[0] = in8
+# asm 1: movl <in8=int64#4d,>x2=stack128#2
+# asm 2: movl <in8=%ecx,>x2=16(%rsp)
+movl %ecx,16(%rsp)
+
+# qhasm: ((uint32 *)&x3)[1] = in9
+# asm 1: movl <in9=int64#5d,4+<x3=stack128#3
+# asm 2: movl <in9=%r8d,4+<x3=32(%rsp)
+movl %r8d,4+32(%rsp)
+
+# qhasm: unsigned>? unsigned<? bytes - 64
+# asm 1: cmp $64,<bytes=int64#6
+# asm 2: cmp $64,<bytes=%r9
+cmp $64,%r9
+# comment:fp stack unchanged by jump
+
+# qhasm: goto bytesatleast65 if unsigned>
+ja ._bytesatleast65
+# comment:fp stack unchanged by jump
+
+# qhasm: goto bytesatleast64 if !unsigned<
+jae ._bytesatleast64
+
+# qhasm: m = out
+# asm 1: mov <out=int64#1,>m=int64#2
+# asm 2: mov <out=%rdi,>m=%rsi
+mov %rdi,%rsi
+
+# qhasm: out = ctarget
+# asm 1: mov <ctarget=int64#3,>out=int64#1
+# asm 2: mov <ctarget=%rdx,>out=%rdi
+mov %rdx,%rdi
+
+# qhasm: i = bytes
+# asm 1: mov <bytes=int64#6,>i=int64#4
+# asm 2: mov <bytes=%r9,>i=%rcx
+mov %r9,%rcx
+
+# qhasm: while (i) { *out++ = *m++; --i }
+rep movsb
+# comment:fp stack unchanged by fallthrough
+
+# qhasm: bytesatleast64:
+._bytesatleast64:
+# comment:fp stack unchanged by fallthrough
+
+# qhasm: done:
+._done:
+
+# qhasm: r11_caller = r11_stack
+# asm 1: movq <r11_stack=stack64#1,>r11_caller=int64#9
+# asm 2: movq <r11_stack=352(%rsp),>r11_caller=%r11
+movq 352(%rsp),%r11
+
+# qhasm: r12_caller = r12_stack
+# asm 1: movq <r12_stack=stack64#2,>r12_caller=int64#10
+# asm 2: movq <r12_stack=360(%rsp),>r12_caller=%r12
+movq 360(%rsp),%r12
+
+# qhasm: r13_caller = r13_stack
+# asm 1: movq <r13_stack=stack64#3,>r13_caller=int64#11
+# asm 2: movq <r13_stack=368(%rsp),>r13_caller=%r13
+movq 368(%rsp),%r13
+
+# qhasm: r14_caller = r14_stack
+# asm 1: movq <r14_stack=stack64#4,>r14_caller=int64#12
+# asm 2: movq <r14_stack=376(%rsp),>r14_caller=%r14
+movq 376(%rsp),%r14
+
+# qhasm: r15_caller = r15_stack
+# asm 1: movq <r15_stack=stack64#5,>r15_caller=int64#13
+# asm 2: movq <r15_stack=384(%rsp),>r15_caller=%r15
+movq 384(%rsp),%r15
+
+# qhasm: rbx_caller = rbx_stack
+# asm 1: movq <rbx_stack=stack64#6,>rbx_caller=int64#14
+# asm 2: movq <rbx_stack=392(%rsp),>rbx_caller=%rbx
+movq 392(%rsp),%rbx
+
+# qhasm: rbp_caller = rbp_stack
+# asm 1: movq <rbp_stack=stack64#7,>rbp_caller=int64#15
+# asm 2: movq <rbp_stack=400(%rsp),>rbp_caller=%rbp
+movq 400(%rsp),%rbp
+
+# qhasm: leave
+add %r11,%rsp
+xor %rax,%rax
+xor %rdx,%rdx
+ret
+
+# qhasm: bytesatleast65:
+._bytesatleast65:
+
+# qhasm: bytes -= 64
+# asm 1: sub $64,<bytes=int64#6
+# asm 2: sub $64,<bytes=%r9
+sub $64,%r9
+
+# qhasm: out += 64
+# asm 1: add $64,<out=int64#1
+# asm 2: add $64,<out=%rdi
+add $64,%rdi
+
+# qhasm: m += 64
+# asm 1: add $64,<m=int64#2
+# asm 2: add $64,<m=%rsi
+add $64,%rsi
+# comment:fp stack unchanged by jump
+
+# qhasm: goto bytesbetween1and255
+jmp ._bytesbetween1and255
diff --git a/include/ZeroTierDebug.h b/include/ZeroTierDebug.h
new file mode 100644
index 00000000..8e5366f0
--- /dev/null
+++ b/include/ZeroTierDebug.h
@@ -0,0 +1,114 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+/**
+ * @file
+ *
+ * Debug macros
+ */
+
+#ifndef ZT_DEBUG_H
+#define ZT_DEBUG_H
+
+#if defined(__linux__) || defined(__APPLE__)
+#include <sys/syscall.h>
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+#define ZT_MSG_INFO true
+#define ZT_COLOR true
+
+// Debug output colors
+#if defined(__APPLE__)
+ #include "TargetConditionals.h"
+#endif
+#if defined(ZT_COLOR) && !defined(_WIN32) && !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE) && !defined(TARGET_IPHONE_SIMULATOR) && !defined(__APP_FRAMEWORK__)
+ #define ZT_RED "\x1B[31m"
+ #define ZT_GRN "\x1B[32m"
+ #define ZT_YEL "\x1B[33m"
+ #define ZT_BLU "\x1B[34m"
+ #define ZT_MAG "\x1B[35m"
+ #define ZT_CYN "\x1B[36m"
+ #define ZT_WHT "\x1B[37m"
+ #define ZT_RESET "\x1B[0m"
+#else
+ #define ZT_RED
+ #define ZT_GRN
+ #define ZT_YEL
+ #define ZT_BLU
+ #define ZT_MAG
+ #define ZT_CYN
+ #define ZT_WHT
+ #define ZT_RESET
+#endif
+
+#define ZT_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // short
+
+#ifdef __linux__
+ #define ZT_THREAD_ID (long)0 // syscall(SYS_gettid)
+#endif
+#ifdef __APPLE__
+ #define ZT_THREAD_ID (long)0 // (long)gettid()
+#endif
+#ifdef _WIN32
+ #define ZT_THREAD_ID (long)0 //
+#endif
+#if defined(__JNI_LIB__)
+ #include <jni.h>
+#endif
+#if defined(__ANDROID__)
+ #include <android/log.h>
+ #define ZT_LOG_TAG "ZTSDK"
+#endif
+#if defined(ZT_TRACE)
+ #if ZT_MSG_INFO == true
+ #if defined(__ANDROID__)
+ #define DEBUG_INFO(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \
+ "INFO : %17s:%5d:%20s: " fmt "\n", ZT_FILENAME, __LINE__, __FUNCTION__, ##args))
+ #endif
+ #if defined(_WIN32)
+ #define DEBUG_INFO(fmt, ...) fprintf(stderr, ZT_GRN "INFO [%ld]: %17s:%5d:%25s: " fmt "\n" \
+ ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, __VA_ARGS__)
+ #endif
+ #if defined(__linux__) or defined(__APPLE__)
+ #define DEBUG_INFO(fmt, args ...) fprintf(stderr, ZT_GRN "INFO [%ld]: %17s:%5d:%25s: " fmt "\n" \
+ ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args)
+ #endif
+ #else
+ #define DEBUG_INFO(fmt, args...)
+ #endif
+#else // blank
+ #if defined(_WIN32)
+ #define DEBUG_INFO(...)
+ #else
+ #define DEBUG_INFO(fmt, args...)
+ #endif
+#endif
+
+#endif // _H
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index 2d7b007b..6da53a73 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
/*
@@ -21,8 +29,8 @@
* engine.
*/
-#ifndef ZT_ZEROTIERONE_H
-#define ZT_ZEROTIERONE_H
+#ifndef ZT_ZEROTIER_API_H
+#define ZT_ZEROTIER_API_H
#include <stdint.h>
@@ -38,6 +46,31 @@
#include <sys/socket.h>
#endif /* Windows or not */
+#if defined (_MSC_VER)
+#ifdef ZT_EXPORT
+#define ZT_SDK_API __declspec(dllexport)
+#else
+#define ZT_SDK_API __declspec(dllimport)
+#if !defined(ZT_SDK)
+#ifdef _DEBUG
+#ifdef _WIN64
+#pragma comment(lib, "ZeroTierOne_x64d.lib")
+#else
+#pragma comment(lib, "ZeroTierOne_x86d.lib")
+#endif
+#else
+#ifdef _WIN64
+#pragma comment(lib, "ZeroTierOne_x64.lib")
+#else
+#pragma comment(lib, "ZeroTierOne_x86.lib")
+#endif
+#endif
+#endif
+#endif
+#else
+#define ZT_SDK_API
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -52,29 +85,47 @@ extern "C" {
#define ZT_DEFAULT_PORT 9993
/**
+ * Minimum MTU, which is the minimum allowed by IPv6 and several specs
+ */
+#define ZT_MIN_MTU 1280
+
+/**
* Maximum MTU for ZeroTier virtual networks
+ */
+#define ZT_MAX_MTU 10000
+
+/**
+ * Minimum UDP payload size allowed
+ */
+#define ZT_MIN_PHYSMTU 1400
+
+/**
+ * Default UDP payload size (physical path MTU) not including UDP and IP overhead
*
- * This is pretty much an unchangeable global constant. To make it change
- * across nodes would require logic to send ICMP packet too big messages,
- * which would complicate things. 1500 has been good enough on most LANs
- * for ages, so a larger MTU should be fine for the forseeable future. This
- * typically results in two UDP packets per single large frame. Experimental
- * results seem to show that this is good. Larger MTUs resulting in more
- * fragments seemed too brittle on slow/crummy links for no benefit.
- *
- * If this does change, also change it in tap.h in the tuntaposx code under
- * mac-tap.
- *
- * Overhead for a normal frame split into two packets:
- *
- * 1414 = 1444 (typical UDP MTU) - 28 (packet header) - 2 (ethertype)
- * 1428 = 1444 (typical UDP MTU) - 16 (fragment header)
- * SUM: 2842
- *
- * We use 2800, which leaves some room for other payload in other types of
- * messages such as multicast propagation or future support for bridging.
+ * This is 1500 - IPv6 UDP overhead - PPPoE overhead and is safe for 99.9% of
+ * all Internet links.
+ */
+#define ZT_DEFAULT_PHYSMTU 1444
+
+/**
+ * Maximum physical UDP payload
+ */
+#define ZT_MAX_PHYSPAYLOAD 10100
+
+/**
+ * Headroom for max physical MTU
+ */
+#define ZT_MAX_HEADROOM 224
+
+/**
+ * Maximum payload MTU for UDP packets
*/
-#define ZT_MAX_MTU 2800
+#define ZT_MAX_PHYSMTU (ZT_MAX_PHYSPAYLOAD + ZT_MAX_HEADROOM)
+
+/**
+ * Maximum size of a remote trace message's serialized Dictionary
+ */
+#define ZT_MAX_REMOTE_TRACE_SIZE 10000
/**
* Maximum length of network short name
@@ -97,62 +148,214 @@ extern "C" {
#define ZT_MAX_NETWORK_SPECIALISTS 256
/**
- * Maximum number of static physical to ZeroTier address mappings (typically relays, etc.)
+ * Maximum number of multicast group subscriptions per network
*/
-#define ZT_MAX_NETWORK_PINNED 16
+#define ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS 4096
/**
- * Maximum number of rule table entries per network (can be increased)
+ * Rules engine revision ID, which specifies rules engine capabilities
*/
-#define ZT_MAX_NETWORK_RULES 256
+#define ZT_RULES_ENGINE_REVISION 1
/**
- * Maximum number of multicast group subscriptions per network
+ * Maximum number of base (non-capability) network rules
*/
-#define ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS 4096
+#define ZT_MAX_NETWORK_RULES 1024
+
+/**
+ * Maximum number of per-member capabilities per network
+ */
+#define ZT_MAX_NETWORK_CAPABILITIES 128
+
+/**
+ * Maximum number of per-member tags per network
+ */
+#define ZT_MAX_NETWORK_TAGS 128
/**
* Maximum number of direct network paths to a given peer
*/
-#define ZT_MAX_PEER_NETWORK_PATHS 4
+#define ZT_MAX_PEER_NETWORK_PATHS 16
/**
- * Maximum number of trusted physical network paths
+ * Maximum number of path configurations that can be set
*/
-#define ZT_MAX_TRUSTED_PATHS 16
+#define ZT_MAX_CONFIGURABLE_PATHS 32
/**
- * Maximum number of hops in a ZeroTier circuit test
- *
- * This is more or less the max that can be fit in a given packet (with
- * fragmentation) and only one address per hop.
+ * Maximum number of rules per capability
+ */
+#define ZT_MAX_CAPABILITY_RULES 64
+
+/**
+ * Maximum number of certificates of ownership to assign to a single network member
+ */
+#define ZT_MAX_CERTIFICATES_OF_OWNERSHIP 4
+
+/**
+ * Global maximum length for capability chain of custody (including initial issue)
+ */
+#define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
+
+/**
+ * Maximum value for link quality (min is 0)
+ */
+#define ZT_PATH_LINK_QUALITY_MAX 0xff
+
+/**
+ * Packet characteristics flag: packet direction, 1 if inbound 0 if outbound
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_INBOUND 0x8000000000000000ULL
+
+/**
+ * Packet characteristics flag: multicast or broadcast destination MAC
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST 0x4000000000000000ULL
+
+/**
+ * Packet characteristics flag: broadcast destination MAC
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST 0x2000000000000000ULL
+
+/**
+ * Packet characteristics flag: sending IP address has a certificate of ownership
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED 0x1000000000000000ULL
+
+/**
+ * Packet characteristics flag: sending MAC address has a certificate of ownership
*/
-#define ZT_CIRCUIT_TEST_MAX_HOPS 256
+#define ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED 0x0800000000000000ULL
/**
- * Maximum number of addresses per hop in a circuit test
+ * Packet characteristics flag: TCP left-most reserved bit
*/
-#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_0 0x0000000000000800ULL
/**
- * Maximum number of cluster members (and max member ID plus one)
+ * Packet characteristics flag: TCP middle reserved bit
*/
-#define ZT_CLUSTER_MAX_MEMBERS 128
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_1 0x0000000000000400ULL
/**
- * Maximum number of physical ZeroTier addresses a cluster member can report
+ * Packet characteristics flag: TCP right-most reserved bit
*/
-#define ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES 16
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RESERVED_2 0x0000000000000200ULL
/**
- * Maximum allowed cluster message length in bytes
+ * Packet characteristics flag: TCP NS flag
*/
-#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_NS 0x0000000000000100ULL
/**
- * A null/empty sockaddr (all zero) to signify an unspecified socket address
+ * Packet characteristics flag: TCP CWR flag
*/
-extern const struct sockaddr_storage ZT_SOCKADDR_NULL;
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_CWR 0x0000000000000080ULL
+
+/**
+ * Packet characteristics flag: TCP ECE flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_ECE 0x0000000000000040ULL
+
+/**
+ * Packet characteristics flag: TCP URG flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_URG 0x0000000000000020ULL
+
+/**
+ * Packet characteristics flag: TCP ACK flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK 0x0000000000000010ULL
+
+/**
+ * Packet characteristics flag: TCP PSH flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_PSH 0x0000000000000008ULL
+
+/**
+ * Packet characteristics flag: TCP RST flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_RST 0x0000000000000004ULL
+
+/**
+ * Packet characteristics flag: TCP SYN flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN 0x0000000000000002ULL
+
+/**
+ * Packet characteristics flag: TCP FIN flag
+ */
+#define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
+
+// Fields in remote trace dictionaries
+#define ZT_REMOTE_TRACE_FIELD__EVENT "event"
+#define ZT_REMOTE_TRACE_FIELD__NODE_ID "nodeId"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_ID "packetId"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_VERB "packetVerb"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_ID "packetTrustedPathId"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_APPROVED "packetTrustedPathApproved"
+#define ZT_REMOTE_TRACE_FIELD__PACKET_HOPS "packetHops"
+#define ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR "remoteZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR "remotePhyAddr"
+#define ZT_REMOTE_TRACE_FIELD__LOCAL_ZTADDR "localZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR "localPhyAddr"
+#define ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET "localSocket"
+#define ZT_REMOTE_TRACE_FIELD__IP_SCOPE "phyAddrIpScope"
+#define ZT_REMOTE_TRACE_FIELD__NETWORK_ID "networkId"
+#define ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR "sourceZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR "destZtAddr"
+#define ZT_REMOTE_TRACE_FIELD__SOURCE_MAC "sourceMac"
+#define ZT_REMOTE_TRACE_FIELD__DEST_MAC "destMac"
+#define ZT_REMOTE_TRACE_FIELD__ETHERTYPE "etherType"
+#define ZT_REMOTE_TRACE_FIELD__VLAN_ID "vlanId"
+#define ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH "frameLength"
+#define ZT_REMOTE_TRACE_FIELD__FRAME_DATA "frameData"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE "filterNoTee"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND "filterInbound"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_RESULT "filterResult"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG "filterBaseRuleLog"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG "filterCapRuleLog"
+#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID "filterMatchingCapId"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE "credType"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID "credId"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP "credTs"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO "credInfo"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO "credIssuedTo"
+#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET "credRevocationTarget"
+#define ZT_REMOTE_TRACE_FIELD__REASON "reason"
+#define ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID "networkControllerId"
+
+// Event types in remote traces
+#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE 0x1000
+#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH 0x1001
+#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH 0x1002
+#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED 0x1003
+#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE 0x1004
+#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID 0x1005
+#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO 0x1006
+#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED 0x2000
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED 0x2001
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED 0x2002
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED 0x2003
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED 0x2004
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT 0x2005
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE 0x2006
+
+// Event types in remote traces in hex string form
+#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S "1000"
+#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S "1001"
+#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S "1002"
+#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S "1003"
+#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S "1004"
+#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S "1005"
+#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO_S "1006"
+#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S "2000"
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S "2001"
+#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S "2002"
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S "2003"
+#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED_S "2004"
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S "2005"
+#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S "2006"
/****************************************************************************/
/* Structures and other types */
@@ -173,22 +376,27 @@ enum ZT_ResultCode
*/
ZT_RESULT_OK = 0,
- // Fatal errors (>0, <1000)
+ /**
+ * Call produced no error but no action was taken
+ */
+ ZT_RESULT_OK_IGNORED = 1,
+
+ // Fatal errors (>100, <1000)
/**
* Ran out of memory
*/
- ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 1,
+ ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 100,
/**
* Data store is not writable or has failed
*/
- ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 2,
+ ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 101,
/**
* Internal error (e.g. unexpected exception indicating bug or build problem)
*/
- ZT_RESULT_FATAL_ERROR_INTERNAL = 3,
+ ZT_RESULT_FATAL_ERROR_INTERNAL = 102,
// Non-fatal errors (>1000)
@@ -212,7 +420,7 @@ enum ZT_ResultCode
* @param x Result code
* @return True if result code indicates a fatal error
*/
-#define ZT_ResultCode_isFatal(x) ((((int)(x)) > 0)&&(((int)(x)) < 1000))
+#define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000))
/**
* Status codes sent to status update callback when things happen
@@ -293,28 +501,104 @@ enum ZT_Event
*
* Meta-data: C string, TRACE message
*/
- ZT_EVENT_TRACE = 5
+ ZT_EVENT_TRACE = 5,
+
+ /**
+ * VERB_USER_MESSAGE received
+ *
+ * These are generated when a VERB_USER_MESSAGE packet is received via
+ * ZeroTier VL1.
+ *
+ * Meta-data: ZT_UserMessage structure
+ */
+ ZT_EVENT_USER_MESSAGE = 6,
+
+ /**
+ * Remote trace received
+ *
+ * These are generated when a VERB_REMOTE_TRACE is received. Note
+ * that any node can fling one of these at us. It is your responsibility
+ * to filter and determine if it's worth paying attention to. If it's
+ * not just drop it. Most nodes that are not active controllers ignore
+ * these, and controllers only save them if they pertain to networks
+ * with remote tracing enabled.
+ *
+ * Meta-data: ZT_RemoteTrace structure
+ */
+ ZT_EVENT_REMOTE_TRACE = 7
};
/**
- * Current node status
+ * Payload of REMOTE_TRACE event
*/
typedef struct
{
/**
- * 40-bit ZeroTier address of this node
+ * ZeroTier address of sender
*/
- uint64_t address;
+ uint64_t origin;
+
+ /**
+ * Null-terminated Dictionary containing key/value pairs sent by origin
+ *
+ * This *should* be a dictionary, but the implementation only checks
+ * that it is a valid non-empty C-style null-terminated string. Be very
+ * careful to use a well-tested parser to parse this as it represents
+ * data received from a potentially un-trusted peer on the network.
+ * Invalid payloads should be dropped.
+ *
+ * The contents of data[] may be modified.
+ */
+ char *data;
/**
- * Current world ID
+ * Length of dict[] in bytes, including terminating null
+ */
+ unsigned int len;
+} ZT_RemoteTrace;
+
+/**
+ * User message used with ZT_EVENT_USER_MESSAGE
+ *
+ * These are direct VL1 P2P messages for application use. Encryption and
+ * authentication in the ZeroTier protocol will guarantee the origin
+ * address and message content, but you are responsible for any other
+ * levels of authentication or access control that are required. Any node
+ * in the world can send you a user message! (Unless your network is air
+ * gapped.)
+ */
+typedef struct
+{
+ /**
+ * ZeroTier address of sender (least significant 40 bits)
*/
- uint64_t worldId;
+ uint64_t origin;
/**
- * Current world revision/timestamp
+ * User message type ID
*/
- uint64_t worldTimestamp;
+ uint64_t typeId;
+
+ /**
+ * User message data (not including type ID)
+ */
+ const void *data;
+
+ /**
+ * Length of data in bytes
+ */
+ unsigned int length;
+} ZT_UserMessage;
+
+/**
+ * Current node status
+ */
+typedef struct
+{
+ /**
+ * 40-bit ZeroTier address of this node
+ */
+ uint64_t address;
/**
* Public identity in string-serialized form (safe to send to others)
@@ -391,12 +675,16 @@ enum ZT_VirtualNetworkType
/**
* The type of a virtual network rules table entry
*
- * These must range from 0 to 127 (0x7f).
+ * These must be from 0 to 63 since the most significant two bits of each
+ * rule type are NOT (MSB) and AND/OR.
*
- * Each rule is composed of one or more MATCHes followed by an ACTION.
+ * Each rule is composed of zero or more MATCHes followed by an ACTION.
+ * An ACTION with no MATCHes is always taken.
*/
enum ZT_VirtualNetworkRuleType
{
+ // 0 to 15 reserved for actions
+
/**
* Drop frame
*/
@@ -408,129 +696,70 @@ enum ZT_VirtualNetworkRuleType
ZT_NETWORK_RULE_ACTION_ACCEPT = 1,
/**
- * Forward a copy of this frame to an observer
+ * Forward a copy of this frame to an observer (by ZT address)
*/
ZT_NETWORK_RULE_ACTION_TEE = 2,
/**
- * Explicitly redirect this frame to another device (ignored if this is the target device)
+ * Exactly like TEE but mandates ACKs from observer
*/
- ZT_NETWORK_RULE_ACTION_REDIRECT = 3,
-
- // <32 == actions
+ ZT_NETWORK_RULE_ACTION_WATCH = 3,
/**
- * Source ZeroTier address -- analogous to an Ethernet port ID on a switch
+ * Drop and redirect this frame to another node (by ZT address)
*/
- ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS = 32,
+ ZT_NETWORK_RULE_ACTION_REDIRECT = 4,
/**
- * Destination ZeroTier address -- analogous to an Ethernet port ID on a switch
+ * Stop evaluating rule set (drops unless there are capabilities, etc.)
*/
- ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS = 33,
+ ZT_NETWORK_RULE_ACTION_BREAK = 5,
/**
- * Ethernet VLAN ID
- */
- ZT_NETWORK_RULE_MATCH_VLAN_ID = 34,
-
- /**
- * Ethernet VLAN PCP
+ * Maximum ID for an ACTION, anything higher is a MATCH
*/
- ZT_NETWORK_RULE_MATCH_VLAN_PCP = 35,
+ ZT_NETWORK_RULE_ACTION__MAX_ID = 15,
- /**
- * Ethernet VLAN DEI
- */
- ZT_NETWORK_RULE_MATCH_VLAN_DEI = 36,
+ // 16 to 63 reserved for match criteria
- /**
- * Ethernet frame type
- */
+ ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS = 24,
+ ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS = 25,
+ ZT_NETWORK_RULE_MATCH_VLAN_ID = 26,
+ ZT_NETWORK_RULE_MATCH_VLAN_PCP = 27,
+ ZT_NETWORK_RULE_MATCH_VLAN_DEI = 28,
+ ZT_NETWORK_RULE_MATCH_MAC_SOURCE = 29,
+ ZT_NETWORK_RULE_MATCH_MAC_DEST = 30,
+ ZT_NETWORK_RULE_MATCH_IPV4_SOURCE = 31,
+ ZT_NETWORK_RULE_MATCH_IPV4_DEST = 32,
+ ZT_NETWORK_RULE_MATCH_IPV6_SOURCE = 33,
+ ZT_NETWORK_RULE_MATCH_IPV6_DEST = 34,
+ ZT_NETWORK_RULE_MATCH_IP_TOS = 35,
+ ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 36,
ZT_NETWORK_RULE_MATCH_ETHERTYPE = 37,
-
- /**
- * Source Ethernet MAC address
- */
- ZT_NETWORK_RULE_MATCH_MAC_SOURCE = 38,
-
- /**
- * Destination Ethernet MAC address
- */
- ZT_NETWORK_RULE_MATCH_MAC_DEST = 39,
-
- /**
- * Source IPv4 address
- */
- ZT_NETWORK_RULE_MATCH_IPV4_SOURCE = 40,
-
- /**
- * Destination IPv4 address
- */
- ZT_NETWORK_RULE_MATCH_IPV4_DEST = 41,
-
- /**
- * Source IPv6 address
- */
- ZT_NETWORK_RULE_MATCH_IPV6_SOURCE = 42,
-
- /**
- * Destination IPv6 address
- */
- ZT_NETWORK_RULE_MATCH_IPV6_DEST = 43,
-
- /**
- * IP TOS (type of service)
- */
- ZT_NETWORK_RULE_MATCH_IP_TOS = 44,
-
- /**
- * IP protocol
- */
- ZT_NETWORK_RULE_MATCH_IP_PROTOCOL = 45,
-
- /**
- * IP source port range (start-end, inclusive)
- */
- ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 46,
-
- /**
- * IP destination port range (start-end, inclusive)
- */
- ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 47,
-
- /**
- * Packet characteristics (set of flags)
- */
- ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 48,
-
- /**
- * Frame size range (start-end, inclusive)
- */
- ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 49,
-
- /**
- * Match a range of relative TCP sequence numbers (e.g. approx first N bytes of stream)
- */
- ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE = 50,
-
- /**
- * Match a certificate of network membership field from the ZT origin's COM: greater than or equal to
- */
- ZT_NETWORK_RULE_MATCH_COM_FIELD_GE = 51,
-
- /**
- * Match a certificate of network membership field from the ZT origin's COM: less than or equal to
- */
- ZT_NETWORK_RULE_MATCH_COM_FIELD_LE = 52
+ ZT_NETWORK_RULE_MATCH_ICMP = 38,
+ ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE = 39,
+ ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE = 40,
+ ZT_NETWORK_RULE_MATCH_CHARACTERISTICS = 41,
+ ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE = 42,
+ ZT_NETWORK_RULE_MATCH_RANDOM = 43,
+ ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE = 44,
+ ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND = 45,
+ ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR = 46,
+ ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR = 47,
+ ZT_NETWORK_RULE_MATCH_TAGS_EQUAL = 48,
+ ZT_NETWORK_RULE_MATCH_TAG_SENDER = 49,
+ ZT_NETWORK_RULE_MATCH_TAG_RECEIVER = 50,
+ ZT_NETWORK_RULE_MATCH_INTEGER_RANGE = 51,
+
+ /**
+ * Maximum ID allowed for a MATCH entry in the rules table
+ */
+ ZT_NETWORK_RULE_MATCH__MAX_ID = 63
};
/**
* Network flow rule
*
- * NOTE: Currently (1.1.x) only etherType is supported! Other things will
- * have no effect until the rules engine is fully implemented.
- *
* Rules are stored in a table in which one or more match entries is followed
* by an action. If more than one match precedes an action, the rule is
* the AND of all matches. An action with no match is always taken since it
@@ -541,16 +770,16 @@ enum ZT_VirtualNetworkRuleType
*/
typedef struct
{
- /**
- * Least significant 7 bits: ZT_VirtualNetworkRuleType, most significant 1 bit is NOT bit
+ /**
+ * Type and flags
*
- * If the NOT bit is set, then matches will be interpreted as "does not
- * match." The NOT bit has no effect on actions.
+ * Bits are: NOTTTTTT
*
- * Use "& 0x7f" to get the enum and "& 0x80" to get the NOT flag.
+ * N - If true, sense of match is inverted (no effect on actions)
+ * O - If true, result is ORed with previous instead of ANDed (no effect on actions)
+ * T - Rule or action type
*
- * The union 'v' is a variant type, and this selects which field in 'v' is
- * actually used and valid.
+ * AND with 0x3f to get type, 0x80 to get NOT bit, and 0x40 to get OR bit.
*/
uint8_t t;
@@ -575,6 +804,20 @@ typedef struct
} ipv4;
/**
+ * Integer range match in packet payload
+ *
+ * This allows matching of ranges of integers up to 64 bits wide where
+ * the range is +/- INT32_MAX. It's packed this way so it fits in 16
+ * bytes and doesn't enlarge the overall size of this union.
+ */
+ struct {
+ uint64_t start; // integer range start
+ uint32_t end; // end of integer range (relative to start, inclusive, 0 for equality w/start)
+ uint16_t idx; // index in packet of integer
+ uint8_t format; // bits in integer (range 1-64, ((format&63)+1)) and endianness (MSB 1 for little, 0 for big)
+ } intRange;
+
+ /**
* Packet characteristic flags being matched
*/
uint64_t characteristics;
@@ -585,14 +828,14 @@ typedef struct
uint16_t port[2];
/**
- * TCP relative sequence number range -- start-end inclusive -- host byte order
+ * 40-bit ZeroTier address (in least significant bits, host byte order)
*/
- uint32_t tcpseq[2];
+ uint64_t zt;
/**
- * 40-bit ZeroTier address (in least significant bits, host byte order)
+ * 0 = never, UINT32_MAX = always
*/
- uint64_t zt;
+ uint32_t randomProbability;
/**
* 48-bit Ethernet MAC address in big-endian order
@@ -625,9 +868,12 @@ typedef struct
uint8_t ipProtocol;
/**
- * IP type of service
+ * IP type of service a.k.a. DSCP field
*/
- uint8_t ipTos;
+ struct {
+ uint8_t mask;
+ uint8_t value[2];
+ } ipTos;
/**
* Ethernet packet size in host byte order (start-end, inclusive)
@@ -635,9 +881,30 @@ typedef struct
uint16_t frameSize[2];
/**
- * COM ID and value for ZT_NETWORK_RULE_MATCH_COM_FIELD_GE and ZT_NETWORK_RULE_MATCH_COM_FIELD_LE
+ * ICMP type and code
*/
- uint64_t comIV[2];
+ struct {
+ uint8_t type; // ICMP type, always matched
+ uint8_t code; // ICMP code if matched
+ uint8_t flags; // flag 0x01 means also match code, otherwise only match type
+ } icmp;
+
+ /**
+ * For tag-related rules
+ */
+ struct {
+ uint32_t id;
+ uint32_t value;
+ } tag;
+
+ /**
+ * Destinations for TEE and REDIRECT
+ */
+ struct {
+ uint64_t address;
+ uint32_t flags;
+ uint16_t length;
+ } fwd;
} v;
} ZT_VirtualNetworkRule;
@@ -712,16 +979,18 @@ enum ZT_VirtualNetworkConfigOperation
/**
* What trust hierarchy role does this peer have?
*/
-enum ZT_PeerRole {
- ZT_PEER_ROLE_LEAF = 0, // ordinary node
- ZT_PEER_ROLE_RELAY = 1, // relay node
- ZT_PEER_ROLE_ROOT = 2 // root server
+enum ZT_PeerRole
+{
+ ZT_PEER_ROLE_LEAF = 0, // ordinary node
+ ZT_PEER_ROLE_MOON = 1, // moon root
+ ZT_PEER_ROLE_PLANET = 2 // planetary root
};
/**
* Vendor ID
*/
-enum ZT_Vendor {
+enum ZT_Vendor
+{
ZT_VENDOR_UNSPECIFIED = 0,
ZT_VENDOR_ZEROTIER = 1
};
@@ -729,7 +998,8 @@ enum ZT_Vendor {
/**
* Platform type
*/
-enum ZT_Platform {
+enum ZT_Platform
+{
ZT_PLATFORM_UNSPECIFIED = 0,
ZT_PLATFORM_LINUX = 1,
ZT_PLATFORM_WINDOWS = 2,
@@ -744,13 +1014,15 @@ enum ZT_Platform {
ZT_PLATFORM_VXWORKS = 11,
ZT_PLATFORM_FREERTOS = 12,
ZT_PLATFORM_SYSBIOS = 13,
- ZT_PLATFORM_HURD = 14
+ ZT_PLATFORM_HURD = 14,
+ ZT_PLATFORM_WEB = 15
};
/**
* Architecture type
*/
-enum ZT_Architecture {
+enum ZT_Architecture
+{
ZT_ARCHITECTURE_UNSPECIFIED = 0,
ZT_ARCHITECTURE_X86 = 1,
ZT_ARCHITECTURE_X64 = 2,
@@ -765,7 +1037,8 @@ enum ZT_Architecture {
ZT_ARCHITECTURE_SPARC32 = 11,
ZT_ARCHITECTURE_SPARC64 = 12,
ZT_ARCHITECTURE_DOTNET_CLR = 13,
- ZT_ARCHITECTURE_JAVA_JVM = 14
+ ZT_ARCHITECTURE_JAVA_JVM = 14,
+ ZT_ARCHITECTURE_WEB = 15
};
/**
@@ -873,6 +1146,21 @@ typedef struct
} ZT_VirtualNetworkList;
/**
+ * Physical path configuration
+ */
+typedef struct {
+ /**
+ * If non-zero set this physical network path to be trusted to disable encryption and authentication
+ */
+ uint64_t trustedPathId;
+
+ /**
+ * Physical path MTU from ZT_MIN_PHYSMTU and ZT_MAX_PHYSMTU or <= 0 to use default
+ */
+ int mtu;
+} ZT_PhysicalPathConfiguration;
+
+/**
* Physical network path to a peer
*/
typedef struct
@@ -898,9 +1186,9 @@ typedef struct
uint64_t trustedPathId;
/**
- * Is path active?
+ * Is path expired?
*/
- int active;
+ int expired;
/**
* Is path preferred?
@@ -919,16 +1207,6 @@ typedef struct
uint64_t address;
/**
- * Time we last received a unicast frame from this peer
- */
- uint64_t lastUnicastFrame;
-
- /**
- * Time we last received a multicast rame from this peer
- */
- uint64_t lastMulticastFrame;
-
- /**
* Remote major version or -1 if not known
*/
int versionMajor;
@@ -944,9 +1222,9 @@ typedef struct
int versionRev;
/**
- * Last measured latency in milliseconds or zero if unknown
+ * Last measured latency in milliseconds or -1 if unknown
*/
- unsigned int latency;
+ int latency;
/**
* What trust hierarchy role does this device have?
@@ -974,267 +1252,69 @@ typedef struct
} ZT_PeerList;
/**
- * ZeroTier circuit test configuration and path
+ * ZeroTier core state objects
*/
-typedef struct {
- /**
- * Test ID -- an arbitrary 64-bit identifier
- */
- uint64_t testId;
-
+enum ZT_StateObjectType
+{
/**
- * Timestamp -- sent with test and echoed back by each reporter
+ * Null object -- ignored
*/
- uint64_t timestamp;
+ ZT_STATE_OBJECT_NULL = 0,
/**
- * Originator credential: network ID
+ * Public address and public key
*
- * If this is nonzero, a network ID will be set for this test and
- * the originator must be its primary network controller. This is
- * currently the only authorization method available, so it must
- * be set to run a test.
+ * Object ID: this node's address if known, or 0 if unknown (first query)
+ * Canonical path: <HOME>/identity.public
+ * Persistence: required
*/
- uint64_t credentialNetworkId;
+ ZT_STATE_OBJECT_IDENTITY_PUBLIC = 1,
/**
- * Hops in circuit test (a.k.a. FIFO for graph traversal)
- */
- struct {
- /**
- * Hop flags (currently unused, must be zero)
- */
- unsigned int flags;
-
- /**
- * Number of addresses in this hop (max: ZT_CIRCUIT_TEST_MAX_HOP_BREADTH)
- */
- unsigned int breadth;
-
- /**
- * 40-bit ZeroTier addresses (most significant 24 bits ignored)
- */
- uint64_t addresses[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH];
- } hops[ZT_CIRCUIT_TEST_MAX_HOPS];
-
- /**
- * Number of hops (max: ZT_CIRCUIT_TEST_MAX_HOPS)
- */
- unsigned int hopCount;
-
- /**
- * If non-zero, circuit test will report back at every hop
- */
- int reportAtEveryHop;
-
- /**
- * An arbitrary user-settable pointer
- */
- void *ptr;
-
- /**
- * Pointer for internal use -- initialize to zero and do not modify
- */
- void *_internalPtr;
-} ZT_CircuitTest;
-
-/**
- * Circuit test result report
- */
-typedef struct {
- /**
- * Sender of report (current hop)
- */
- uint64_t current;
-
- /**
- * Previous hop
- */
- uint64_t upstream;
-
- /**
- * 64-bit test ID
- */
- uint64_t testId;
-
- /**
- * Timestamp from original test (echoed back at each hop)
- */
- uint64_t timestamp;
-
- /**
- * Timestamp on remote device
- */
- uint64_t remoteTimestamp;
-
- /**
- * 64-bit packet ID of packet received by the reporting device
- */
- uint64_t sourcePacketId;
-
- /**
- * Flags (currently unused, will be zero)
- */
- uint64_t flags;
-
- /**
- * ZeroTier protocol-level hop count of packet received by reporting device (>0 indicates relayed)
- */
- unsigned int sourcePacketHopCount;
-
- /**
- * Error code (currently unused, will be zero)
- */
- unsigned int errorCode;
-
- /**
- * Remote device vendor ID
- */
- enum ZT_Vendor vendor;
-
- /**
- * Remote device protocol compliance version
- */
- unsigned int protocolVersion;
-
- /**
- * Software major version
- */
- unsigned int majorVersion;
-
- /**
- * Software minor version
- */
- unsigned int minorVersion;
-
- /**
- * Software revision
- */
- unsigned int revision;
-
- /**
- * Platform / OS
- */
- enum ZT_Platform platform;
-
- /**
- * System architecture
- */
- enum ZT_Architecture architecture;
-
- /**
- * Local device address on which packet was received by reporting device
+ * Full identity with secret key
*
- * This may have ss_family equal to zero (null address) if unspecified.
+ * Object ID: this node's address if known, or 0 if unknown (first query)
+ * Canonical path: <HOME>/identity.secret
+ * Persistence: required, should be stored with restricted permissions e.g. mode 0600 on *nix
*/
- struct sockaddr_storage receivedOnLocalAddress;
+ ZT_STATE_OBJECT_IDENTITY_SECRET = 2,
/**
- * Remote address from which reporter received the test packet
+ * The planet (there is only one per... well... planet!)
*
- * This may have ss_family set to zero (null address) if unspecified.
+ * Object ID: world ID of planet, or 0 if unknown (first query)
+ * Canonical path: <HOME>/planet
+ * Persistence: recommended
*/
- struct sockaddr_storage receivedFromRemoteAddress;
+ ZT_STATE_OBJECT_PLANET = 3,
/**
- * Next hops to which packets are being or will be sent by the reporter
+ * A moon (federated root set)
*
- * In addition to reporting back, the reporter may send the test on if
- * there are more recipients in the FIFO. If it does this, it can report
- * back the address(es) that make up the next hop and the physical address
- * for each if it has one. The physical address being null/unspecified
- * typically indicates that no direct path exists and the next packet
- * will be relayed.
- */
- struct {
- /**
- * 40-bit ZeroTier address
- */
- uint64_t address;
-
- /**
- * Physical address or null address (ss_family == 0) if unspecified or unknown
- */
- struct sockaddr_storage physicalAddress;
- } nextHops[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH];
-
- /**
- * Number of next hops reported in nextHops[]
- */
- unsigned int nextHopCount;
-} ZT_CircuitTestReport;
-
-/**
- * A cluster member's status
- */
-typedef struct {
- /**
- * This cluster member's ID (from 0 to 1-ZT_CLUSTER_MAX_MEMBERS)
- */
- unsigned int id;
-
- /**
- * Number of milliseconds since last 'alive' heartbeat message received via cluster backplane address
+ * Object ID: world ID of moon
+ * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID)
+ * Persistence: required if moon memberships should persist
*/
- unsigned int msSinceLastHeartbeat;
+ ZT_STATE_OBJECT_MOON = 4,
/**
- * Non-zero if cluster member is alive
- */
- int alive;
-
- /**
- * X, Y, and Z coordinates of this member (if specified, otherwise zero)
+ * Peer and related state
*
- * What these mean depends on the location scheme being used for
- * location-aware clustering. At present this is GeoIP and these
- * will be the X, Y, and Z coordinates of the location on a spherical
- * approximation of Earth where Earth's core is the origin (in km).
- * They don't have to be perfect and need only be comparable with others
- * to find shortest path via the standard vector distance formula.
- */
- int x,y,z;
-
- /**
- * Cluster member's last reported load
- */
- uint64_t load;
-
- /**
- * Number of peers
- */
- uint64_t peers;
-
- /**
- * Physical ZeroTier endpoints for this member (where peers are sent when directed here)
- */
- struct sockaddr_storage zeroTierPhysicalEndpoints[ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES];
-
- /**
- * Number of physical ZeroTier endpoints this member is announcing
- */
- unsigned int numZeroTierPhysicalEndpoints;
-} ZT_ClusterMemberStatus;
-
-/**
- * ZeroTier cluster status
- */
-typedef struct {
- /**
- * My cluster member ID (a record for 'self' is included in member[])
- */
- unsigned int myId;
-
- /**
- * Number of cluster members
+ * Object ID: peer address
+ * Canonical path: <HOME>/peers.d/<ID> (10-digit address
+ * Persistence: optional, can be cleared at any time
*/
- unsigned int clusterSize;
+ ZT_STATE_OBJECT_PEER = 5,
/**
- * Cluster member statuses
+ * Network configuration
+ *
+ * Object ID: peer address
+ * Canonical path: <HOME>/networks.d/<NETWORKID>.conf (16-digit hex ID)
+ * Persistence: required if network memberships should persist
*/
- ZT_ClusterMemberStatus members[ZT_CLUSTER_MAX_MEMBERS];
-} ZT_ClusterStatus;
+ ZT_STATE_OBJECT_NETWORK_CONFIG = 6
+};
/**
* An instance of a ZeroTier One node (opaque)
@@ -1269,6 +1349,7 @@ typedef void ZT_Node;
typedef int (*ZT_VirtualNetworkConfigFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
+ void *, /* Thread ptr */
uint64_t, /* Network ID */
void **, /* Modifiable network user PTR */
enum ZT_VirtualNetworkConfigOperation, /* Config operation */
@@ -1284,6 +1365,7 @@ typedef int (*ZT_VirtualNetworkConfigFunction)(
typedef void (*ZT_VirtualNetworkFrameFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
+ void *, /* Thread ptr */
uint64_t, /* Network ID */
void **, /* Modifiable network user PTR */
uint64_t, /* Source MAC */
@@ -1303,81 +1385,61 @@ typedef void (*ZT_VirtualNetworkFrameFunction)(
* in the definition of ZT_Event.
*/
typedef void (*ZT_EventCallback)(
- ZT_Node *,
- void *,
- enum ZT_Event,
- const void *);
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
+ void *, /* Thread ptr */
+ enum ZT_Event, /* Event type */
+ const void *); /* Event payload (if applicable) */
/**
- * Function to get an object from the data store
- *
- * Parameters: (1) object name, (2) buffer to fill, (3) size of buffer, (4)
- * index in object to start reading, (5) result parameter that must be set
- * to the actual size of the object if it exists.
- *
- * Object names can contain forward slash (/) path separators. They will
- * never contain .. or backslash (\), so this is safe to map as a Unix-style
- * path if the underlying storage permits. For security reasons we recommend
- * returning errors if .. or \ are used.
+ * Callback for storing and/or publishing state information
*
- * The function must return the actual number of bytes read. If the object
- * doesn't exist, it should return -1. -2 should be returned on other errors
- * such as errors accessing underlying storage.
+ * See ZT_StateObjectType docs for information about each state object type
+ * and when and if it needs to be persisted.
*
- * If the read doesn't fit in the buffer, the max number of bytes should be
- * read. The caller may call the function multiple times to read the whole
- * object.
+ * An object of length -1 is sent to indicate that an object should be
+ * deleted.
*/
-typedef long (*ZT_DataStoreGetFunction)(
- ZT_Node *,
- void *,
- const char *,
- void *,
- unsigned long,
- unsigned long,
- unsigned long *);
+typedef void (*ZT_StatePutFunction)(
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
+ void *, /* Thread ptr */
+ enum ZT_StateObjectType, /* State object type */
+ const uint64_t [2], /* State object ID (if applicable) */
+ const void *, /* State object data */
+ int); /* Length of data or -1 to delete */
/**
- * Function to store an object in the data store
- *
- * Parameters: (1) node, (2) user ptr, (3) object name, (4) object data,
- * (5) object size, (6) secure? (bool).
- *
- * If secure is true, the file should be set readable and writable only
- * to the user running ZeroTier One. What this means is platform-specific.
+ * Callback for retrieving stored state information
*
- * Name semantics are the same as the get function. This must return zero on
- * success. You can return any OS-specific error code on failure, as these
- * may be visible in logs or error messages and might aid in debugging.
- *
- * If the data pointer is null, this must be interpreted as a delete
- * operation.
+ * This function should return the number of bytes actually stored to the
+ * buffer or -1 if the state object was not found or the buffer was too
+ * small to store it.
*/
-typedef int (*ZT_DataStorePutFunction)(
- ZT_Node *,
- void *,
- const char *,
- const void *,
- unsigned long,
- int);
+typedef int (*ZT_StateGetFunction)(
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
+ void *, /* Thread ptr */
+ enum ZT_StateObjectType, /* State object type */
+ const uint64_t [2], /* State object ID (if applicable) */
+ void *, /* Buffer to store state object data */
+ unsigned int); /* Length of data buffer in bytes */
/**
- * Function to send a ZeroTier packet out over the wire
+ * Function to send a ZeroTier packet out over the physical wire (L2/L3)
*
* Parameters:
* (1) Node
* (2) User pointer
- * (3) Local interface address
+ * (3) Local socket or -1 for "all" or "any"
* (4) Remote address
* (5) Packet data
* (6) Packet length
* (7) Desired IP TTL or 0 to use default
*
- * If there is only one local interface it is safe to ignore the local
- * interface address. Otherwise if running with multiple interfaces, the
- * correct local interface should be chosen by address unless NULL. If
- * the ss_family field is zero (NULL address), a random or preferred
- * default interface should be used.
+ * If there is only one local socket, the local socket can be ignored.
+ * If the local socket is -1, the packet should be sent out from all
+ * bound local sockets or a random bound local socket.
*
* If TTL is nonzero, packets should have their IP TTL value set to this
* value if possible. If this is not possible it is acceptable to ignore
@@ -1390,7 +1452,8 @@ typedef int (*ZT_DataStorePutFunction)(
typedef int (*ZT_WirePacketSendFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
- const struct sockaddr_storage *, /* Local address */
+ void *, /* Thread ptr */
+ int64_t, /* Local socket */
const struct sockaddr_storage *, /* Remote address */
const void *, /* Packet data */
unsigned int, /* Packet length */
@@ -1402,8 +1465,9 @@ typedef int (*ZT_WirePacketSendFunction)(
* Paramters:
* (1) Node
* (2) User pointer
- * (3) Local interface address
- * (4) Remote address
+ * (3) ZeroTier address or 0 for none/any
+ * (4) Local socket or -1 if unknown
+ * (5) Remote address
*
* This function must return nonzero (true) if the path should be used.
*
@@ -1415,47 +1479,109 @@ typedef int (*ZT_WirePacketSendFunction)(
* all configured ZeroTier interfaces and check to ensure that the supplied
* addresses will not result in ZeroTier traffic being sent over a ZeroTier
* interface (recursion).
- *
- * Obviously this is not required in configurations where this can't happen,
- * such as network containers or embedded.
*/
typedef int (*ZT_PathCheckFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
- const struct sockaddr_storage *, /* Local address */
+ void *, /* Thread ptr */
+ uint64_t, /* ZeroTier address */
+ int64_t, /* Local socket or -1 if unknown */
const struct sockaddr_storage *); /* Remote address */
+/**
+ * Function to get physical addresses for ZeroTier peers
+ *
+ * Parameters:
+ * (1) Node
+ * (2) User pointer
+ * (3) ZeroTier address (least significant 40 bits)
+ * (4) Desried address family or -1 for any
+ * (5) Buffer to fill with result
+ *
+ * If provided this function will be occasionally called to get physical
+ * addresses that might be tried to reach a ZeroTier address. It must
+ * return a nonzero (true) value if the result buffer has been filled
+ * with an address.
+ */
+typedef int (*ZT_PathLookupFunction)(
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
+ void *, /* Thread ptr */
+ uint64_t, /* ZeroTier address (40 bits) */
+ int, /* Desired ss_family or -1 for any */
+ struct sockaddr_storage *); /* Result buffer */
+
/****************************************************************************/
/* C Node API */
/****************************************************************************/
/**
- * Create a new ZeroTier One node
+ * Structure for configuring ZeroTier core callback functions
+ */
+struct ZT_Node_Callbacks
+{
+ /**
+ * Struct version -- must currently be 0
+ */
+ long version;
+
+ /**
+ * REQUIRED: Function to store and/or replicate state objects
+ */
+ ZT_StatePutFunction statePutFunction;
+
+ /**
+ * REQUIRED: Function to retrieve state objects from an object store
+ */
+ ZT_StateGetFunction stateGetFunction;
+
+ /**
+ * REQUIRED: Function to send packets over the physical wire
+ */
+ ZT_WirePacketSendFunction wirePacketSendFunction;
+
+ /**
+ * REQUIRED: Function to inject frames into a virtual network's TAP
+ */
+ ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction;
+
+ /**
+ * REQUIRED: Function to be called when virtual networks are configured or changed
+ */
+ ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction;
+
+ /**
+ * REQUIRED: Function to be called to notify external code of important events
+ */
+ ZT_EventCallback eventCallback;
+
+ /**
+ * OPTIONAL: Function to check whether a given physical path should be used
+ */
+ ZT_PathCheckFunction pathCheckFunction;
+
+ /**
+ * OPTIONAL: Function to get hints to physical paths to ZeroTier addresses
+ */
+ ZT_PathLookupFunction pathLookupFunction;
+};
+
+/**
+ * Create a new ZeroTier node
*
- * Note that this can take a few seconds the first time it's called, as it
- * will generate an identity.
+ * This will attempt to load its identity via the state get function in the
+ * callback struct. If that fails it will generate a new identity and store
+ * it. Identity generation can take anywhere from a few hundred milliseconds
+ * to a few seconds depending on your CPU speed.
*
* @param node Result: pointer is set to new node instance on success
* @param uptr User pointer to pass to functions/callbacks
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
+ * @param callbacks Callback function configuration
* @param now Current clock in milliseconds
- * @param dataStoreGetFunction Function called to get objects from persistent storage
- * @param dataStorePutFunction Function called to put objects in persistent storage
- * @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change
- * @param pathCheckFunction A function to check whether a path should be used for ZeroTier traffic, or NULL to allow any path
- * @param eventCallback Function to receive status updates and non-fatal error notices
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_new(
- ZT_Node **node,
- void *uptr,
- uint64_t now,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
/**
* Delete a node and free all resources it consumes
@@ -1465,33 +1591,36 @@ enum ZT_ResultCode ZT_Node_new(
*
* @param node Node to delete
*/
-void ZT_Node_delete(ZT_Node *node);
+ZT_SDK_API void ZT_Node_delete(ZT_Node *node);
/**
* Process a packet received from the physical wire
*
* @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param now Current clock in milliseconds
- * @param localAddress Local address, or point to ZT_SOCKADDR_NULL if unspecified
+ * @param localSocket Local socket (you can use 0 if only one local socket is bound and ignore this)
* @param remoteAddress Origin of packet
* @param packetData Packet data
* @param packetLength Packet length
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_processWirePacket(
+ZT_SDK_API enum ZT_ResultCode ZT_Node_processWirePacket(
ZT_Node *node,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
/**
* Process a frame from a virtual network port (tap)
*
* @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param now Current clock in milliseconds
* @param nwid ZeroTier 64-bit virtual network ID
* @param sourceMac Source MAC address (least significant 48 bits)
@@ -1503,9 +1632,10 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
+ZT_SDK_API enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
ZT_Node *node,
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -1513,17 +1643,18 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
/**
* Perform periodic background operations
*
* @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param now Current clock in milliseconds
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline);
/**
* Join a network
@@ -1539,7 +1670,7 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
* @param uptr An arbitrary pointer to associate with this network (default: NULL)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr);
/**
* Leave a network
@@ -1556,7 +1687,7 @@ enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr);
* @param uptr Target pointer is set to uptr (if not NULL)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr);
/**
* Subscribe to an Ethernet multicast group
@@ -1578,12 +1709,13 @@ enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr);
* This does not generate an update call to networkConfigCallback().
*
* @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
* @param nwid 64-bit network ID
* @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
* @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
/**
* Unsubscribe from an Ethernet multicast group (or all groups)
@@ -1599,7 +1731,33 @@ enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64
* @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed)
* @return OK (0) or error code if a fatal error condition has occurred
*/
-enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+
+/**
+ * Add or update a moon
+ *
+ * Moons are persisted in the data store in moons.d/, so this can persist
+ * across invocations if the contents of moon.d are scanned and orbit is
+ * called for each on startup.
+ *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
+ * @param moonWorldId Moon's world ID
+ * @param moonSeed If non-zero, the ZeroTier address of any member of the moon to query for moon definition
+ * @param len Length of moonWorld in bytes
+ * @return Error if moon was invalid or failed to be added
+ */
+ZT_SDK_API enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
+
+/**
+ * Remove a moon (does nothing if not present)
+ *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
+ * @param moonWorldId World ID of moon to remove
+ * @return Error if anything bad happened
+ */
+ZT_SDK_API enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId);
/**
* Get this node's 40-bit ZeroTier address
@@ -1607,7 +1765,7 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
* @param node Node instance
* @return ZeroTier address (least significant 40 bits of 64-bit int)
*/
-uint64_t ZT_Node_address(ZT_Node *node);
+ZT_SDK_API uint64_t ZT_Node_address(ZT_Node *node);
/**
* Get the status of this node
@@ -1615,7 +1773,7 @@ uint64_t ZT_Node_address(ZT_Node *node);
* @param node Node instance
* @param status Buffer to fill with current node status
*/
-void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status);
+ZT_SDK_API void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status);
/**
* Get a list of known peer nodes
@@ -1626,7 +1784,7 @@ void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status);
* @param node Node instance
* @return List of known peers or NULL on failure
*/
-ZT_PeerList *ZT_Node_peers(ZT_Node *node);
+ZT_SDK_API ZT_PeerList *ZT_Node_peers(ZT_Node *node);
/**
* Get the status of a virtual network
@@ -1638,7 +1796,7 @@ ZT_PeerList *ZT_Node_peers(ZT_Node *node);
* @param nwid 64-bit network ID
* @return Network configuration or NULL if we are not a member of this network
*/
-ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid);
+ZT_SDK_API ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid);
/**
* Enumerate and get status of all networks
@@ -1646,7 +1804,7 @@ ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t nwid);
* @param node Node instance
* @return List of networks or NULL on failure
*/
-ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node);
+ZT_SDK_API ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node);
/**
* Free a query result buffer
@@ -1656,7 +1814,7 @@ ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node);
* @param node Node instance
* @param qr Query result buffer
*/
-void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
+ZT_SDK_API void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
/**
* Add a local interface address
@@ -1680,12 +1838,28 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
* @param addr Local interface address
* @return Boolean: non-zero if address was accepted and added
*/
-int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr);
+ZT_SDK_API int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr);
/**
* Clear local interface addresses
*/
-void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
+ZT_SDK_API void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
+
+/**
+ * Send a VERB_USER_MESSAGE to another ZeroTier node
+ *
+ * There is no delivery guarantee here. Failure can occur if the message is
+ * too large or if dest is not a valid ZeroTier address.
+ *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
+ * @param dest Destination ZeroTier address
+ * @param typeId VERB_USER_MESSAGE type ID
+ * @param data Payload data to attach to user message
+ * @param len Length of data in bytes
+ * @return Boolean: non-zero on success, zero on failure
+ */
+ZT_SDK_API int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
/**
* Set a network configuration master instance for this node
@@ -1702,194 +1876,17 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
* @param networkConfigMasterInstance Instance of NetworkConfigMaster C++ class or NULL to disable
* @return OK (0) or error code if a fatal error condition has occurred
*/
-void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
+ZT_SDK_API void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
/**
- * Initiate a VL1 circuit test
- *
- * This sends an initial VERB_CIRCUIT_TEST and reports results back to the
- * supplied callback until circuitTestEnd() is called. The supplied
- * ZT_CircuitTest structure should be initially zeroed and then filled
- * in with settings and hops.
- *
- * It is the caller's responsibility to call circuitTestEnd() and then
- * to dispose of the test structure. Otherwise this node will listen
- * for results forever.
- *
- * @param node Node instance
- * @param test Test configuration
- * @param reportCallback Function to call each time a report is received
- * @return OK or error if, for example, test is too big for a packet or support isn't compiled in
- */
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *, ZT_CircuitTest *,const ZT_CircuitTestReport *));
-
-/**
- * Stop listening for results to a given circuit test
- *
- * This does not free the 'test' structure. The caller may do that
- * after calling this method to unregister it.
- *
- * Any reports that are received for a given test ID after it is
- * terminated are ignored.
- *
- * @param node Node instance
- * @param test Test configuration to unregister
- */
-void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test);
-
-/**
- * Initialize cluster operation
- *
- * This initializes the internal structures and state for cluster operation.
- * It takes two function pointers. The first is to a function that can be
- * used to send data to cluster peers (mechanism is not defined by Node),
- * and the second is to a function that can be used to get the location of
- * a physical address in X,Y,Z coordinate space (e.g. as cartesian coordinates
- * projected from the center of the Earth).
- *
- * Send function takes an arbitrary pointer followed by the cluster member ID
- * to send data to, a pointer to the data, and the length of the data. The
- * maximum message length is ZT_CLUSTER_MAX_MESSAGE_LENGTH (65535). Messages
- * must be delivered whole and may be dropped or transposed, though high
- * failure rates are undesirable and can cause problems. Validity checking or
- * CRC is also not required since the Node validates the authenticity of
- * cluster messages using cryptogrphic methods and will silently drop invalid
- * messages.
- *
- * Address to location function is optional and if NULL geo-handoff is not
- * enabled (in this case x, y, and z in clusterInit are also unused). It
- * takes an arbitrary pointer followed by a physical address and three result
- * parameters for x, y, and z. It returns zero on failure or nonzero if these
- * three coordinates have been set. Coordinate space is arbitrary and can be
- * e.g. coordinates on Earth relative to Earth's center. These can be obtained
- * from latitutde and longitude with versions of the Haversine formula.
- *
- * See: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
- *
- * Neither the send nor the address to location function should block. If the
- * address to location function does not have a location for an address, it
- * should return zero and then look up the address for future use since it
- * will be called again in (typically) 1-3 minutes.
- *
- * Note that both functions can be called from any thread from which the
- * various Node functions are called, and so must be thread safe if multiple
- * threads are being used.
- *
- * @param node Node instance
- * @param myId My cluster member ID (less than or equal to ZT_CLUSTER_MAX_MEMBERS)
- * @param zeroTierPhysicalEndpoints Preferred physical address(es) for ZeroTier clients to contact this cluster member (for peer redirect)
- * @param numZeroTierPhysicalEndpoints Number of physical endpoints in zeroTierPhysicalEndpoints[] (max allowed: 255)
- * @param x My cluster member's X location
- * @param y My cluster member's Y location
- * @param z My cluster member's Z location
- * @param sendFunction Function to be called to send data to other cluster members
- * @param sendFunctionArg First argument to sendFunction()
- * @param addressToLocationFunction Function to be called to get the location of a physical address or NULL to disable geo-handoff
- * @param addressToLocationFunctionArg First argument to addressToLocationFunction()
- * @return OK or UNSUPPORTED_OPERATION if this Node was not built with cluster support
- */
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
-
-/**
- * Add a member to this cluster
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS)
- * @return OK or error if clustering is disabled, ID invalid, etc.
- */
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId);
-
-/**
- * Remove a member from this cluster
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param memberId Member ID to remove (nothing happens if not present)
- */
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId);
-
-/**
- * Handle an incoming cluster state message
- *
- * The message itself contains cluster member IDs, and invalid or badly
- * addressed messages will be silently discarded.
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param msg Cluster message
- * @param len Length of cluster message
- */
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len);
-
-/**
- * Get the current status of the cluster from this node's point of view
- *
- * Calling this without clusterInit() or without cluster support will just
- * zero out the structure and show a cluster size of zero.
- *
- * @param node Node instance
- * @param cs Cluster status structure to fill with data
- */
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
-
-/**
- * Set trusted paths
- *
- * A trusted path is a physical network (network/bits) over which both
- * encryption and authentication can be skipped to improve performance.
- * Each trusted path must have a non-zero unique ID that is the same across
- * all participating nodes.
- *
- * We don't recommend using trusted paths at all unless you really *need*
- * near-bare-metal performance. Even on a LAN authentication and encryption
- * are never a bad thing, and anything that introduces an "escape hatch"
- * for encryption should be treated with the utmost care.
- *
- * Calling with NULL pointers for networks and ids and a count of zero clears
- * all trusted paths.
- *
- * @param node Node instance
- * @param networks Array of [count] networks
- * @param ids Array of [count] corresponding non-zero path IDs (zero path IDs are ignored)
- * @param count Number of trusted paths-- values greater than ZT_MAX_TRUSTED_PATHS are clipped
- */
-void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
-
-/**
- * Do things in the background until Node dies
- *
- * This function can be called from one or more background threads to process
- * certain tasks in the background to improve foreground performance. It will
- * not return until the Node is shut down. If threading is not enabled in
- * this build it will return immediately and will do nothing.
- *
- * This is completely optional. If this is never called, all processing is
- * done in the foreground in the various processXXXX() methods.
- *
- * This does NOT replace or eliminate the need to call the normal
- * processBackgroundTasks() function in your main loop. This mechanism is
- * used to offload the processing of expensive mssages onto background
- * handler threads to prevent foreground performance degradation under
- * high load.
+ * Set configuration for a given physical path
*
* @param node Node instance
+ * @param pathNetwork Network/CIDR of path or NULL to clear the cache and reset all paths to default
+ * @param pathConfig Path configuration or NULL to erase this entry and therefore reset it to NULL
+ * @return OK or error code
*/
-void ZT_Node_backgroundThreadMain(ZT_Node *node);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
/**
* Get ZeroTier One version
@@ -1898,7 +1895,7 @@ void ZT_Node_backgroundThreadMain(ZT_Node *node);
* @param minor Result: minor version
* @param revision Result: revision
*/
-void ZT_version(int *major,int *minor,int *revision);
+ZT_SDK_API void ZT_version(int *major,int *minor,int *revision);
#ifdef __cplusplus
}
diff --git a/java/jni/Android.mk b/java/jni/Android.mk
index c563879c..8cda2474 100644
--- a/java/jni/Android.mk
+++ b/java/jni/Android.mk
@@ -3,22 +3,28 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ZeroTierOneJNI
-LOCAL_C_INCLUDES := $(ZT1)/include
-LOCAL_C_INCLUDES += $(ZT1)/node
-LOCAL_LDLIBS := -llog
+LOCAL_C_INCLUDES := \
+ $(ZT1)/include \
+ $(ZT1)/node \
+ $(ZT1)/osdep \
+ $(ZT1)/ext/miniupnpc \
+ $(ZT1)/ext/libnatpmp
+
+LOCAL_LDLIBS := -llog -latomic
# LOCAL_CFLAGS := -g
+LOCAL_CFLAGS := -DZT_USE_MINIUPNPC
+
# ZeroTierOne SDK source files
LOCAL_SRC_FILES := \
- $(ZT1)/ext/lz4/lz4.c \
- $(ZT1)/ext/json-parser/json.c \
- $(ZT1)/ext/http-parser/http_parser.c \
- $(ZT1)/node/C25519.cpp \
+ $(ZT1)/node/C25519.cpp \
+ $(ZT1)/node/Capability.cpp \
$(ZT1)/node/CertificateOfMembership.cpp \
- $(ZT1)/node/DeferredPackets.cpp \
+ $(ZT1)/node/CertificateOfOwnership.cpp \
$(ZT1)/node/Identity.cpp \
$(ZT1)/node/IncomingPacket.cpp \
$(ZT1)/node/InetAddress.cpp \
+ $(ZT1)/node/Membership.cpp \
$(ZT1)/node/Multicaster.cpp \
$(ZT1)/node/Network.cpp \
$(ZT1)/node/NetworkConfig.cpp \
@@ -28,14 +34,35 @@ LOCAL_SRC_FILES := \
$(ZT1)/node/Path.cpp \
$(ZT1)/node/Peer.cpp \
$(ZT1)/node/Poly1305.cpp \
+ $(ZT1)/node/Revocation.cpp \
$(ZT1)/node/Salsa20.cpp \
$(ZT1)/node/SelfAwareness.cpp \
$(ZT1)/node/SHA512.cpp \
$(ZT1)/node/Switch.cpp \
+ $(ZT1)/node/Tag.cpp \
$(ZT1)/node/Topology.cpp \
+ $(ZT1)/node/Trace.cpp \
$(ZT1)/node/Utils.cpp \
- $(ZT1)/osdep/Http.cpp \
- $(ZT1)/osdep/OSUtils.cpp
+ $(ZT1)/osdep/OSUtils.cpp \
+ $(ZT1)/osdep/PortMapper.cpp
+
+# libminiupnpc and libnatpmp files
+LOCAL_SRC_FILES += \
+ $(ZT1)/ext/miniupnpc/connecthostport.c \
+ $(ZT1)/ext/miniupnpc/igd_desc_parse.c \
+ $(ZT1)/ext/miniupnpc/minisoap.c \
+ $(ZT1)/ext/miniupnpc/minissdpc.c \
+ $(ZT1)/ext/miniupnpc/miniupnpc.c \
+ $(ZT1)/ext/miniupnpc/miniwget.c \
+ $(ZT1)/ext/miniupnpc/minixml.c \
+ $(ZT1)/ext/miniupnpc/portlistingparse.c \
+ $(ZT1)/ext/miniupnpc/receivedata.c \
+ $(ZT1)/ext/miniupnpc/upnpcommands.c \
+ $(ZT1)/ext/miniupnpc/upnpdev.c \
+ $(ZT1)/ext/miniupnpc/upnperrors.c \
+ $(ZT1)/ext/miniupnpc/upnpreplyparse.c \
+ $(ZT1)/ext/libnatpmp/natpmp.c \
+ $(ZT1)/ext/libnatpmp/getgateway.c
# JNI Files
LOCAL_SRC_FILES += \
diff --git a/java/jni/Application.mk b/java/jni/Application.mk
index 6950c0e6..f5a87ac7 100644
--- a/java/jni/Application.mk
+++ b/java/jni/Application.mk
@@ -1,5 +1,5 @@
# NDK_TOOLCHAIN_VERSION := clang3.5
-APP_STL := gnustl_static
-APP_CPPFLAGS := -O3 -fPIC -fPIE -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1
+APP_STL := c++_static
+APP_CPPFLAGS := -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1
APP_PLATFORM := android-14
APP_ABI := all
diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp
index 512bf839..c52a2066 100644
--- a/java/jni/ZT_jniutils.cpp
+++ b/java/jni/ZT_jniutils.cpp
@@ -1,3 +1,21 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
#include "ZT_jniutils.h"
#include "ZT_jnilookup.h"
#include <string>
@@ -12,7 +30,7 @@ extern "C" {
jobject createResultObject(JNIEnv *env, ZT_ResultCode code)
{
jclass resultClass = NULL;
-
+
jobject resultObject = NULL;
resultClass = lookup.findClass("com/zerotier/sdk/ResultCode");
@@ -49,14 +67,14 @@ jobject createResultObject(JNIEnv *env, ZT_ResultCode code)
}
jfieldID enumField = lookup.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;");
- if(env->ExceptionCheck() || enumField == NULL)
+ if(env->ExceptionCheck() || enumField == NULL)
{
LOGE("Error on FindStaticField");
return NULL;
}
resultObject = env->GetStaticObjectField(resultClass, enumField);
- if(env->ExceptionCheck() || resultObject == NULL)
+ if(env->ExceptionCheck() || resultObject == NULL)
{
LOGE("Error on GetStaticObjectField");
}
@@ -136,6 +154,11 @@ jobject createEvent(JNIEnv *env, ZT_Event event)
case ZT_EVENT_TRACE:
fieldName = "EVENT_TRACE";
break;
+ case ZT_EVENT_USER_MESSAGE:
+ break;
+ case ZT_EVENT_REMOTE_TRACE:
+ default:
+ break;
}
jfieldID enumField = lookup.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;");
@@ -162,11 +185,11 @@ jobject createPeerRole(JNIEnv *env, ZT_PeerRole role)
case ZT_PEER_ROLE_LEAF:
fieldName = "PEER_ROLE_LEAF";
break;
- case ZT_PEER_ROLE_RELAY:
- fieldName = "PEER_ROLE_RELAY";
+ case ZT_PEER_ROLE_MOON:
+ fieldName = "PEER_ROLE_MOON";
break;
- case ZT_PEER_ROLE_ROOT:
- fieldName = "PEER_ROLE_ROOTS";
+ case ZT_PEER_ROLE_PLANET:
+ fieldName = "PEER_ROLE_PLANET";
break;
}
@@ -313,11 +336,20 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
return NULL;
}
- jobject inetAddressObject = newInetAddress(env, addr);
+ jobject inetAddressObject = NULL;
- if(env->ExceptionCheck() || inetAddressObject == NULL)
+ if(addr.ss_family != 0)
+ {
+ inetAddressObject = newInetAddress(env, addr);
+
+ if(env->ExceptionCheck() || inetAddressObject == NULL)
+ {
+ LOGE("Error creating new inet address");
+ return NULL;
+ }
+ }
+ else
{
- LOGE("Error creating new inet address");
return NULL;
}
@@ -350,10 +382,9 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
break;
default:
{
- LOGE("ERROR: addr.ss_family is not set or unknown");
break;
}
- };
+ }
jobject inetSocketAddressObject = env->NewObject(inetSocketAddressClass, inetSocketAddress_constructor, inetAddressObject, port);
@@ -371,7 +402,6 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
jfieldID addressField = NULL;
jfieldID lastSendField = NULL;
jfieldID lastReceiveField = NULL;
- jfieldID activeField = NULL;
jfieldID preferredField = NULL;
jmethodID ppp_constructor = NULL;
@@ -404,13 +434,6 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
return NULL;
}
- activeField = lookup.findField(pppClass, "active", "Z");
- if(env->ExceptionCheck() || activeField == NULL)
- {
- LOGE("Error finding active field");
- return NULL;
- }
-
preferredField = lookup.findField(pppClass, "preferred", "Z");
if(env->ExceptionCheck() || preferredField == NULL)
{
@@ -441,7 +464,6 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
env->SetObjectField(pppObject, addressField, addressObject);
env->SetLongField(pppObject, lastSendField, ppp.lastSend);
env->SetLongField(pppObject, lastReceiveField, ppp.lastReceive);
- env->SetBooleanField(pppObject, activeField, ppp.active);
env->SetBooleanField(pppObject, preferredField, ppp.preferred);
if(env->ExceptionCheck()) {
@@ -451,15 +473,13 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
return pppObject;
}
-jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
+jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
{
LOGV("newPeer called");
jclass peerClass = NULL;
jfieldID addressField = NULL;
- jfieldID lastUnicastFrameField = NULL;
- jfieldID lastMulticastFrameField = NULL;
jfieldID versionMajorField = NULL;
jfieldID versionMinorField = NULL;
jfieldID versionRevField = NULL;
@@ -483,20 +503,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
return NULL;
}
- lastUnicastFrameField = lookup.findField(peerClass, "lastUnicastFrame", "J");
- if(env->ExceptionCheck() || lastUnicastFrameField == NULL)
- {
- LOGE("Error finding lastUnicastFrame field of Peer object");
- return NULL;
- }
-
- lastMulticastFrameField = lookup.findField(peerClass, "lastMulticastFrame", "J");
- if(env->ExceptionCheck() || lastMulticastFrameField == NULL)
- {
- LOGE("Error finding lastMulticastFrame field of Peer object");
- return NULL;
- }
-
versionMajorField = lookup.findField(peerClass, "versionMajor", "I");
if(env->ExceptionCheck() || versionMajorField == NULL)
{
@@ -554,8 +560,6 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
}
env->SetLongField(peerObject, addressField, (jlong)peer.address);
- env->SetLongField(peerObject, lastUnicastFrameField, (jlong)peer.lastUnicastFrame);
- env->SetLongField(peerObject, lastMulticastFrameField, (jlong)peer.lastMulticastFrame);
env->SetIntField(peerObject, versionMajorField, peer.versionMajor);
env->SetIntField(peerObject, versionMinorField, peer.versionMinor);
env->SetIntField(peerObject, versionRevField, peer.versionRev);
@@ -571,7 +575,7 @@ jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
jobjectArray arrayObject = env->NewObjectArray(
peer.pathCount, peerPhysicalPathClass, NULL);
- if(env->ExceptionCheck() || arrayObject == NULL)
+ if(env->ExceptionCheck() || arrayObject == NULL)
{
LOGE("Error creating PeerPhysicalPath[] array");
return NULL;
@@ -609,6 +613,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
jfieldID portErrorField = NULL;
jfieldID netconfRevisionField = NULL;
jfieldID assignedAddressesField = NULL;
+ jfieldID routesField = NULL;
vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig");
if(vnetConfigClass == NULL)
@@ -709,13 +714,22 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
return NULL;
}
- assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;");
+ assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses",
+ "[Ljava/net/InetSocketAddress;");
if(env->ExceptionCheck() || assignedAddressesField == NULL)
{
LOGE("Error getting assignedAddresses field");
return NULL;
}
+ routesField = lookup.findField(vnetConfigClass, "routes",
+ "[Lcom/zerotier/sdk/VirtualNetworkRoute;");
+ if(env->ExceptionCheck() || routesField == NULL)
+ {
+ LOGE("Error getting routes field");
+ return NULL;
+ }
+
env->SetLongField(vnetConfigObj, nwidField, vnetConfig.nwid);
env->SetLongField(vnetConfigObj, macField, vnetConfig.mac);
jstring nameStr = env->NewStringUTF(vnetConfig.name);
@@ -773,6 +787,34 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
env->SetObjectField(vnetConfigObj, assignedAddressesField, assignedAddrArrayObj);
+ jclass virtualNetworkRouteClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkRoute");
+ if(env->ExceptionCheck() || virtualNetworkRouteClass == NULL)
+ {
+ LOGE("Error finding VirtualNetworkRoute class");
+ return NULL;
+ }
+
+ jobjectArray routesArrayObj = env->NewObjectArray(
+ vnetConfig.routeCount, virtualNetworkRouteClass, NULL);
+ if(env->ExceptionCheck() || routesArrayObj == NULL)
+ {
+ LOGE("Error creating VirtualNetworkRoute[] array");
+ return NULL;
+ }
+
+ for(unsigned int i = 0; i < vnetConfig.routeCount; ++i)
+ {
+ jobject routeObj = newVirtualNetworkRoute(env, vnetConfig.routes[i]);
+ env->SetObjectArrayElement(routesArrayObj, i, routeObj);
+ if(env->ExceptionCheck())
+ {
+ LOGE("Error assigning VirtualNetworkRoute to array");
+ return NULL;
+ }
+ }
+
+ env->SetObjectField(vnetConfigObj, routesField, routesArrayObj);
+
return vnetConfigObj;
}
@@ -831,6 +873,72 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev)
return versionObj;
}
+jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route)
+{
+ jclass virtualNetworkRouteClass = NULL;
+ jmethodID routeConstructor = NULL;
+
+ virtualNetworkRouteClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkRoute");
+ if(env->ExceptionCheck() || virtualNetworkRouteClass == NULL)
+ {
+ return NULL;
+ }
+
+ routeConstructor = lookup.findMethod(virtualNetworkRouteClass, "<init>", "()V");
+ if(env->ExceptionCheck() || routeConstructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject routeObj = env->NewObject(virtualNetworkRouteClass, routeConstructor);
+ if(env->ExceptionCheck() || routeObj == NULL)
+ {
+ return NULL;
+ }
+
+ jfieldID targetField = NULL;
+ jfieldID viaField = NULL;
+ jfieldID flagsField = NULL;
+ jfieldID metricField = NULL;
+
+ targetField = lookup.findField(virtualNetworkRouteClass, "target",
+ "Ljava/net/InetSocketAddress;");
+ if(env->ExceptionCheck() || targetField == NULL)
+ {
+ return NULL;
+ }
+
+ viaField = lookup.findField(virtualNetworkRouteClass, "via",
+ "Ljava/net/InetSocketAddress;");
+ if(env->ExceptionCheck() || targetField == NULL)
+ {
+ return NULL;
+ }
+
+ flagsField = lookup.findField(virtualNetworkRouteClass, "flags", "I");
+ if(env->ExceptionCheck() || flagsField == NULL)
+ {
+ return NULL;
+ }
+
+ metricField = lookup.findField(virtualNetworkRouteClass, "metric", "I");
+ if(env->ExceptionCheck() || metricField == NULL)
+ {
+ return NULL;
+ }
+
+ jobject targetObj = newInetSocketAddress(env, route.target);
+ jobject viaObj = newInetSocketAddress(env, route.via);
+
+ env->SetObjectField(routeObj, targetField, targetObj);
+ env->SetObjectField(routeObj, viaField, viaObj);
+ env->SetIntField(routeObj, flagsField, (jint)route.flags);
+ env->SetIntField(routeObj, metricField, (jint)route.metric);
+
+ return routeObj;
+}
+
#ifdef __cplusplus
}
-#endif \ No newline at end of file
+#endif
+
diff --git a/java/jni/ZT_jniutils.h b/java/jni/ZT_jniutils.h
index 34dfc471..56b63179 100644
--- a/java/jni/ZT_jniutils.h
+++ b/java/jni/ZT_jniutils.h
@@ -1,3 +1,21 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
#ifndef ZT_jniutils_h_
#define ZT_jniutils_h_
#include <stdio.h>
@@ -10,17 +28,31 @@ extern "C" {
#define LOG_TAG "ZeroTierOneJNI"
-#if __ANDROID__
+#if defined(__ANDROID__)
+
#include <android/log.h>
-#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
-#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+
+ #if !defined(NDEBUG)
+ #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+ #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+ #else
+ #define LOGV(...)
+ #define LOGD(...)
+ #endif
+
+ #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+ #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#else
-#define LOGV(...) fprintf(stdout, __VA_ARGS__)
-#define LOGI(...) fprintf(stdout, __VA_ARGS__)
-#define LOGD(...) fprintf(stdout, __VA_ARGS__)
-#define LOGE(...) fprintf(stdout, __VA_ARGS__)
+ #if !defined(NDEBUG)
+ #define LOGV(...) fprintf(stdout, __VA_ARGS__)
+ #define LOGD(...) fprintf(stdout, __VA_ARGS__)
+ #else
+ #define LOGV(...)
+ #define LOGD(...)
+ #endif
+
+ #define LOGI(...) fprintf(stdout, __VA_ARGS__)
+ #define LOGE(...) fprintf(stdout, __VA_ARGS__)
#endif
jobject createResultObject(JNIEnv *env, ZT_ResultCode code);
@@ -42,6 +74,8 @@ jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &config);
jobject newVersion(JNIEnv *env, int major, int minor, int rev);
+jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route);
+
#ifdef __cplusplus
}
#endif
diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp
index 4d9a2102..9a36e99b 100644
--- a/java/jni/com_zerotierone_sdk_Node.cpp
+++ b/java/jni/com_zerotierone_sdk_Node.cpp
@@ -32,6 +32,8 @@
#include <ZeroTierOne.h>
#include "Mutex.hpp"
+#include "PortMapper.hpp"
+
#include <map>
#include <string>
#include <assert.h>
@@ -56,7 +58,13 @@ namespace {
, eventListener(NULL)
, frameListener(NULL)
, configListener(NULL)
- {}
+ , pathChecker(NULL)
+ , callbacks(NULL)
+ , portMapper(NULL)
+ {
+ callbacks = (ZT_Node_Callbacks*)malloc(sizeof(ZT_Node_Callbacks));
+ memset(callbacks, 0, sizeof(ZT_Node_Callbacks));
+ }
~JniRef()
{
@@ -69,9 +77,16 @@ namespace {
env->DeleteGlobalRef(eventListener);
env->DeleteGlobalRef(frameListener);
env->DeleteGlobalRef(configListener);
+ env->DeleteGlobalRef(pathChecker);
+
+ free(callbacks);
+ callbacks = NULL;
+
+ delete portMapper;
+ portMapper = NULL;
}
- uint64_t id;
+ int64_t id;
JavaVM *jvm;
@@ -83,12 +98,18 @@ namespace {
jobject eventListener;
jobject frameListener;
jobject configListener;
+ jobject pathChecker;
+
+ ZT_Node_Callbacks *callbacks;
+
+ ZeroTier::PortMapper *portMapper;
};
int VirtualNetworkConfigFunctionCallback(
ZT_Node *node,
void *userData,
+ void *threadData,
uint64_t nwid,
void **,
enum ZT_VirtualNetworkConfigOperation operation,
@@ -99,6 +120,11 @@ namespace {
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->configListener == NULL) {
+ LOGE("configListener is NULL");
+ return -1;
+ }
+
jclass configListenerClass = env->GetObjectClass(ref->configListener);
if(configListenerClass == NULL)
{
@@ -130,13 +156,14 @@ namespace {
}
return env->CallIntMethod(
- ref->configListener,
- configListenerCallbackMethod,
+ ref->configListener,
+ configListenerCallbackMethod,
(jlong)nwid, operationObject, networkConfigObject);
}
void VirtualNetworkFrameFunctionCallback(ZT_Node *node,
void *userData,
+ void *threadData,
uint64_t nwid,
void**,
uint64_t sourceMac,
@@ -147,13 +174,19 @@ namespace {
unsigned int frameLength)
{
LOGV("VirtualNetworkFrameFunctionCallback");
+#ifndef NDEBUG
unsigned char* local = (unsigned char*)frameData;
LOGV("Type Bytes: 0x%02x%02x", local[12], local[13]);
+#endif
JniRef *ref = (JniRef*)userData;
assert(ref->node == node);
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->frameListener == NULL) {
+ LOGE("frameListener is NULL");
+ return;
+ }
jclass frameListenerClass = env->GetObjectClass(ref->frameListener);
if(env->ExceptionCheck() || frameListenerClass == NULL)
@@ -194,109 +227,232 @@ namespace {
void EventCallback(ZT_Node *node,
void *userData,
- enum ZT_Event event,
- const void *data)
- {
+ void *threadData,
+ enum ZT_Event event,
+ const void *data) {
LOGV("EventCallback");
- JniRef *ref = (JniRef*)userData;
- if(ref->node != node && event != ZT_EVENT_UP)
- {
+ JniRef *ref = (JniRef *) userData;
+ if (ref->node != node && event != ZT_EVENT_UP) {
LOGE("Nodes not equal. ref->node %p, node %p. Event: %d", ref->node, node, event);
return;
}
JNIEnv *env = NULL;
- ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ ref->jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
+ if (ref->eventListener == NULL) {
+ LOGE("eventListener is NULL");
+ return;
+ }
jclass eventListenerClass = env->GetObjectClass(ref->eventListener);
- if(eventListenerClass == NULL)
- {
+ if (eventListenerClass == NULL) {
LOGE("Couldn't class for EventListener instance");
return;
}
jmethodID onEventMethod = lookup.findMethod(eventListenerClass,
- "onEvent", "(Lcom/zerotier/sdk/Event;)V");
- if(onEventMethod == NULL)
- {
+ "onEvent", "(Lcom/zerotier/sdk/Event;)V");
+ if (onEventMethod == NULL) {
LOGE("Couldn't find onEvent method");
return;
}
jmethodID onTraceMethod = lookup.findMethod(eventListenerClass,
- "onTrace", "(Ljava/lang/String;)V");
- if(onTraceMethod == NULL)
- {
+ "onTrace", "(Ljava/lang/String;)V");
+ if (onTraceMethod == NULL) {
LOGE("Couldn't find onTrace method");
return;
}
jobject eventObject = createEvent(env, event);
- if(eventObject == NULL)
- {
+ if (eventObject == NULL) {
return;
}
- switch(event)
- {
- case ZT_EVENT_UP:
- {
- LOGD("Event Up");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+ switch (event) {
+ case ZT_EVENT_UP: {
+ LOGD("Event Up");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_OFFLINE: {
+ LOGD("Event Offline");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_ONLINE: {
+ LOGD("Event Online");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_DOWN: {
+ LOGD("Event Down");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: {
+ LOGV("Identity Collision");
+ // call onEvent()
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ }
+ break;
+ case ZT_EVENT_TRACE: {
+ LOGV("Trace Event");
+ // call onTrace()
+ if (data != NULL) {
+ const char *message = (const char *) data;
+ jstring messageStr = env->NewStringUTF(message);
+ env->CallVoidMethod(ref->eventListener, onTraceMethod, messageStr);
+ }
+ }
+ break;
+ case ZT_EVENT_USER_MESSAGE:
+ case ZT_EVENT_REMOTE_TRACE:
+ default:
+ break;
}
- case ZT_EVENT_OFFLINE:
- {
- LOGD("Event Offline");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+ }
+
+ void StatePutFunction(
+ ZT_Node *node,
+ void *userData,
+ void *threadData,
+ enum ZT_StateObjectType type,
+ const uint64_t id[2],
+ const void *buffer,
+ int bufferLength) {
+ char p[4096] = {0};
+ bool secure = false;
+ switch (type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ snprintf(p, sizeof(p), "identity.public");
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ snprintf(p, sizeof(p), "identity.secret");
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ snprintf(p, sizeof(p), "planet");
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ snprintf(p, sizeof(p), "moons.d/%.16llx.moon", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ snprintf(p, sizeof(p), "networks.d/%.16llx.conf", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ snprintf(p, sizeof(p), "peers.d/%.10llx", (unsigned long long)id[0]);
+ break;
+ default:
+ return;
}
- case ZT_EVENT_ONLINE:
- {
- LOGD("Event Online");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+
+ if (strlen(p) < 1) {
+ return;
}
- case ZT_EVENT_DOWN:
+
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ if (ref->dataStorePutListener == NULL) {
+ LOGE("dataStorePutListener is NULL");
+ return;
+ }
+
+ jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener);
+ if (dataStorePutClass == NULL)
{
- LOGD("Event Down");
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
- break;
+ LOGE("Couldn't find class for DataStorePutListener instance");
+ return;
}
- case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION:
+
+ jmethodID dataStorePutCallbackMethod = lookup.findMethod(
+ dataStorePutClass,
+ "onDataStorePut",
+ "(Ljava/lang/String;[BZ)I");
+ if(dataStorePutCallbackMethod == NULL)
{
- LOGV("Identity Collision");
- // call onEvent()
- env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ LOGE("Couldn't find onDataStorePut method");
+ return;
}
- break;
- case ZT_EVENT_TRACE:
+
+ jmethodID deleteMethod = lookup.findMethod(dataStorePutClass,
+ "onDelete", "(Ljava/lang/String;)I");
+ if(deleteMethod == NULL)
{
- LOGV("Trace Event");
- // call onTrace()
- if(data != NULL)
+ LOGE("Couldn't find onDelete method");
+ return;
+ }
+
+ jstring nameStr = env->NewStringUTF(p);
+
+ if (bufferLength >= 0) {
+ LOGD("JNI: Write file: %s", p);
+ // set operation
+ jbyteArray bufferObj = env->NewByteArray(bufferLength);
+ if(env->ExceptionCheck() || bufferObj == NULL)
{
- const char* message = (const char*)data;
- jstring messageStr = env->NewStringUTF(message);
- env->CallVoidMethod(ref->eventListener, onTraceMethod, messageStr);
+ LOGE("Error creating byte array buffer!");
+ return;
}
- }
- break;
+
+ env->SetByteArrayRegion(bufferObj, 0, bufferLength, (jbyte*)buffer);
+
+ env->CallIntMethod(ref->dataStorePutListener,
+ dataStorePutCallbackMethod,
+ nameStr, bufferObj, secure);
+ } else {
+ LOGD("JNI: Delete file: %s", p);
+ env->CallIntMethod(ref->dataStorePutListener, deleteMethod, nameStr);
}
}
- long DataStoreGetFunction(ZT_Node *node,
- void *userData,
- const char *objectName,
- void *buffer,
- unsigned long bufferSize,
- unsigned long bufferIndex,
- unsigned long *out_objectSize)
- {
+ int StateGetFunction(
+ ZT_Node *node,
+ void *userData,
+ void *threadData,
+ ZT_StateObjectType type,
+ const uint64_t id[2],
+ void *buffer,
+ unsigned int bufferLength) {
+ char p[4096] = {0};
+ switch (type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ snprintf(p, sizeof(p), "identity.public");
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ snprintf(p, sizeof(p), "identity.secret");
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ snprintf(p, sizeof(p), "planet");
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ snprintf(p, sizeof(p), "moons.d/%.16llx.moon", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ snprintf(p, sizeof(p), "networks.d/%.16llx.conf", (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ snprintf(p, sizeof(p), "peers.d/%.10llx", (unsigned long long)id[0]);
+ break;
+ default:
+ return -1;
+ }
+
+ if (strlen(p) < 1) {
+ return -1;
+ }
+
JniRef *ref = (JniRef*)userData;
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->dataStoreGetListener == NULL) {
+ LOGE("dataStoreGetListener is NULL");
+ return -2;
+ }
+
jclass dataStoreGetClass = env->GetObjectClass(ref->dataStoreGetListener);
if(dataStoreGetClass == NULL)
{
@@ -305,176 +461,254 @@ namespace {
}
jmethodID dataStoreGetCallbackMethod = lookup.findMethod(
- dataStoreGetClass,
- "onDataStoreGet",
- "(Ljava/lang/String;[BJ[J)J");
+ dataStoreGetClass,
+ "onDataStoreGet",
+ "(Ljava/lang/String;[B)J");
if(dataStoreGetCallbackMethod == NULL)
{
LOGE("Couldn't find onDataStoreGet method");
return -2;
}
- jstring nameStr = env->NewStringUTF(objectName);
+ jstring nameStr = env->NewStringUTF(p);
if(nameStr == NULL)
{
LOGE("Error creating name string object");
return -2; // out of memory
}
- jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ jbyteArray bufferObj = env->NewByteArray(bufferLength);
if(bufferObj == NULL)
{
- LOGE("Error creating byte[] buffer of size: %lu", bufferSize);
+ LOGE("Error creating byte[] buffer of size: %u", bufferLength);
return -2;
}
- jlongArray objectSizeObj = env->NewLongArray(1);
- if(objectSizeObj == NULL)
- {
- LOGE("Error creating long[1] array for actual object size");
- return -2; // couldn't create long[1] array
- }
+ LOGV("Calling onDataStoreGet(%s, %p)", p, buffer);
- LOGV("Calling onDataStoreGet(%s, %p, %lu, %p)",
- objectName, buffer, bufferIndex, objectSizeObj);
+ int retval = (int)env->CallLongMethod(
+ ref->dataStoreGetListener,
+ dataStoreGetCallbackMethod,
+ nameStr,
+ bufferObj);
- long retval = (long)env->CallLongMethod(
- ref->dataStoreGetListener, dataStoreGetCallbackMethod,
- nameStr, bufferObj, (jlong)bufferIndex, objectSizeObj);
+ LOGV("onDataStoreGet returned %d", retval);
if(retval > 0)
{
void *data = env->GetPrimitiveArrayCritical(bufferObj, NULL);
memcpy(buffer, data, retval);
env->ReleasePrimitiveArrayCritical(bufferObj, data, 0);
-
- jlong *objSize = (jlong*)env->GetPrimitiveArrayCritical(objectSizeObj, NULL);
- *out_objectSize = (unsigned long)objSize[0];
- env->ReleasePrimitiveArrayCritical(objectSizeObj, objSize, 0);
}
- LOGV("Out Object Size: %lu", *out_objectSize);
-
return retval;
}
- int DataStorePutFunction(ZT_Node *node,
+ int WirePacketSendFunction(ZT_Node *node,
void *userData,
- const char *objectName,
+ void *threadData,
+ int64_t localSocket,
+ const struct sockaddr_storage *remoteAddress,
const void *buffer,
- unsigned long bufferSize,
- int secure)
+ unsigned int bufferSize,
+ unsigned int ttl)
{
+ LOGV("WirePacketSendFunction(%lld, %p, %p, %d)", (long long)localSocket, remoteAddress, buffer, bufferSize);
JniRef *ref = (JniRef*)userData;
+ assert(ref->node == node);
+
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ if (ref->packetSender == NULL) {
+ LOGE("packetSender is NULL");
+ return -1;
+ }
- jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener);
- if(dataStorePutClass == NULL)
+ jclass packetSenderClass = env->GetObjectClass(ref->packetSender);
+ if(packetSenderClass == NULL)
{
- LOGE("Couldn't find class for DataStorePutListener instance");
+ LOGE("Couldn't find class for PacketSender instance");
return -1;
}
- jmethodID dataStorePutCallbackMethod = lookup.findMethod(
- dataStorePutClass,
- "onDataStorePut",
- "(Ljava/lang/String;[BZ)I");
- if(dataStorePutCallbackMethod == NULL)
+ jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass,
+ "onSendPacketRequested", "(JLjava/net/InetSocketAddress;[BI)I");
+ if(packetSenderCallbackMethod == NULL)
{
- LOGE("Couldn't find onDataStorePut method");
+ LOGE("Couldn't find onSendPacketRequested method");
return -2;
}
- jmethodID deleteMethod = lookup.findMethod(dataStorePutClass,
- "onDelete", "(Ljava/lang/String;)I");
- if(deleteMethod == NULL)
- {
- LOGE("Couldn't find onDelete method");
- return -3;
+ jobject remoteAddressObj = newInetSocketAddress(env, *remoteAddress);
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
+ int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, localSocket, remoteAddressObj, bufferObj);
+
+ LOGV("JNI Packet Sender returned: %d", retval);
+ return retval;
+ }
+
+ int PathCheckFunction(ZT_Node *node,
+ void *userPtr,
+ void *threadPtr,
+ uint64_t address,
+ int64_t localSocket,
+ const struct sockaddr_storage *remoteAddress)
+ {
+ JniRef *ref = (JniRef*)userPtr;
+ assert(ref->node == node);
+
+ if(ref->pathChecker == NULL) {
+ return true;
}
- jstring nameStr = env->NewStringUTF(objectName);
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
- if(buffer == NULL)
+ jclass pathCheckerClass = env->GetObjectClass(ref->pathChecker);
+ if(pathCheckerClass == NULL)
{
- LOGD("JNI: Delete file: %s", objectName);
- // delete operation
- return env->CallIntMethod(
- ref->dataStorePutListener, deleteMethod, nameStr);
+ LOGE("Couldn't find class for PathChecker instance");
+ return true;
}
- else
+
+ jmethodID pathCheckCallbackMethod = lookup.findMethod(pathCheckerClass,
+ "onPathCheck", "(JJLjava/net/InetSocketAddress;)Z");
+ if(pathCheckCallbackMethod == NULL)
{
- LOGD("JNI: Write file: %s", objectName);
- // set operation
- jbyteArray bufferObj = env->NewByteArray(bufferSize);
- if(env->ExceptionCheck() || bufferObj == NULL)
- {
- LOGE("Error creating byte array buffer!");
- return -4;
- }
+ LOGE("Couldn't find onPathCheck method implementation");
+ return true;
+ }
- env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
- bool bsecure = secure != 0;
+ struct sockaddr_storage nullAddress = {0};
+ jobject remoteAddressObj = NULL;
- return env->CallIntMethod(ref->dataStorePutListener,
- dataStorePutCallbackMethod,
- nameStr, bufferObj, bsecure);
+ if(memcmp(remoteAddress, &nullAddress, sizeof(sockaddr_storage)) != 0)
+ {
+ remoteAddressObj = newInetSocketAddress(env, *remoteAddress);
}
+
+ return env->CallBooleanMethod(ref->pathChecker, pathCheckCallbackMethod, address, localSocket, remoteAddressObj);
}
- int WirePacketSendFunction(ZT_Node *node,
- void *userData,
- const struct sockaddr_storage *localAddress,
- const struct sockaddr_storage *remoteAddress,
- const void *buffer,
- unsigned int bufferSize,
- unsigned int ttl)
+ int PathLookupFunction(ZT_Node *node,
+ void *userPtr,
+ void *threadPtr,
+ uint64_t address,
+ int ss_family,
+ struct sockaddr_storage *result)
{
- LOGV("WirePacketSendFunction(%p, %p, %p, %d)", localAddress, remoteAddress, buffer, bufferSize);
- JniRef *ref = (JniRef*)userData;
+ JniRef *ref = (JniRef*)userPtr;
assert(ref->node == node);
+ if(ref->pathChecker == NULL) {
+ return false;
+ }
+
JNIEnv *env = NULL;
ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+ jclass pathCheckerClass = env->GetObjectClass(ref->pathChecker);
+ if(pathCheckerClass == NULL)
+ {
+ LOGE("Couldn't find class for PathChecker instance");
+ return false;
+ }
+
+ jmethodID pathLookupMethod = lookup.findMethod(pathCheckerClass,
+ "onPathLookup", "(JI)Ljava/net/InetSocketAddress;");
+ if(pathLookupMethod == NULL) {
+ return false;
+ }
- jclass packetSenderClass = env->GetObjectClass(ref->packetSender);
- if(packetSenderClass == NULL)
+ jobject sockAddressObject = env->CallObjectMethod(ref->pathChecker, pathLookupMethod, address, ss_family);
+ if(sockAddressObject == NULL)
{
- LOGE("Couldn't find class for PacketSender instance");
- return -1;
+ LOGE("Unable to call onPathLookup implementation");
+ return false;
}
- jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass,
- "onSendPacketRequested", "(Ljava/net/InetSocketAddress;Ljava/net/InetSocketAddress;[BI)I");
- if(packetSenderCallbackMethod == NULL)
+ jclass inetSockAddressClass = env->GetObjectClass(sockAddressObject);
+ if(inetSockAddressClass == NULL)
{
- LOGE("Couldn't find onSendPacketRequested method");
- return -2;
+ LOGE("Unable to find InetSocketAddress class");
+ return false;
+ }
+
+ jmethodID getAddressMethod = lookup.findMethod(inetSockAddressClass, "getAddress", "()Ljava/net/InetAddress;");
+ if(getAddressMethod == NULL)
+ {
+ LOGE("Unable to find InetSocketAddress.getAddress() method");
+ return false;
+ }
+
+ jmethodID getPortMethod = lookup.findMethod(inetSockAddressClass, "getPort", "()I");
+ if(getPortMethod == NULL)
+ {
+ LOGE("Unable to find InetSocketAddress.getPort() method");
+ return false;
}
+
+ jint port = env->CallIntMethod(sockAddressObject, getPortMethod);
+ jobject addressObject = env->CallObjectMethod(sockAddressObject, getAddressMethod);
- jobject localAddressObj = NULL;
- if(memcmp(localAddress, &ZT_SOCKADDR_NULL, sizeof(sockaddr_storage)) != 0)
+ jclass inetAddressClass = lookup.findClass("java/net/InetAddress");
+ if(inetAddressClass == NULL)
{
- localAddressObj = newInetSocketAddress(env, *localAddress);
+ LOGE("Unable to find InetAddress class");
+ return false;
}
- jobject remoteAddressObj = newInetSocketAddress(env, *remoteAddress);
- jbyteArray bufferObj = env->NewByteArray(bufferSize);
- env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
- int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, localAddressObj, remoteAddressObj, bufferObj);
+ getAddressMethod = lookup.findMethod(inetAddressClass, "getAddress", "()[B");
+ if(getAddressMethod == NULL)
+ {
+ LOGE("Unable to find InetAddress.getAddress() method");
+ return false;
+ }
- LOGV("JNI Packet Sender returned: %d", retval);
- return retval;
+ jbyteArray addressBytes = (jbyteArray)env->CallObjectMethod(addressObject, getAddressMethod);
+ if(addressBytes == NULL)
+ {
+ LOGE("Unable to call InetAddress.getBytes()");
+ return false;
+ }
+
+ int addressSize = env->GetArrayLength(addressBytes);
+ if(addressSize == 4)
+ {
+ // IPV4
+ sockaddr_in *addr = (sockaddr_in*)result;
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(port);
+
+ void *data = env->GetPrimitiveArrayCritical(addressBytes, NULL);
+ memcpy(&addr->sin_addr, data, 4);
+ env->ReleasePrimitiveArrayCritical(addressBytes, data, 0);
+ }
+ else if (addressSize == 16)
+ {
+ // IPV6
+ sockaddr_in6 *addr = (sockaddr_in6*)result;
+ addr->sin6_family = AF_INET6;
+ addr->sin6_port = htons(port);
+ void *data = env->GetPrimitiveArrayCritical(addressBytes, NULL);
+ memcpy(&addr->sin6_addr, data, 16);
+ env->ReleasePrimitiveArrayCritical(addressBytes, data, 0);
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
}
- typedef std::map<uint64_t, JniRef*> NodeMap;
+ typedef std::map<int64_t, JniRef*> NodeMap;
static NodeMap nodeMap;
ZeroTier::Mutex nodeMapMutex;
- ZT_Node* findNode(uint64_t nodeId)
+ ZT_Node* findNode(int64_t nodeId)
{
ZeroTier::Mutex::Lock lock(nodeMapMutex);
NodeMap::iterator found = nodeMap.find(nodeId);
@@ -487,7 +721,7 @@ namespace {
}
}
-JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
lookup.setJavaVM(vm);
return JNI_VERSION_1_6;
@@ -512,7 +746,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
ZT_Node *node;
JniRef *ref = new JniRef;
- ref->id = (uint64_t)now;
+ ref->id = (int64_t)now;
env->GetJavaVM(&ref->jvm);
jclass cls = env->GetObjectClass(obj);
@@ -602,17 +836,35 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
}
ref->eventListener = env->NewGlobalRef(tmp);
+ fid = lookup.findField(
+ cls, "pathChecker", "Lcom/zerotier/sdk/PathChecker;");
+ if(fid == NULL)
+ {
+ LOGE("no path checker?");
+ return NULL;
+ }
+
+ tmp = env->GetObjectField(obj, fid);
+ if(tmp != NULL)
+ {
+ ref->pathChecker = env->NewGlobalRef(tmp);
+ }
+
+ ref->callbacks->stateGetFunction = &StateGetFunction;
+ ref->callbacks->statePutFunction = &StatePutFunction;
+ ref->callbacks->wirePacketSendFunction = &WirePacketSendFunction;
+ ref->callbacks->virtualNetworkFrameFunction = &VirtualNetworkFrameFunctionCallback;
+ ref->callbacks->virtualNetworkConfigFunction = &VirtualNetworkConfigFunctionCallback;
+ ref->callbacks->eventCallback = &EventCallback;
+ ref->callbacks->pathCheckFunction = &PathCheckFunction;
+ ref->callbacks->pathLookupFunction = &PathLookupFunction;
+
ZT_ResultCode rc = ZT_Node_new(
&node,
ref,
- (uint64_t)now,
- &DataStoreGetFunction,
- &DataStorePutFunction,
- &WirePacketSendFunction,
- &VirtualNetworkFrameFunctionCallback,
- &VirtualNetworkConfigFunctionCallback,
NULL,
- &EventCallback);
+ ref->callbacks,
+ (int64_t)now);
if(rc != ZT_RESULT_OK)
{
@@ -628,10 +880,16 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
return resultObject;
}
+ uint64_t nodeId = ZT_Node_address(node);
+ if (nodeId != 0) {
+ char uniqueName[64];
+ snprintf(uniqueName, sizeof(uniqueName), "ZeroTier Android/%.10llx@%u", (unsigned long long)nodeId, 9993);
+ ref->portMapper = new ZeroTier::PortMapper(9993, uniqueName);
+ }
+
ZeroTier::Mutex::Lock lock(nodeMapMutex);
ref->node = node;
nodeMap.insert(std::make_pair(ref->id, ref));
-
return resultObject;
}
@@ -645,11 +903,11 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete(
JNIEnv *env, jobject obj, jlong id)
{
LOGV("Destroying ZT_Node struct");
- uint64_t nodeId = (uint64_t)id;
+ int64_t nodeId = (int64_t)id;
NodeMap::iterator found;
{
- ZeroTier::Mutex::Lock lock(nodeMapMutex);
+ ZeroTier::Mutex::Lock lock(nodeMapMutex);
found = nodeMap.find(nodeId);
}
@@ -675,9 +933,9 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete(
* Signature: (JJJJJII[B[J)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
- JNIEnv *env, jobject obj,
- jlong id,
- jlong in_now,
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
jlong in_nwid,
jlong in_sourceMac,
jlong in_destMac,
@@ -686,8 +944,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
jbyteArray in_frameData,
jlongArray out_nextBackgroundTaskDeadline)
{
- uint64_t nodeId = (uint64_t) id;
-
+ int64_t nodeId = (int64_t) id;
+
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -702,7 +960,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- uint64_t now = (uint64_t)in_now;
+ int64_t now = (int64_t)in_now;
uint64_t nwid = (uint64_t)in_nwid;
uint64_t sourceMac = (uint64_t)in_sourceMac;
uint64_t destMac = (uint64_t)in_destMac;
@@ -715,10 +973,11 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
memcpy(localData, frameData, frameLength);
env->ReleasePrimitiveArrayCritical(in_frameData, frameData, 0);
- uint64_t nextBackgroundTaskDeadline = 0;
+ int64_t nextBackgroundTaskDeadline = 0;
ZT_ResultCode rc = ZT_Node_processVirtualNetworkFrame(
node,
+ NULL,
now,
nwid,
sourceMac,
@@ -739,18 +998,18 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
/*
* Class: com_zerotier_sdk_Node
* Method: processWirePacket
- * Signature: (JJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode;
+ * Signature: (JJJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
- JNIEnv *env, jobject obj,
+ JNIEnv *env, jobject obj,
jlong id,
- jlong in_now,
- jobject in_localAddress,
+ jlong in_now,
+ jlong in_localSocket,
jobject in_remoteAddress,
jbyteArray in_packetData,
jlongArray out_nextBackgroundTaskDeadline)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -759,14 +1018,14 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline);
+ unsigned int nbtd_len = (unsigned int)env->GetArrayLength(out_nextBackgroundTaskDeadline);
if(nbtd_len < 1)
{
LOGE("nbtd_len < 1");
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- uint64_t now = (uint64_t)in_now;
+ int64_t now = (int64_t)in_now;
// get the java.net.InetSocketAddress class and getAddress() method
jclass inetAddressClass = lookup.findClass("java/net/InetAddress");
@@ -794,12 +1053,6 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
jmethodID inetSockGetAddressMethod = lookup.findMethod(
InetSocketAddressClass, "getAddress", "()Ljava/net/InetAddress;");
- jobject localAddrObj = NULL;
- if(in_localAddress != NULL)
- {
- localAddrObj = env->CallObjectMethod(in_localAddress, inetSockGetAddressMethod);
- }
-
jobject remoteAddrObject = env->CallObjectMethod(in_remoteAddress, inetSockGetAddressMethod);
if(remoteAddrObject == NULL)
@@ -810,7 +1063,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
jmethodID inetSock_getPort = lookup.findMethod(
InetSocketAddressClass, "getPort", "()I");
- if(env->ExceptionCheck() || inetSock_getPort == NULL)
+ if(env->ExceptionCheck() || inetSock_getPort == NULL)
{
LOGE("Couldn't find getPort method on InetSocketAddress");
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
@@ -834,48 +1087,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
}
unsigned int addrSize = env->GetArrayLength(remoteAddressArray);
-
-
- sockaddr_storage localAddress = {};
-
- if(localAddrObj == NULL)
- {
- localAddress = ZT_SOCKADDR_NULL;
- }
- else
- {
- int localPort = env->CallIntMethod(in_localAddress, inetSock_getPort);
- jbyteArray localAddressArray = (jbyteArray)env->CallObjectMethod(localAddrObj, getAddressMethod);
- if(localAddressArray != NULL)
- {
- unsigned int localAddrSize = env->GetArrayLength(localAddressArray);
- jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(localAddressArray, NULL);
-
- if(localAddrSize == 16)
- {
- sockaddr_in6 ipv6 = {};
- ipv6.sin6_family = AF_INET6;
- ipv6.sin6_port = htons(localPort);
- memcpy(ipv6.sin6_addr.s6_addr, addr, 16);
- memcpy(&localAddress, &ipv6, sizeof(sockaddr_in6));
- }
- else if(localAddrSize)
- {
- // IPV4 address
- sockaddr_in ipv4 = {};
- ipv4.sin_family = AF_INET;
- ipv4.sin_port = htons(localPort);
- memcpy(&ipv4.sin_addr, addr, 4);
- memcpy(&localAddress, &ipv4, sizeof(sockaddr_in));
- }
- else
- {
- localAddress = ZT_SOCKADDR_NULL;
- }
- env->ReleasePrimitiveArrayCritical(localAddressArray, addr, 0);
- }
- }
// get the address bytes
jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(remoteAddressArray, NULL);
@@ -908,7 +1120,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
}
env->ReleasePrimitiveArrayCritical(remoteAddressArray, addr, 0);
- unsigned int packetLength = env->GetArrayLength(in_packetData);
+ unsigned int packetLength = (unsigned int)env->GetArrayLength(in_packetData);
if(packetLength == 0)
{
LOGE("Empty packet?!?");
@@ -919,17 +1131,18 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
memcpy(localData, packetData, packetLength);
env->ReleasePrimitiveArrayCritical(in_packetData, packetData, 0);
- uint64_t nextBackgroundTaskDeadline = 0;
+ int64_t nextBackgroundTaskDeadline = 0;
ZT_ResultCode rc = ZT_Node_processWirePacket(
node,
+ NULL,
now,
- &localAddress,
+ in_localSocket,
&remoteAddress,
localData,
packetLength,
&nextBackgroundTaskDeadline);
- if(rc != ZT_RESULT_OK)
+ if(rc != ZT_RESULT_OK)
{
LOGE("ZT_Node_processWirePacket returned: %d", rc);
}
@@ -949,12 +1162,12 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
* Signature: (JJ[J)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
- JNIEnv *env, jobject obj,
+ JNIEnv *env, jobject obj,
jlong id,
jlong in_now,
jlongArray out_nextBackgroundTaskDeadline)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -968,10 +1181,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
}
- uint64_t now = (uint64_t)in_now;
- uint64_t nextBackgroundTaskDeadline = 0;
+ int64_t now = (int64_t)in_now;
+ int64_t nextBackgroundTaskDeadline = 0;
- ZT_ResultCode rc = ZT_Node_processBackgroundTasks(node, now, &nextBackgroundTaskDeadline);
+ ZT_ResultCode rc = ZT_Node_processBackgroundTasks(node, NULL, now, &nextBackgroundTaskDeadline);
jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL);
outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
@@ -988,7 +1201,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -998,7 +1211,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
uint64_t nwid = (uint64_t)in_nwid;
- ZT_ResultCode rc = ZT_Node_join(node, nwid, NULL);
+ ZT_ResultCode rc = ZT_Node_join(node, nwid, NULL, NULL);
return createResultObject(env, rc);
}
@@ -1011,7 +1224,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave(
JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1021,8 +1234,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave(
uint64_t nwid = (uint64_t)in_nwid;
- ZT_ResultCode rc = ZT_Node_leave(node, nwid, NULL);
-
+ ZT_ResultCode rc = ZT_Node_leave(node, nwid, NULL, NULL);
+
return createResultObject(env, rc);
}
@@ -1032,13 +1245,13 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave(
* Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe(
- JNIEnv *env, jobject obj,
- jlong id,
+ JNIEnv *env, jobject obj,
+ jlong id,
jlong in_nwid,
jlong in_multicastGroup,
jlong in_multicastAdi)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1051,7 +1264,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe(
unsigned long multicastAdi = (unsigned long)in_multicastAdi;
ZT_ResultCode rc = ZT_Node_multicastSubscribe(
- node, nwid, multicastGroup, multicastAdi);
+ node, NULL, nwid, multicastGroup, multicastAdi);
return createResultObject(env, rc);
}
@@ -1062,13 +1275,13 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe(
* Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe(
- JNIEnv *env, jobject obj,
- jlong id,
+ JNIEnv *env, jobject obj,
+ jlong id,
jlong in_nwid,
jlong in_multicastGroup,
jlong in_multicastAdi)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1087,6 +1300,54 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe(
}
/*
+ * Class: com_zerotier_sdk_Node
+ * Method: orbit
+ * Signature: (JJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_orbit(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_moonWorldId,
+ jlong in_moonSeed)
+{
+ int64_t nodeId = (int64_t)id;
+ ZT_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t moonWorldId = (uint64_t)in_moonWorldId;
+ uint64_t moonSeed = (uint64_t)in_moonSeed;
+
+ ZT_ResultCode rc = ZT_Node_orbit(node, NULL, moonWorldId, moonSeed);
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: deorbit
+ * Signature: (JJ)L/com/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_deorbit(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_moonWorldId)
+{
+ int64_t nodeId = (int64_t)id;
+ ZT_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t moonWorldId = (uint64_t)in_moonWorldId;
+
+ ZT_ResultCode rc = ZT_Node_deorbit(node, NULL, moonWorldId);
+ return createResultObject(env, rc);
+}
+
+/*
* Class: com_zerotier_sdk_Node
* Method: address
* Signature: (J)J
@@ -1094,7 +1355,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe(
JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address(
JNIEnv *env , jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1114,7 +1375,7 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address(
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
(JNIEnv *env, jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1131,7 +1392,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
{
return NULL;
}
-
+
nodeStatusConstructor = lookup.findMethod(
nodeStatusClass, "<init>", "()V");
if(nodeStatusConstructor == NULL)
@@ -1206,7 +1467,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig(
JNIEnv *env, jobject obj, jlong id, jlong nwid)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1215,7 +1476,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig(
}
ZT_VirtualNetworkConfig *vnetConfig = ZT_Node_networkConfig(node, nwid);
-
+
jobject vnetConfigObject = newNetworkConfig(env, *vnetConfig);
ZT_Node_freeQueryResult(node, vnetConfig);
@@ -1248,7 +1509,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version(
JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
JNIEnv *env, jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
@@ -1257,7 +1518,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
}
ZT_PeerList *peerList = ZT_Node_peers(node);
-
+
if(peerList == NULL)
{
LOGE("ZT_Node_peers returned NULL");
@@ -1296,7 +1557,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
{
jobject peerObj = newPeer(env, peerList->peers[i]);
env->SetObjectArrayElement(peerArrayObj, i, peerObj);
- if(env->ExceptionCheck())
+ if(env->ExceptionCheck())
{
LOGE("Error assigning Peer object to array");
break;
@@ -1317,7 +1578,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks(
JNIEnv *env, jobject obj, jlong id)
{
- uint64_t nodeId = (uint64_t) id;
+ int64_t nodeId = (int64_t) id;
ZT_Node *node = findNode(nodeId);
if(node == NULL)
{
diff --git a/java/jni/com_zerotierone_sdk_Node.h b/java/jni/com_zerotierone_sdk_Node.h
index 7c1011ab..8487d8af 100644
--- a/java/jni/com_zerotierone_sdk_Node.h
+++ b/java/jni/com_zerotierone_sdk_Node.h
@@ -37,7 +37,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame
* Signature: (JJLjava/net/InetSockAddress;Ljava/net/InetSockAddress;[B[J)Lcom/zerotier/sdk/ResultCode;
*/
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket
- (JNIEnv *, jobject, jlong, jlong, jobject, jobject, jbyteArray, jlongArray);
+ (JNIEnv *, jobject, jlong, jlong, jlong, jobject, jbyteArray, jlongArray);
/*
* Class: com_zerotier_sdk_Node
diff --git a/java/src/com/zerotier/sdk/DataStoreGetListener.java b/java/src/com/zerotier/sdk/DataStoreGetListener.java
index b525be63..317511e0 100644
--- a/java/src/com/zerotier/sdk/DataStoreGetListener.java
+++ b/java/src/com/zerotier/sdk/DataStoreGetListener.java
@@ -46,13 +46,9 @@ public interface DataStoreGetListener {
*
* @param name Name of the object in the data store
* @param out_buffer buffer to put the object in
- * @param bufferIndex index in the object to start reading
- * @param out_objectSize long[1] to be set to the actual size of the object if it exists.
- * @return the actual number of bytes read.
+ * @return size of the object
*/
public long onDataStoreGet(
String name,
- byte[] out_buffer,
- long bufferIndex,
- long[] out_objectSize);
+ byte[] out_buffer);
}
diff --git a/java/src/com/zerotier/sdk/Node.java b/java/src/com/zerotier/sdk/Node.java
index 4bc6e184..ef6ac9d2 100644
--- a/java/src/com/zerotier/sdk/Node.java
+++ b/java/src/com/zerotier/sdk/Node.java
@@ -74,6 +74,7 @@ public class Node {
private final EventListener eventListener;
private final VirtualNetworkFrameListener frameListener;
private final VirtualNetworkConfigListener configListener;
+ private final PathChecker pathChecker;
/**
* Create a new ZeroTier One node
@@ -88,6 +89,7 @@ public class Node {
* @param eventListener User written instance of the {@link EventListener} interface to receive status updates and non-fatal error notices. This instance must be unique per Node object.
* @param frameListener
* @param configListener User written instance of the {@link VirtualNetworkConfigListener} interface to be called when virtual LANs are created, deleted, or their config parameters change. This instance must be unique per Node object.
+ * @param pathChecker User written instance of the {@link PathChecker} interface. Not required and can be null.
*/
public Node(long now,
DataStoreGetListener getListener,
@@ -95,7 +97,8 @@ public class Node {
PacketSender sender,
EventListener eventListener,
VirtualNetworkFrameListener frameListener,
- VirtualNetworkConfigListener configListener) throws NodeException
+ VirtualNetworkConfigListener configListener,
+ PathChecker pathChecker) throws NodeException
{
this.nodeId = now;
@@ -105,6 +108,7 @@ public class Node {
this.eventListener = eventListener;
this.frameListener = frameListener;
this.configListener = configListener;
+ this.pathChecker = pathChecker;
ResultCode rc = node_init(now);
if(rc != ResultCode.RESULT_OK)
@@ -169,12 +173,12 @@ public class Node {
*/
public ResultCode processWirePacket(
long now,
- InetSocketAddress localAddress,
+ long localSocket,
InetSocketAddress remoteAddress,
byte[] packetData,
long[] nextBackgroundTaskDeadline) {
return processWirePacket(
- nodeId, now, localAddress, remoteAddress, packetData,
+ nodeId, now, localSocket, remoteAddress, packetData,
nextBackgroundTaskDeadline);
}
@@ -319,6 +323,34 @@ public class Node {
}
/**
+ * Add or update a moon
+ *
+ * Moons are persisted in the data store in moons.d/, so this can persist
+ * across invocations if the contents of moon.d are scanned and orbit is
+ * called for each on startup.
+ *
+ * @param moonWorldId Moon's world ID
+ * @param moonSeed If non-zero, the ZeroTier address of any member of the moon to query for moon definition
+ * @return Error if moon was invalid or failed to be added
+ */
+ public ResultCode orbit(
+ long moonWorldId,
+ long moonSeed) {
+ return orbit(nodeId, moonWorldId, moonSeed);
+ }
+
+ /**
+ * Remove a moon (does nothing if not present)
+ *
+ * @param moonWorldId World ID of moon to remove
+ * @return Error if anything bad happened
+ */
+ public ResultCode deorbit(
+ long moonWorldId) {
+ return deorbit(nodeId, moonWorldId);
+ }
+
+ /**
* Get this node's 40-bit ZeroTier address
*
* @return ZeroTier address (least significant 40 bits of 64-bit int)
@@ -394,7 +426,7 @@ public class Node {
private native ResultCode processWirePacket(
long nodeId,
long now,
- InetSocketAddress localAddress,
+ long localSocket,
InetSocketAddress remoteAddress,
byte[] packetData,
long[] nextBackgroundTaskDeadline);
@@ -420,6 +452,15 @@ public class Node {
long multicastGroup,
long multicastAdi);
+ private native ResultCode orbit(
+ long nodeId,
+ long moonWorldId,
+ long moonSeed);
+
+ private native ResultCode deorbit(
+ long nodeId,
+ long moonWorldId);
+
private native long address(long nodeId);
private native NodeStatus status(long nodeId);
diff --git a/java/src/com/zerotier/sdk/PacketSender.java b/java/src/com/zerotier/sdk/PacketSender.java
index 22893ec7..06ec01bc 100644
--- a/java/src/com/zerotier/sdk/PacketSender.java
+++ b/java/src/com/zerotier/sdk/PacketSender.java
@@ -37,13 +37,13 @@ public interface PacketSender {
* on failure. Note that success does not (of course) guarantee packet
* delivery. It only means that the packet appears to have been sent.</p>
*
- * @param localAddr {@link InetSocketAddress} to send from. Set to null if not specified.
+ * @param localSocket socket file descriptor to send from. Set to -1 if not specified.
* @param remoteAddr {@link InetSocketAddress} to send to
* @param packetData data to send
* @return 0 on success, any error code on failure.
*/
public int onSendPacketRequested(
- InetSocketAddress localAddr,
+ long localSocket,
InetSocketAddress remoteAddr,
byte[] packetData,
int ttl);
diff --git a/java/src/com/zerotier/sdk/PathChecker.java b/java/src/com/zerotier/sdk/PathChecker.java
new file mode 100644
index 00000000..6bf31df2
--- /dev/null
+++ b/java/src/com/zerotier/sdk/PathChecker.java
@@ -0,0 +1,45 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.net.InetSocketAddress;
+
+public interface PathChecker {
+ /**
+ * Callback to check whether a path should be used for ZeroTier traffic
+ *
+ * This function must return true if the path should be used.
+ *
+ * If no path check function is specified, ZeroTier will still exclude paths
+ * that overlap with ZeroTier-assigned and managed IP address blocks. But the
+ * use of a path check function is recommended to ensure that recursion does
+ * not occur in cases where addresses are assigned by the OS or managed by
+ * an out of band mechanism like DHCP. The path check function should examine
+ * all configured ZeroTier interfaces and check to ensure that the supplied
+ * addresses will not result in ZeroTier traffic being sent over a ZeroTier
+ * interface (recursion).
+ *
+ * Obviously this is not required in configurations where this can't happen,
+ * such as network containers or embedded.
+ *
+ * @param ztAddress ZeroTier address or 0 for none/any
+ * @param localSocket Local interface socket. -1 if unspecified
+ * @param remoteAddress remote address
+ */
+ boolean onPathCheck(long ztAddress, long localSocket, InetSocketAddress remoteAddress);
+
+ /**
+ * Function to get physical addresses for ZeroTier peers
+ *
+ * If provided this function will be occasionally called to get physical
+ * addresses that might be tried to reach a ZeroTier address.
+ *
+ * @param ztAddress ZeroTier address (least significant 40 bits)
+ * @param ss_family desired address family or -1 for any
+ * @return address and port of ztAddress or null
+ */
+ InetSocketAddress onPathLookup(long ztAddress, int ss_family);
+}
diff --git a/java/src/com/zerotier/sdk/Peer.java b/java/src/com/zerotier/sdk/Peer.java
index fb2d1065..eb3d7130 100644
--- a/java/src/com/zerotier/sdk/Peer.java
+++ b/java/src/com/zerotier/sdk/Peer.java
@@ -34,8 +34,6 @@ import java.util.ArrayList;
*/
public final class Peer {
private long address;
- private long lastUnicastFrame;
- private long lastMulticastFrame;
private int versionMajor;
private int versionMinor;
private int versionRev;
@@ -53,20 +51,6 @@ public final class Peer {
}
/**
- * Time we last received a unicast frame from this peer
- */
- public final long lastUnicastFrame() {
- return lastUnicastFrame;
- }
-
- /**
- * Time we last received a multicast rame from this peer
- */
- public final long lastMulticastFrame() {
- return lastMulticastFrame;
- }
-
- /**
* Remote major version or -1 if not known
*/
public final int versionMajor() {
diff --git a/java/src/com/zerotier/sdk/PeerPhysicalPath.java b/java/src/com/zerotier/sdk/PeerPhysicalPath.java
index d64ea56b..3f9a8612 100644
--- a/java/src/com/zerotier/sdk/PeerPhysicalPath.java
+++ b/java/src/com/zerotier/sdk/PeerPhysicalPath.java
@@ -37,7 +37,6 @@ public final class PeerPhysicalPath {
private long lastSend;
private long lastReceive;
private boolean fixed;
- private boolean active;
private boolean preferred;
private PeerPhysicalPath() {}
@@ -71,13 +70,6 @@ public final class PeerPhysicalPath {
}
/**
- * Is path active?
- */
- public final boolean isActive() {
- return active;
- }
-
- /**
* Is path preferred?
*/
public final boolean isPreferred() {
diff --git a/java/src/com/zerotier/sdk/PeerRole.java b/java/src/com/zerotier/sdk/PeerRole.java
index d7d55f05..fce183d9 100644
--- a/java/src/com/zerotier/sdk/PeerRole.java
+++ b/java/src/com/zerotier/sdk/PeerRole.java
@@ -34,12 +34,12 @@ public enum PeerRole {
PEER_ROLE_LEAF,
/**
- * relay node
+ * moon root
*/
- PEER_ROLE_RELAY,
+ PEER_ROLE_MOON,
/**
- * root server
+ * planetary root
*/
- PEER_ROLE_ROOT
+ PEER_ROLE_PLANET
} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
index fbcbd3a4..64512dad 100644
--- a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
+++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
@@ -50,6 +50,7 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
private boolean enabled;
private long netconfRevision;
private InetSocketAddress[] assignedAddresses;
+ private VirtualNetworkRoute[] routes;
private VirtualNetworkConfig() {
@@ -60,13 +61,24 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
if(assignedAddresses.length == cfg.assignedAddresses.length) {
for(int i = 0; i < assignedAddresses.length; ++i) {
if(!assignedAddresses[i].equals(cfg.assignedAddresses[i])) {
- return false;
+ aaEqual = false;
}
}
} else {
aaEqual = false;
}
+ boolean routesEqual = true;
+ if(routes.length == cfg.routes.length) {
+ for (int i = 0; i < routes.length; ++i) {
+ if (!routes[i].equals(cfg.routes[i])) {
+ routesEqual = false;
+ }
+ }
+ } else {
+ routesEqual = false;
+ }
+
return nwid == cfg.nwid &&
mac == cfg.mac &&
name.equals(cfg.name) &&
@@ -78,7 +90,7 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
broadcastEnabled == cfg.broadcastEnabled &&
portError == cfg.portError &&
enabled == cfg.enabled &&
- aaEqual;
+ aaEqual && routesEqual;
}
public int compareTo(VirtualNetworkConfig cfg) {
@@ -188,4 +200,11 @@ public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConf
public final InetSocketAddress[] assignedAddresses() {
return assignedAddresses;
}
+
+ /**
+ * ZeroTier-assigned routes (in {@link com.zerotier.sdk.VirtualNetworkRoute} objects)
+ *
+ * @return
+ */
+ public final VirtualNetworkRoute[] routes() { return routes; }
}
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkRoute.java b/java/src/com/zerotier/sdk/VirtualNetworkRoute.java
new file mode 100644
index 00000000..b89dce7b
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkRoute.java
@@ -0,0 +1,102 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.net.InetSocketAddress;
+
+public final class VirtualNetworkRoute implements Comparable<VirtualNetworkRoute>
+{
+ private VirtualNetworkRoute() {
+ target = null;
+ via = null;
+ flags = 0;
+ metric = 0;
+ }
+
+ /**
+ * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default
+ */
+ public InetSocketAddress target;
+
+ /**
+ * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway)
+ */
+ public InetSocketAddress via;
+
+ /**
+ * Route flags
+ */
+ public int flags;
+
+ /**
+ * Route metric (not currently used)
+ */
+ public int metric;
+
+
+ @Override
+ public int compareTo(VirtualNetworkRoute other) {
+ return target.toString().compareTo(other.target.toString());
+ }
+
+ public boolean equals(VirtualNetworkRoute other) {
+ boolean targetEquals;
+ if (target == null && other.target == null) {
+ targetEquals = true;
+ }
+ else if (target == null && other.target != null) {
+ targetEquals = false;
+ }
+ else if (target != null && other.target == null) {
+ targetEquals = false;
+ }
+ else {
+ targetEquals = target.equals(other.target);
+ }
+
+
+ boolean viaEquals;
+ if (via == null && other.via == null) {
+ viaEquals = true;
+ }
+ else if (via == null && other.via != null) {
+ viaEquals = false;
+ }
+ else if (via != null && other.via == null) {
+ viaEquals = false;
+ }
+ else {
+ viaEquals = via.equals(other.via);
+ }
+
+ return viaEquals &&
+ viaEquals &&
+ flags == other.flags &&
+ metric == other.metric;
+ }
+}
diff --git a/linux-build-farm/README.md b/linux-build-farm/README.md
deleted file mode 100644
index 8055eb0b..00000000
--- a/linux-build-farm/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Dockerized Linux Build Farm
-======
-
-This subfolder contains Dockerfiles and a script to build Linux packages for a variety of Linux distributions. It's also an excellent way to test your CPU fans and stress test your disk.
-
-Running `build.sh` with no arguments builds everything. You can run `build.sh` with the name of a distro (e.g. centos-7) to only build that. Both 32 and 64 bit packages are built except where no 32-bit version of the distribution exists.
-
-The `make-apt-repos.sh` and `make-rpm-repos.sh` scripts build repositories. They may require some editing for outside-of-ZeroTier use, and be careful with the apt one if you have an existing *aptly* configuration.
diff --git a/linux-build-farm/amazon-2016.03/x64/Dockerfile b/linux-build-farm/amazon-2016.03/x64/Dockerfile
deleted file mode 100644
index bd1a246a..00000000
--- a/linux-build-farm/amazon-2016.03/x64/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-#FROM ambakshi/amazon-linux:2016.03
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN yum update -y
-#RUN yum install -y epel-release
-#RUN yum install -y make development-tools rpmdevtools clang gcc-c++ ruby ruby-devel
-
-#RUN gem install ronn
-
-FROM zerotier/zt1-build-amazon-2016.03-x64-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/build.sh b/linux-build-farm/build.sh
deleted file mode 100755
index 0eb7c5d2..00000000
--- a/linux-build-farm/build.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin
-
-subdirs=$*
-if [ ! -n "$subdirs" ]; then
- subdirs=`find . -type d -name '*-*' -printf '%f '`
-fi
-
-if [ ! -d ./ubuntu-trusty ]; then
- echo 'Must run from linux-build-farm subfolder.'
- exit 1
-fi
-
-rm -f zt1-src.tar.gz
-cd ..
-git archive --format=tar.gz --prefix=ZeroTierOne/ -o linux-build-farm/zt1-src.tar.gz HEAD
-cd linux-build-farm
-
-# Note that --privileged is used so we can bind mount VM shares when building in a VM.
-# It has no other impact or purpose, but probably doesn't matter here in any case.
-
-for distro in $subdirs; do
- echo
- echo "--- BUILDING FOR $distro ---"
- echo
-
- cd $distro
-
- if [ -d x64 ]; then
- cd x64
- mv ../../zt1-src.tar.gz .
- docker build -t zt1-build-${distro}-x64 .
- mv zt1-src.tar.gz ../..
- cd ..
- fi
-
- if [ -d x86 ]; then
- cd x86
- mv ../../zt1-src.tar.gz .
- docker build -t zt1-build-${distro}-x86 .
- mv zt1-src.tar.gz ../..
- cd ..
- fi
-
- rm -f *.deb *.rpm
-
-# exit 0
-
- if [ ! -n "`echo $distro | grep -F debian`" -a ! -n "`echo $distro | grep -F ubuntu`" ]; then
- if [ -d x64 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x64 /bin/bash -c 'cd /ZeroTierOne ; make redhat ; cd .. ; cp `find /root/rpmbuild -type f -name *.rpm` /artifacts ; ls -l /artifacts'
- fi
- if [ -d x86 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x86 /bin/bash -c 'cd /ZeroTierOne ; make redhat ; cd .. ; cp `find /root/rpmbuild -type f -name *.rpm` /artifacts ; ls -l /artifacts'
- fi
- else
- if [ -d x64 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x64 /bin/bash -c 'cd /ZeroTierOne ; make debian ; cd .. ; cp *.deb /artifacts ; ls -l /artifacts'
- fi
- if [ -d x86 ]; then
- docker run --rm -v `pwd`:/artifacts --privileged -it zt1-build-${distro}-x86 /bin/bash -c 'cd /ZeroTierOne ; make debian ; cd .. ; cp *.deb /artifacts ; ls -l /artifacts'
- fi
- fi
-
- cd ..
-done
-
-rm -f zt1-src.tar.gz
diff --git a/linux-build-farm/centos-6/x64/Dockerfile b/linux-build-farm/centos-6/x64/Dockerfile
deleted file mode 100644
index 2796e422..00000000
--- a/linux-build-farm/centos-6/x64/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM centos:6
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y epel-release
-RUN yum install -y make development-tools rpmdevtools clang gcc-c++ tar
-
-RUN yum install -y nodejs npm
-
-# Stop use of http-parser-devel which is installed by nodejs/npm
-RUN rm -f /usr/include/http_parser.h
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/centos-6/x86/Dockerfile b/linux-build-farm/centos-6/x86/Dockerfile
deleted file mode 100644
index 8192d139..00000000
--- a/linux-build-farm/centos-6/x86/Dockerfile
+++ /dev/null
@@ -1,13 +0,0 @@
-FROM toopher/centos-i386:centos6
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y epel-release
-RUN yum install -y make development-tools rpmdevtools clang gcc-c++ tar
-
-RUN yum install -y nodejs npm
-
-# Stop use of http-parser-devel which is installed by nodejs/npm
-RUN rm -f /usr/include/http_parser.h
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/centos-7/x64/Dockerfile b/linux-build-farm/centos-7/x64/Dockerfile
deleted file mode 100644
index 10b58402..00000000
--- a/linux-build-farm/centos-7/x64/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM centos:7
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y epel-release
-RUN yum install -y make development-tools rpmdevtools clang gcc-c++ ruby ruby-devel
-
-RUN gem install ronn
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/centos-7/x86/Dockerfile b/linux-build-farm/centos-7/x86/Dockerfile
deleted file mode 100644
index a637a8d3..00000000
--- a/linux-build-farm/centos-7/x86/Dockerfile
+++ /dev/null
@@ -1,22 +0,0 @@
-#FROM zerotier/centos7-32bit
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN echo 'i686-redhat-linux' >/etc/rpm/platform
-
-#RUN yum update -y
-#RUN yum install -y make development-tools rpmdevtools http-parser-devel lz4-devel libnatpmp-devel
-
-#RUN yum install -y gcc-c++
-#RUN rpm --install --force https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
-#RUN rpm --install --force ftp://rpmfind.net/linux/centos/6.8/os/i386/Packages/libffi-3.0.5-3.2.el6.i686.rpm
-#RUN yum install -y clang
-
-FROM zerotier/zt1-build-centos-7-x86-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum install -y ruby ruby-devel
-RUN gem install ronn
-
-#RUN rpm --erase http-parser-devel lz4-devel libnatpmp-devel
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-jessie/x64/Dockerfile b/linux-build-farm/debian-jessie/x64/Dockerfile
deleted file mode 100644
index 316c1d83..00000000
--- a/linux-build-farm/debian-jessie/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM debian:jessie
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.5
-
-RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-jessie/x86/Dockerfile b/linux-build-farm/debian-jessie/x86/Dockerfile
deleted file mode 100644
index 3ad83329..00000000
--- a/linux-build-farm/debian-jessie/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM 32bit/debian:jessie
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.5
-
-RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-stretch/x64/Dockerfile b/linux-build-farm/debian-stretch/x64/Dockerfile
deleted file mode 100644
index c973c2b7..00000000
--- a/linux-build-farm/debian-stretch/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM debian:stretch
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
-
-#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-stretch/x86/Dockerfile b/linux-build-farm/debian-stretch/x86/Dockerfile
deleted file mode 100644
index bfc7a86f..00000000
--- a/linux-build-farm/debian-stretch/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM mcandre/docker-debian-32bit:stretch
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang
-
-#RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-wheezy/x64/Dockerfile b/linux-build-farm/debian-wheezy/x64/Dockerfile
deleted file mode 100644
index 77e1c325..00000000
--- a/linux-build-farm/debian-wheezy/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM debian:wheezy
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
-
-RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
-RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules
diff --git a/linux-build-farm/debian-wheezy/x86/Dockerfile b/linux-build-farm/debian-wheezy/x86/Dockerfile
deleted file mode 100644
index 1f0117d2..00000000
--- a/linux-build-farm/debian-wheezy/x86/Dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-#FROM tubia/debian:wheezy
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN apt-get update
-#RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
-
-FROM zerotier/zt1-build-debian-wheezy-x86-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
-
-RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
-RUN mv -f /ZeroTierOne/debian/rules.wheezy /ZeroTierOne/debian/rules
diff --git a/linux-build-farm/fedora-22/x64/Dockerfile b/linux-build-farm/fedora-22/x64/Dockerfile
deleted file mode 100644
index 6da0a921..00000000
--- a/linux-build-farm/fedora-22/x64/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM fedora:22
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN yum update -y
-RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
-
-RUN rpm --erase http-parser-devel
-RUN yum install -y rubygem-ronn ruby
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/fedora-22/x86/Dockerfile b/linux-build-farm/fedora-22/x86/Dockerfile
deleted file mode 100644
index 3c24b844..00000000
--- a/linux-build-farm/fedora-22/x86/Dockerfile
+++ /dev/null
@@ -1,19 +0,0 @@
-#FROM nickcis/fedora-32:22
-#MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-#RUN mkdir -p /etc/dnf/vars
-#RUN echo 'i386' >/etc/dnf/vars/basearch
-#RUN echo 'i386' >/etc/dnf/vars/arch
-
-#RUN yum update -y
-#RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
-
-FROM zerotier/zt1-build-fedora-22-x86-base
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN echo 'i686-redhat-linux' >/etc/rpm/platform
-
-RUN rpm --erase http-parser-devel
-RUN yum install -y rubygem-ronn ruby
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/make-apt-repos.sh b/linux-build-farm/make-apt-repos.sh
deleted file mode 100755
index 7a81cc5c..00000000
--- a/linux-build-farm/make-apt-repos.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-# This builds a series of Debian repositories for each distribution.
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
-
-for distro in debian-* ubuntu-*; do
- if [ -n "`find ${distro} -name '*.deb' -type f`" ]; then
- arches=`ls ${distro}/*.deb | cut -d _ -f 3 | cut -d . -f 1 | xargs | sed 's/ /,/g'`
- distro_name=`echo $distro | cut -d '-' -f 2`
- echo '---' $distro / $distro_name / $arches
- aptly repo create -architectures=${arches} -comment="ZeroTier, Inc. Debian Packages" -component="main" -distribution=${distro_name} zt-release-${distro_name}
- aptly repo add zt-release-${distro_name} ${distro}/*.deb
- aptly publish repo zt-release-${distro_name} $distro_name
- fi
-done
diff --git a/linux-build-farm/make-rpm-repos.sh b/linux-build-farm/make-rpm-repos.sh
deleted file mode 100755
index 0ed1cfe4..00000000
--- a/linux-build-farm/make-rpm-repos.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/bash
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
-
-GPG_KEY=contact@zerotier.com
-
-rm -rf /tmp/zt-rpm-repo
-mkdir /tmp/zt-rpm-repo
-
-for distro in centos-* fedora-* amazon-*; do
- dname=`echo $distro | cut -d '-' -f 1`
- if [ "$dname" = "centos" ]; then
- dname=el
- fi
- if [ "$dname" = "fedora" ]; then
- dname=fc
- fi
- if [ "$dname" = "amazon" ]; then
- dname=amzn1
- fi
- dvers=`echo $distro | cut -d '-' -f 2`
-
- mkdir -p /tmp/zt-rpm-repo/$dname/$dvers
-
- cp -v $distro/*.rpm /tmp/zt-rpm-repo/$dname/$dvers
-done
-
-rpmsign --resign --key-id=$GPG_KEY --digest-algo=sha256 `find /tmp/zt-rpm-repo -type f -name '*.rpm'`
-
-for db in `find /tmp/zt-rpm-repo -mindepth 2 -maxdepth 2 -type d`; do
- createrepo --database $db
-done
-
-# Stupid RHEL stuff
-cd /tmp/zt-rpm-repo/el
-ln -sf 6 6Client
-ln -sf 6 6Workstation
-ln -sf 6 6Server
-ln -sf 6 6.0
-ln -sf 6 6.1
-ln -sf 6 6.2
-ln -sf 6 6.3
-ln -sf 6 6.4
-ln -sf 6 6.5
-ln -sf 6 6.6
-ln -sf 6 6.7
-ln -sf 6 6.8
-ln -sf 6 6.9
-ln -sf 7 7Client
-ln -sf 7 7Workstation
-ln -sf 7 7Server
-ln -sf 7 7.0
-ln -sf 7 7.1
-ln -sf 7 7.2
-ln -sf 7 7.3
-ln -sf 7 7.4
-ln -sf 7 7.5
-ln -sf 7 7.6
-ln -sf 7 7.7
-ln -sf 7 7.8
-ln -sf 7 7.9
-
-echo
-echo Repo created in /tmp/zt-rpm-repo
diff --git a/linux-build-farm/ubuntu-trusty/x64/Dockerfile b/linux-build-farm/ubuntu-trusty/x64/Dockerfile
deleted file mode 100644
index f84cc6e3..00000000
--- a/linux-build-farm/ubuntu-trusty/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM ubuntu:14.04
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.6
-
-RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-trusty/x86/Dockerfile b/linux-build-farm/ubuntu-trusty/x86/Dockerfile
deleted file mode 100644
index 6be3ae87..00000000
--- a/linux-build-farm/ubuntu-trusty/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM 32bit/ubuntu:14.04
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.6
-
-RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-wily/x64/Dockerfile b/linux-build-farm/ubuntu-wily/x64/Dockerfile
deleted file mode 100644
index 99b8d34c..00000000
--- a/linux-build-farm/ubuntu-wily/x64/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM ubuntu:wily
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.7
-
-RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-wily/x86/Dockerfile b/linux-build-farm/ubuntu-wily/x86/Dockerfile
deleted file mode 100644
index 86ad14f2..00000000
--- a/linux-build-farm/ubuntu-wily/x86/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-FROM daald/ubuntu32:wily
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.7
-
-RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
-RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-xenial/x64/Dockerfile b/linux-build-farm/ubuntu-xenial/x64/Dockerfile
deleted file mode 100644
index fa665a0a..00000000
--- a/linux-build-farm/ubuntu-xenial/x64/Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM ubuntu:xenial
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
-
-#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
-
-RUN rm -f /usr/bin/clang++ /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-xenial/x86/Dockerfile b/linux-build-farm/ubuntu-xenial/x86/Dockerfile
deleted file mode 100644
index d01eec9b..00000000
--- a/linux-build-farm/ubuntu-xenial/x86/Dockerfile
+++ /dev/null
@@ -1,14 +0,0 @@
-FROM f69m/ubuntu32:xenial
-MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
-
-RUN apt-get update
-RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev libnatpmp-dev dh-systemd ruby-ronn g++ make devscripts clang-3.8
-
-#RUN ln -sf /usr/bin/clang++-3.8 /usr/bin/clang++
-#RUN ln -sf /usr/bin/clang-3.8 /usr/bin/clang
-
-RUN rm -f /usr/bin/clang++ /usr/bin/clang
-
-RUN dpkg --purge libhttp-parser-dev
-
-ADD zt1-src.tar.gz /
diff --git a/macui/ZeroTier One.xcodeproj/project.pbxproj b/macui/ZeroTier One.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..fc5cfc1f
--- /dev/null
+++ b/macui/ZeroTier One.xcodeproj/project.pbxproj
@@ -0,0 +1,382 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */; };
+ 932D47331D1CD861004BCFE2 /* PreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */; };
+ 932D47371D1CDC9B004BCFE2 /* AboutViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 932D47351D1CDC9B004BCFE2 /* AboutViewController.xib */; };
+ 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDD1CE7C816005CA2AC /* Assets.xcassets */; };
+ 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BDF1CE7C816005CA2AC /* MainMenu.xib */; };
+ 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */; };
+ 93326BEF1CE7DA30005CA2AC /* ShowNetworksViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */; };
+ 93D1675F1D54191C00330C99 /* NodeStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1675E1D54191C00330C99 /* NodeStatus.m */; };
+ 93D167621D541BC200330C99 /* ServiceCom.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167611D541BC200330C99 /* ServiceCom.m */; };
+ 93D167661D54308200330C99 /* Network.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167651D54308200330C99 /* Network.m */; };
+ 93D167691D57E7EA00330C99 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167681D57E7EA00330C99 /* AboutViewController.m */; };
+ 93D1676D1D57EB8400330C99 /* PreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1676C1D57EB8400330C99 /* PreferencesViewController.m */; };
+ 93D167701D57FD3800330C99 /* NetworkMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1676F1D57FD3800330C99 /* NetworkMonitor.m */; };
+ 93D167731D58093C00330C99 /* NetworkInfoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167721D58093C00330C99 /* NetworkInfoCell.m */; };
+ 93D167761D580C3500330C99 /* ShowNetworksViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167751D580C3500330C99 /* ShowNetworksViewController.m */; };
+ 93D167791D5815E600330C99 /* JoinNetworkViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D167781D5815E600330C99 /* JoinNetworkViewController.m */; };
+ 93D1677C1D58228A00330C99 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1677B1D58228A00330C99 /* AppDelegate.m */; };
+ 93D1679B1D58300F00330C99 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D1679A1D58300F00330C99 /* main.m */; };
+ 93D1679D1D595F0000330C99 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D1679C1D595F0000330C99 /* WebKit.framework */; };
+ 93DAFB271D3F0BEE004D5417 /* about.html in Resources */ = {isa = PBXBuildFile; fileRef = 93DAFB261D3F0BEE004D5417 /* about.html */; };
+ 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = ZeroTierIcon.icns; sourceTree = "<group>"; };
+ 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencesViewController.xib; sourceTree = "<group>"; };
+ 932D47351D1CDC9B004BCFE2 /* AboutViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutViewController.xib; sourceTree = "<group>"; };
+ 93326BD81CE7C816005CA2AC /* ZeroTier One.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZeroTier One.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 93326BDD1CE7C816005CA2AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+ 93326BE01CE7C816005CA2AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
+ 93326BE21CE7C816005CA2AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = JoinNetworkViewController.xib; sourceTree = "<group>"; };
+ 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShowNetworksViewController.xib; sourceTree = "<group>"; };
+ 93D1675D1D54191C00330C99 /* NodeStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NodeStatus.h; sourceTree = "<group>"; };
+ 93D1675E1D54191C00330C99 /* NodeStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NodeStatus.m; sourceTree = "<group>"; };
+ 93D167601D541BC200330C99 /* ServiceCom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServiceCom.h; sourceTree = "<group>"; };
+ 93D167611D541BC200330C99 /* ServiceCom.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServiceCom.m; sourceTree = "<group>"; };
+ 93D167641D54308200330C99 /* Network.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Network.h; sourceTree = "<group>"; };
+ 93D167651D54308200330C99 /* Network.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Network.m; sourceTree = "<group>"; };
+ 93D167671D57E7EA00330C99 /* AboutViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutViewController.h; sourceTree = "<group>"; };
+ 93D167681D57E7EA00330C99 /* AboutViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutViewController.m; sourceTree = "<group>"; };
+ 93D1676B1D57EB8400330C99 /* PreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesViewController.h; sourceTree = "<group>"; };
+ 93D1676C1D57EB8400330C99 /* PreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesViewController.m; sourceTree = "<group>"; };
+ 93D1676E1D57FD3800330C99 /* NetworkMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkMonitor.h; sourceTree = "<group>"; };
+ 93D1676F1D57FD3800330C99 /* NetworkMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkMonitor.m; sourceTree = "<group>"; };
+ 93D167711D58093C00330C99 /* NetworkInfoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkInfoCell.h; sourceTree = "<group>"; };
+ 93D167721D58093C00330C99 /* NetworkInfoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkInfoCell.m; sourceTree = "<group>"; };
+ 93D167741D580C3500330C99 /* ShowNetworksViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowNetworksViewController.h; sourceTree = "<group>"; };
+ 93D167751D580C3500330C99 /* ShowNetworksViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowNetworksViewController.m; sourceTree = "<group>"; };
+ 93D167771D5815E600330C99 /* JoinNetworkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JoinNetworkViewController.h; sourceTree = "<group>"; };
+ 93D167781D5815E600330C99 /* JoinNetworkViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JoinNetworkViewController.m; sourceTree = "<group>"; };
+ 93D1677A1D58228A00330C99 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ 93D1677B1D58228A00330C99 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ 93D1679A1D58300F00330C99 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 93D1679C1D595F0000330C99 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
+ 93DAFB261D3F0BEE004D5417 /* about.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = about.html; sourceTree = "<group>"; };
+ 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AuthtokenCopy.m; sourceTree = "<group>"; };
+ 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthtokenCopy.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 93326BD51CE7C816005CA2AC /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 93D1679D1D595F0000330C99 /* WebKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 93326BCF1CE7C816005CA2AC = {
+ isa = PBXGroup;
+ children = (
+ 93D1679C1D595F0000330C99 /* WebKit.framework */,
+ 93326BDA1CE7C816005CA2AC /* ZeroTier One */,
+ 93326BD91CE7C816005CA2AC /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 93326BD91CE7C816005CA2AC /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 93326BD81CE7C816005CA2AC /* ZeroTier One.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 93326BDA1CE7C816005CA2AC /* ZeroTier One */ = {
+ isa = PBXGroup;
+ children = (
+ 932D472E1D1CD499004BCFE2 /* ZeroTierIcon.icns */,
+ 93326BDD1CE7C816005CA2AC /* Assets.xcassets */,
+ 93326BDF1CE7C816005CA2AC /* MainMenu.xib */,
+ 93326BE21CE7C816005CA2AC /* Info.plist */,
+ 93DAFE4A1CFE53CA00547CC4 /* AuthtokenCopy.m */,
+ 93DAFE4C1CFE53DA00547CC4 /* AuthtokenCopy.h */,
+ 93D1676E1D57FD3800330C99 /* NetworkMonitor.h */,
+ 93D1676F1D57FD3800330C99 /* NetworkMonitor.m */,
+ 93DAFB261D3F0BEE004D5417 /* about.html */,
+ 93D1675D1D54191C00330C99 /* NodeStatus.h */,
+ 93D1675E1D54191C00330C99 /* NodeStatus.m */,
+ 93D167601D541BC200330C99 /* ServiceCom.h */,
+ 93D167611D541BC200330C99 /* ServiceCom.m */,
+ 93D167641D54308200330C99 /* Network.h */,
+ 93D167651D54308200330C99 /* Network.m */,
+ 93D167671D57E7EA00330C99 /* AboutViewController.h */,
+ 93D167681D57E7EA00330C99 /* AboutViewController.m */,
+ 932D47351D1CDC9B004BCFE2 /* AboutViewController.xib */,
+ 93D1676B1D57EB8400330C99 /* PreferencesViewController.h */,
+ 93D1676C1D57EB8400330C99 /* PreferencesViewController.m */,
+ 932D47311D1CD861004BCFE2 /* PreferencesViewController.xib */,
+ 93D167711D58093C00330C99 /* NetworkInfoCell.h */,
+ 93D167721D58093C00330C99 /* NetworkInfoCell.m */,
+ 93D167741D580C3500330C99 /* ShowNetworksViewController.h */,
+ 93D167751D580C3500330C99 /* ShowNetworksViewController.m */,
+ 93326BED1CE7DA30005CA2AC /* ShowNetworksViewController.xib */,
+ 93D167771D5815E600330C99 /* JoinNetworkViewController.h */,
+ 93D167781D5815E600330C99 /* JoinNetworkViewController.m */,
+ 93326BE91CE7D9B9005CA2AC /* JoinNetworkViewController.xib */,
+ 93D1677A1D58228A00330C99 /* AppDelegate.h */,
+ 93D1677B1D58228A00330C99 /* AppDelegate.m */,
+ 93D1679A1D58300F00330C99 /* main.m */,
+ );
+ path = "ZeroTier One";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 93326BD71CE7C816005CA2AC /* ZeroTier One */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 93326BE51CE7C816005CA2AC /* Build configuration list for PBXNativeTarget "ZeroTier One" */;
+ buildPhases = (
+ 93326BD41CE7C816005CA2AC /* Sources */,
+ 93326BD51CE7C816005CA2AC /* Frameworks */,
+ 93326BD61CE7C816005CA2AC /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "ZeroTier One";
+ productName = "ZeroTier One";
+ productReference = 93326BD81CE7C816005CA2AC /* ZeroTier One.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 93326BD01CE7C816005CA2AC /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0730;
+ LastUpgradeCheck = 0800;
+ ORGANIZATIONNAME = "ZeroTier, Inc";
+ TargetAttributes = {
+ 93326BD71CE7C816005CA2AC = {
+ CreatedOnToolsVersion = 7.3;
+ };
+ };
+ };
+ buildConfigurationList = 93326BD31CE7C816005CA2AC /* Build configuration list for PBXProject "ZeroTier One" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 93326BCF1CE7C816005CA2AC;
+ productRefGroup = 93326BD91CE7C816005CA2AC /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 93326BD71CE7C816005CA2AC /* ZeroTier One */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 93326BD61CE7C816005CA2AC /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 93DAFB271D3F0BEE004D5417 /* about.html in Resources */,
+ 93326BEF1CE7DA30005CA2AC /* ShowNetworksViewController.xib in Resources */,
+ 932D47371D1CDC9B004BCFE2 /* AboutViewController.xib in Resources */,
+ 93326BEB1CE7D9B9005CA2AC /* JoinNetworkViewController.xib in Resources */,
+ 93326BDE1CE7C816005CA2AC /* Assets.xcassets in Resources */,
+ 93326BE11CE7C816005CA2AC /* MainMenu.xib in Resources */,
+ 932D472F1D1CD499004BCFE2 /* ZeroTierIcon.icns in Resources */,
+ 932D47331D1CD861004BCFE2 /* PreferencesViewController.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 93326BD41CE7C816005CA2AC /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 93D1679B1D58300F00330C99 /* main.m in Sources */,
+ 93D167621D541BC200330C99 /* ServiceCom.m in Sources */,
+ 93D167761D580C3500330C99 /* ShowNetworksViewController.m in Sources */,
+ 93DAFE4B1CFE53CA00547CC4 /* AuthtokenCopy.m in Sources */,
+ 93D167701D57FD3800330C99 /* NetworkMonitor.m in Sources */,
+ 93D1675F1D54191C00330C99 /* NodeStatus.m in Sources */,
+ 93D167691D57E7EA00330C99 /* AboutViewController.m in Sources */,
+ 93D1676D1D57EB8400330C99 /* PreferencesViewController.m in Sources */,
+ 93D1677C1D58228A00330C99 /* AppDelegate.m in Sources */,
+ 93D167731D58093C00330C99 /* NetworkInfoCell.m in Sources */,
+ 93D167661D54308200330C99 /* Network.m in Sources */,
+ 93D167791D5815E600330C99 /* JoinNetworkViewController.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 93326BDF1CE7C816005CA2AC /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 93326BE01CE7C816005CA2AC /* Base */,
+ );
+ name = MainMenu.xib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 93326BE31CE7C816005CA2AC /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 93326BE41CE7C816005CA2AC /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ 93326BE61CE7C816005CA2AC /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = "ZeroTier One/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.10;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 93326BE71CE7C816005CA2AC /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_MODULES = YES;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = "ZeroTier One/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.10;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.zerotier.ZeroTier-One";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "ZeroTier One/ZeroTier One-Bridging-Header.h";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 93326BD31CE7C816005CA2AC /* Build configuration list for PBXProject "ZeroTier One" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 93326BE31CE7C816005CA2AC /* Debug */,
+ 93326BE41CE7C816005CA2AC /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 93326BE51CE7C816005CA2AC /* Build configuration list for PBXNativeTarget "ZeroTier One" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 93326BE61CE7C816005CA2AC /* Debug */,
+ 93326BE71CE7C816005CA2AC /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 93326BD01CE7C816005CA2AC /* Project object */;
+}
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/macui/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 88f36fc7..fd60338e 100644
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/macui/ZeroTier One.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
- location = "self:MacGap.xcodeproj">
+ location = "self:ZeroTier One.xcodeproj">
</FileRef>
</Workspace>
diff --git a/macui/ZeroTier One/AboutViewController.h b/macui/ZeroTier One/AboutViewController.h
new file mode 100644
index 00000000..d3d5bc14
--- /dev/null
+++ b/macui/ZeroTier One/AboutViewController.h
@@ -0,0 +1,33 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <WebKit/WebKit.h>
+
+@interface AboutViewController : NSViewController <WebPolicyDelegate>
+
+@property (nonatomic, weak) IBOutlet WebView *webView;
+
+- (void)viewDidLoad;
+
+- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation
+ request:(NSURLRequest *)request
+ frame:(WebFrame *)frame
+decisionListener:(id<WebPolicyDecisionListener>)listener;
+
+@end
diff --git a/macui/ZeroTier One/AboutViewController.m b/macui/ZeroTier One/AboutViewController.m
new file mode 100644
index 00000000..7f92977d
--- /dev/null
+++ b/macui/ZeroTier One/AboutViewController.m
@@ -0,0 +1,57 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "AboutViewController.h"
+
+@interface AboutViewController ()
+
+@end
+
+@implementation AboutViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ [self.webView setWantsLayer:YES];
+ self.webView.layer.borderWidth = 1.0f;
+ [self.webView.layer setCornerRadius:1.0f];
+ self.webView.layer.masksToBounds = YES;
+ [self.webView.layer setBorderColor:[[NSColor darkGrayColor] CGColor]];
+ self.webView.policyDelegate = self;
+
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSURL *path = [bundle URLForResource:@"about" withExtension:@"html"];
+ if(path) {
+ [self.webView.mainFrame loadRequest:[NSURLRequest requestWithURL:path]];
+ }
+}
+
+- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation
+ request:(NSURLRequest *)request
+ frame:(WebFrame *)frame
+decisionListener:(id<WebPolicyDecisionListener>)listener
+{
+ if(request.URL != nil && request.URL.host != nil) {
+ [[NSWorkspace sharedWorkspace] openURL:request.URL];
+ }
+ else {
+ [listener use];
+ }
+}
+
+@end
diff --git a/macui/ZeroTier One/AboutViewController.xib b/macui/ZeroTier One/AboutViewController.xib
new file mode 100644
index 00000000..a0df0fcf
--- /dev/null
+++ b/macui/ZeroTier One/AboutViewController.xib
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
+ <plugIn identifier="com.apple.WebKitIBPlugin" version="10116"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="AboutViewController" customModule="ZeroTier_One" customModuleProvider="target">
+ <connections>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ <outlet property="webView" destination="3BS-QW-rZO" id="ucY-A9-7p7"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <webView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3BS-QW-rZO">
+ <rect key="frame" x="20" y="20" width="440" height="440"/>
+ <webPreferences key="preferences" defaultFontSize="16" defaultFixedFontSize="13" minimumFontSize="0">
+ <nil key="identifier"/>
+ </webPreferences>
+ </webView>
+ </subviews>
+ <point key="canvasLocation" x="463" y="570"/>
+ </customView>
+ </objects>
+</document>
diff --git a/macui/ZeroTier One/AppDelegate.h b/macui/ZeroTier One/AppDelegate.h
new file mode 100644
index 00000000..a00cfba9
--- /dev/null
+++ b/macui/ZeroTier One/AppDelegate.h
@@ -0,0 +1,61 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class NetworkMonitor;
+@class Network;
+@class NodeStatus;
+
+@interface AppDelegate : NSObject <NSApplicationDelegate>
+
+@property (weak, nonatomic) IBOutlet NSWindow *window;
+
+@property (nonatomic) NSStatusItem *statusItem;
+
+@property (nonatomic) NSPopover *networkListPopover;
+@property (nonatomic) NSPopover *joinNetworkPopover;
+@property (nonatomic) NSPopover *preferencesPopover;
+@property (nonatomic) NSPopover *aboutPopover;
+
+@property (nonatomic) id transientMonitor;
+
+@property (nonatomic) NetworkMonitor *monitor;
+
+@property (nonatomic) NSMutableArray<Network*> *networks;
+
+@property (nonatomic) NodeStatus *status;
+
+- (void)buildMenu;
+
+- (void)onNetworkListUpdated:(NSNotification*)note;
+- (void)onNodeStatusUpdated:(NSNotification*)note;
+
+- (void)showNetworks;
+- (void)joinNetwork;
+- (void)showPreferences;
+- (void)showAbout;
+- (void)quit;
+- (void)toggleNetwork:(NSMenuItem*)sender;
+- (void)copyNodeID;
+
+- (void)closeJoinNetworkPopover;
+
+- (void)darkModeChanged:(NSNotification*)note;
+
+@end
diff --git a/macui/ZeroTier One/AppDelegate.m b/macui/ZeroTier One/AppDelegate.m
new file mode 100644
index 00000000..ae3e042f
--- /dev/null
+++ b/macui/ZeroTier One/AppDelegate.m
@@ -0,0 +1,378 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "AppDelegate.h"
+#import "NetworkMonitor.h"
+#import "Network.h"
+#import "NodeStatus.h"
+#import "JoinNetworkViewController.h"
+#import "ShowNetworksViewController.h"
+#import "PreferencesViewController.h"
+#import "AboutViewController.h"
+#import "ServiceCom.h"
+
+@implementation AppDelegate
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
+ self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:-2.0f];
+ self.networkListPopover = [[NSPopover alloc] init];
+ self.joinNetworkPopover = [[NSPopover alloc] init];
+ self.preferencesPopover = [[NSPopover alloc] init];
+ self.aboutPopover = [[NSPopover alloc] init];
+ self.transientMonitor = nil;
+ self.monitor = [[NetworkMonitor alloc] init];
+ self.networks = [NSMutableArray<Network*> array];
+ self.status = nil;
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSDictionary *defaultsDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"firstRun"];
+ [defaults registerDefaults:defaultsDict];
+
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+
+ [nc addObserver:self
+ selector:@selector(onNetworkListUpdated:)
+ name:NetworkUpdateKey
+ object:nil];
+ [nc addObserver:self
+ selector:@selector(onNodeStatusUpdated:)
+ name:StatusUpdateKey
+ object:nil];
+
+ NSString *osxMode = [defaults stringForKey:@"AppleInterfaceStyle"];
+
+ if(osxMode != nil && [osxMode isEqualToString:@"Dark"]) {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMacWhite"];
+ }
+ else {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMac"];
+ }
+
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(darkModeChanged:)
+ name:@"AppleInterfaceThemeChangedNotification"
+ object:nil];
+
+ [self buildMenu];
+ JoinNetworkViewController *jnvc = [[JoinNetworkViewController alloc] initWithNibName:@"JoinNetworkViewController" bundle:nil];
+ jnvc.appDelegate = self;
+ self.joinNetworkPopover.contentViewController = jnvc;
+ self.joinNetworkPopover.behavior = NSPopoverBehaviorTransient;
+
+ ShowNetworksViewController *showNetworksView = [[ShowNetworksViewController alloc] initWithNibName:@"ShowNetworksViewController" bundle:nil];
+ showNetworksView.netMonitor = self.monitor;
+ self.networkListPopover.contentViewController = showNetworksView;
+ self.networkListPopover.behavior = NSPopoverBehaviorTransient;
+
+ PreferencesViewController *prefsView = [[PreferencesViewController alloc] initWithNibName:@"PreferencesViewController" bundle:nil];
+ self.preferencesPopover.contentViewController = prefsView;
+ self.preferencesPopover.behavior = NSPopoverBehaviorTransient;
+
+ self.aboutPopover.contentViewController = [[AboutViewController alloc] initWithNibName:@"AboutViewController" bundle:nil];
+ self.aboutPopover.behavior = NSPopoverBehaviorTransient;
+
+ BOOL firstRun = [defaults boolForKey:@"firstRun"];
+
+ if(firstRun) {
+ [defaults setBool:NO forKey:@"firstRun"];
+ [defaults synchronize];
+
+ [prefsView setLaunchAtLoginEnabled:YES];
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ sleep(2);
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ [self showAbout];
+ }];
+ });
+ }
+
+ [self.monitor updateNetworkInfo];
+ [self.monitor start];
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [[NSDistributedNotificationCenter defaultCenter] removeObserver:self
+ name:@"AppleInterfaceThemeChangedNotification"
+ object:nil];
+}
+
+- (void)showNetworks {
+ NSButton *button = nil;
+ NSRect frame;
+ if ([self.statusItem respondsToSelector:@selector(button)]) {
+ button = self.statusItem.button;
+ frame = button.bounds;
+ } else if ([self.statusItem respondsToSelector:@selector(_button)]) {
+ button = [self.statusItem performSelector:@selector(_button)];
+ frame = button.bounds;
+ } else {
+ NSLog(@"Can't get view. Uh oh.");
+ return;
+ }
+
+ [self.networkListPopover showRelativeToRect:frame
+ ofView:button
+ preferredEdge:NSMinYEdge];
+
+ if(self.transientMonitor == nil) {
+ self.transientMonitor =
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.networkListPopover close];
+ }];
+ }
+}
+
+- (void)joinNetwork {
+ NSButton *button = nil;
+ NSRect frame;
+ if ([self.statusItem respondsToSelector:@selector(button)]) {
+ button = self.statusItem.button;
+ frame = button.bounds;
+ } else if ([self.statusItem respondsToSelector:@selector(_button)]) {
+ button = [self.statusItem performSelector:@selector(_button)];
+ frame = button.bounds;
+ } else {
+ NSLog(@"Can't get view. Uh oh.");
+ return;
+ }
+
+ [self.joinNetworkPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+ if(self.transientMonitor == nil) {
+ self.transientMonitor =
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.joinNetworkPopover close];
+ }];
+ }
+}
+
+- (void)showPreferences {
+ NSButton *button = nil;
+ NSRect frame;
+ if ([self.statusItem respondsToSelector:@selector(button)]) {
+ button = self.statusItem.button;
+ frame = button.bounds;
+ } else if ([self.statusItem respondsToSelector:@selector(_button)]) {
+ button = [self.statusItem performSelector:@selector(_button)];
+ frame = button.bounds;
+ } else {
+ NSLog(@"Can't get view. Uh oh.");
+ return;
+ }
+
+ [self.preferencesPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+ if(self.transientMonitor == nil) {
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.preferencesPopover close];
+ }];
+ }
+}
+
+- (void)showAbout {
+ NSButton *button = nil;
+ NSRect frame;
+ if ([self.statusItem respondsToSelector:@selector(button)]) {
+ button = self.statusItem.button;
+ frame = button.bounds;
+ } else if ([self.statusItem respondsToSelector:@selector(_button)]) {
+ button = [self.statusItem performSelector:@selector(_button)];
+ frame = button.bounds;
+ } else {
+ NSLog(@"Can't get view. Uh oh.");
+ return;
+ }
+
+ [self.aboutPopover showRelativeToRect:button.bounds
+ ofView:button
+ preferredEdge:NSMinYEdge];
+ if(self.transientMonitor == nil) {
+ [NSEvent addGlobalMonitorForEventsMatchingMask:(NSLeftMouseDown|NSRightMouseDown|NSOtherMouseDown)
+ handler:^(NSEvent * _Nonnull e) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ [self.aboutPopover close];
+ }];
+ }
+}
+
+- (void)quit {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+}
+
+- (void)onNetworkListUpdated:(NSNotification*)note {
+ NSArray<Network*> *netList = [note.userInfo objectForKey:@"networks"];
+ [(ShowNetworksViewController*)self.networkListPopover.contentViewController setNetworks:netList];
+ self.networks = [netList mutableCopy];
+
+ [self buildMenu];
+}
+
+- (void)onNodeStatusUpdated:(NSNotification*)note {
+ NodeStatus *status = [note.userInfo objectForKey:@"status"];
+ self.status = status;
+
+ [self buildMenu];
+}
+
+- (void)buildMenu {
+ NSMenu *menu = [[NSMenu alloc] init];
+
+ if(self.status != nil) {
+ NSString *nodeId = @"Node ID: ";
+ nodeId = [nodeId stringByAppendingString:self.status.address];
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:nodeId
+ action:@selector(copyNodeID)
+ keyEquivalent:@""]];
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Network Details..."
+ action:@selector(showNetworks)
+ keyEquivalent:@"n"]];
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Join Network..."
+ action:@selector(joinNetwork)
+ keyEquivalent:@"j"]];
+
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ if([self.networks count] > 0) {
+ for(Network *net in self.networks) {
+ NSString *nwid = [NSString stringWithFormat:@"%10llx", net.nwid];
+ NSString *networkName = @"";
+ if([net.name lengthOfBytesUsingEncoding:NSUTF8StringEncoding] == 0) {
+ networkName = nwid;
+ }
+ else {
+ networkName = [NSString stringWithFormat:@"%@ (%@)", nwid, net.name];
+ }
+
+ if(net.allowDefault && net.connected) {
+ networkName = [networkName stringByAppendingString:@" [default]"];
+ }
+
+ NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:networkName
+ action:@selector(toggleNetwork:)
+ keyEquivalent:@""];
+ if(net.connected) {
+ item.state = NSOnState;
+ }
+ else {
+ item.state = NSOffState;
+ }
+
+ item.representedObject = net;
+
+ [menu addItem:item];
+ }
+
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"About ZeroTier One..."
+ action:@selector(showAbout)
+ keyEquivalent:@""]];
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Preferences..."
+ action:@selector(showPreferences)
+ keyEquivalent:@""]];
+
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Quit"
+ action:@selector(quit)
+ keyEquivalent:@"q"]];
+
+ self.statusItem.menu = menu;
+}
+
+- (void)toggleNetwork:(NSMenuItem*)sender {
+ Network *network = sender.representedObject;
+ NSString *nwid = [NSString stringWithFormat:@"%10llx", network.nwid];
+
+ if(network.connected) {
+ NSError *error = nil;
+
+ [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error];
+
+ if (error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ }
+ }
+ else {
+ NSError *error = nil;
+ [[ServiceCom sharedInstance] joinNetwork:nwid
+ allowManaged:network.allowManaged
+ allowGlobal:network.allowGlobal
+ allowDefault:(network.allowDefault && ![Network defaultRouteExists:self.networks])
+ error:&error];
+
+ if (error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ }
+ }
+}
+
+- (void)copyNodeID {
+ if(self.status != nil) {
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
+ [pasteboard setString:self.status.address forType:NSPasteboardTypeString];
+ }
+}
+
+- (void)darkModeChanged:(NSNotification*)note {
+ NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
+
+ if(osxMode != nil && [osxMode isEqualToString:@"Dark"]) {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMacWhite"];
+ }
+ else {
+ self.statusItem.image = [NSImage imageNamed:@"MenuBarIconMac"];
+ }
+}
+
+- (void)closeJoinNetworkPopover {
+ if (self.transientMonitor) {
+ [NSEvent removeMonitor:self.transientMonitor];
+ self.transientMonitor = nil;
+ }
+ [self.joinNetworkPopover close];
+}
+
+@end
diff --git a/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json b/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..24c81d35
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,59 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "32x32",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "128x128",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "256x256",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "ZeroTierIcon512x512.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "size" : "512x512",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png b/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png
new file mode 100644
index 00000000..d225c2e3
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/AppIcon.appiconset/ZeroTierIcon512x512.png
Binary files differ
diff --git a/macui/ZeroTier One/Assets.xcassets/Contents.json b/macui/ZeroTier One/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..da4a164c
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json
new file mode 100644
index 00000000..a680b58b
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "filename" : "Menubar.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "filename" : "MenuBar@2x.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png
new file mode 100644
index 00000000..9fd3d3de
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/MenuBar@2x.png
Binary files differ
diff --git a/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png
new file mode 100644
index 00000000..ee0d7e3f
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMac.imageset/Menubar.png
Binary files differ
diff --git a/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json
new file mode 100644
index 00000000..61737760
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "mac",
+ "filename" : "MenubarWhite.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "mac",
+ "filename" : "MenubarWhite@2x.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "template-rendering-intent" : "template"
+ }
+} \ No newline at end of file
diff --git a/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png
new file mode 100644
index 00000000..7049ae55
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite.png
Binary files differ
diff --git a/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png
new file mode 100644
index 00000000..8c20e36f
--- /dev/null
+++ b/macui/ZeroTier One/Assets.xcassets/MenuBarIconMacWhite.imageset/MenubarWhite@2x.png
Binary files differ
diff --git a/macui/ZeroTier One/AuthtokenCopy.h b/macui/ZeroTier One/AuthtokenCopy.h
new file mode 100644
index 00000000..f0497cc6
--- /dev/null
+++ b/macui/ZeroTier One/AuthtokenCopy.h
@@ -0,0 +1,26 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef AuthtokenCopy_h
+#define AuthtokenCopy_h
+
+#import <Foundation/Foundation.h>
+
+NSString* getAdminAuthToken(AuthorizationRef authRef);
+
+#endif /* AuthtokenCopy_h */
diff --git a/macui/ZeroTier One/AuthtokenCopy.m b/macui/ZeroTier One/AuthtokenCopy.m
new file mode 100644
index 00000000..a10350f7
--- /dev/null
+++ b/macui/ZeroTier One/AuthtokenCopy.m
@@ -0,0 +1,97 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "AuthtokenCopy.h"
+
+
+NSString* getAdminAuthToken(AuthorizationRef authRef) {
+ char *tool = "/bin/cat";
+ char *args[] = { "/Library/Application Support/ZeroTier/One/authtoken.secret", NULL};
+ FILE *pipe = nil;
+ char token[25];
+ memset(token, 0, sizeof(char)*25);
+
+
+ OSStatus status = AuthorizationExecuteWithPrivileges(authRef, tool, kAuthorizationFlagDefaults, args, &pipe);
+
+ if (status != errAuthorizationSuccess) {
+ NSLog(@"Reading authtoken failed!");
+
+
+ switch(status) {
+ case errAuthorizationDenied:
+ NSLog(@"Autorization Denied");
+ break;
+ case errAuthorizationCanceled:
+ NSLog(@"Authorization Canceled");
+ break;
+ case errAuthorizationInternal:
+ NSLog(@"Authorization Internal");
+ break;
+ case errAuthorizationBadAddress:
+ NSLog(@"Bad Address");
+ break;
+ case errAuthorizationInvalidRef:
+ NSLog(@"Invalid Ref");
+ break;
+ case errAuthorizationInvalidSet:
+ NSLog(@"Invalid Set");
+ break;
+ case errAuthorizationInvalidTag:
+ NSLog(@"Invalid Tag");
+ break;
+ case errAuthorizationInvalidFlags:
+ NSLog(@"Invalid Flags");
+ break;
+ case errAuthorizationInvalidPointer:
+ NSLog(@"Invalid Pointer");
+ break;
+ case errAuthorizationToolExecuteFailure:
+ NSLog(@"Tool Execute Failure");
+ break;
+ case errAuthorizationToolEnvironmentError:
+ NSLog(@"Tool Environment Failure");
+ break;
+ case errAuthorizationExternalizeNotAllowed:
+ NSLog(@"Externalize Not Allowed");
+ break;
+ case errAuthorizationInteractionNotAllowed:
+ NSLog(@"Interaction Not Allowed");
+ break;
+ case errAuthorizationInternalizeNotAllowed:
+ NSLog(@"Internalize Not Allowed");
+ break;
+ default:
+ NSLog(@"Unknown Error");
+ break;
+ }
+
+ return @"";
+ }
+
+ if(pipe != nil) {
+ fread(&token, sizeof(char), 24, pipe);
+ fclose(pipe);
+
+ return [NSString stringWithUTF8String:token];
+ }
+
+ return @"";
+}
diff --git a/macui/ZeroTier One/Base.lproj/MainMenu.xib b/macui/ZeroTier One/Base.lproj/MainMenu.xib
new file mode 100644
index 00000000..6b6da2a6
--- /dev/null
+++ b/macui/ZeroTier One/Base.lproj/MainMenu.xib
@@ -0,0 +1,680 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
+ <connections>
+ <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customObject id="Voe-Tx-rLC" customClass="AppDelegate">
+ <connections>
+ <outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
+ </connections>
+ </customObject>
+ <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
+ <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
+ <items>
+ <menuItem title="ZeroTier One" id="1Xt-HY-uBw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="ZeroTier One" systemMenu="apple" id="uQy-DD-JDr">
+ <items>
+ <menuItem title="About ZeroTier One" id="5kV-Vb-QxS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
+ <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
+ <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
+ <menuItem title="Services" id="NMo-om-nkz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
+ <menuItem title="Hide ZeroTier One" keyEquivalent="h" id="Olw-nP-bQN">
+ <connections>
+ <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All" id="Kd2-mp-pUS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
+ <menuItem title="Quit ZeroTier One" keyEquivalent="q" id="4sb-4s-VLi">
+ <connections>
+ <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="File" id="dMs-cI-mzQ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="File" id="bib-Uj-vzu">
+ <items>
+ <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
+ <connections>
+ <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
+ <connections>
+ <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Open Recent" id="tXI-mr-wws">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
+ <items>
+ <menuItem title="Clear Menu" id="vNY-rz-j42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
+ <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
+ <connections>
+ <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
+ <connections>
+ <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
+ <connections>
+ <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Revert to Saved" id="KaW-ft-85H">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
+ <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
+ <connections>
+ <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Edit" id="5QF-Oa-p0T">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Edit" id="W48-6f-4Dl">
+ <items>
+ <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
+ <connections>
+ <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
+ <connections>
+ <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
+ <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
+ <connections>
+ <action selector="cut:" target="-1" id="YJe-68-I9s"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
+ <connections>
+ <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
+ <connections>
+ <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Delete" id="pa3-QI-u2k">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
+ <connections>
+ <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
+ <menuItem title="Find" id="4EN-yA-p0u">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Find" id="1b7-l0-nxx">
+ <items>
+ <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
+ <connections>
+ <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
+ <connections>
+ <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
+ <items>
+ <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
+ <connections>
+ <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
+ <connections>
+ <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
+ <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Substitutions" id="9ic-FL-obx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
+ <items>
+ <menuItem title="Show Substitutions" id="z6F-FW-3nz">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
+ <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Quotes" id="hQb-2v-fYv">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Dashes" id="rgM-f4-ycn">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smart Links" id="cwL-P1-jid">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Data Detectors" id="tRr-pd-1PS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Text Replacement" id="HFQ-gK-NFA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Transformations" id="2oI-Rn-ZJC">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
+ <items>
+ <menuItem title="Make Upper Case" id="vmV-6d-7jI">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Make Lower Case" id="d9M-CD-aMd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Capitalize" id="UEZ-Bs-lqG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Speech" id="xrE-MZ-jX0">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
+ <items>
+ <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Format" id="jxT-CU-nIS">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Format" id="GEO-Iw-cKr">
+ <items>
+ <menuItem title="Font" id="Gi5-1S-RQB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
+ <items>
+ <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
+ <connections>
+ <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
+ <connections>
+ <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
+ <connections>
+ <action selector="underline:" target="-1" id="FYS-2b-JAY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
+ <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
+ <connections>
+ <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
+ <menuItem title="Kern" id="jBQ-r6-VK2">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
+ <items>
+ <menuItem title="Use Default" id="GUa-eO-cwY">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="cDB-IK-hbR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Tighten" id="46P-cB-AYj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Loosen" id="ogc-rX-tC1">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Ligatures" id="o6e-r0-MWq">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
+ <items>
+ <menuItem title="Use Default" id="agt-UL-0e3">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use None" id="J7y-lM-qPV">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Use All" id="xQD-1f-W4t">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Baseline" id="OaQ-X3-Vso">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Baseline" id="ijk-EB-dga">
+ <items>
+ <menuItem title="Use Default" id="3Om-Ey-2VK">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Superscript" id="Rqc-34-cIF">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Subscript" id="I0S-gh-46l">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Raise" id="2h7-ER-AoG">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Lower" id="1tx-W0-xDw">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
+ <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
+ <connections>
+ <action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
+ <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Text" id="Fal-I4-PZk">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Text" id="d9c-me-L2H">
+ <items>
+ <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
+ <connections>
+ <action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
+ <connections>
+ <action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Justify" id="J5U-5w-g23">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
+ <connections>
+ <action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
+ <menuItem title="Writing Direction" id="H1b-Si-o9J">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
+ <items>
+ <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="YGs-j5-SAR">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
+ </connections>
+ </menuItem>
+ <menuItem id="Lbh-J2-qVU">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
+ </connections>
+ </menuItem>
+ <menuItem id="jFq-tB-4Kx">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
+ <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem id="Nop-cj-93Q">
+ <string key="title"> Default</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
+ </connections>
+ </menuItem>
+ <menuItem id="BgM-ve-c93">
+ <string key="title"> Left to Right</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
+ </connections>
+ </menuItem>
+ <menuItem id="RB4-Sm-HuC">
+ <string key="title"> Right to Left</string>
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
+ <menuItem title="Show Ruler" id="vLm-3I-IUL">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
+ <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
+ <connections>
+ <action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="View" id="H8h-7b-M4v">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="View" id="HyV-fh-RgO">
+ <items>
+ <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
+ <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
+ <connections>
+ <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Window" id="aUF-d1-5bR">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
+ <items>
+ <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
+ <connections>
+ <action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Zoom" id="R4o-n2-Eq4">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
+ </connections>
+ </menuItem>
+ <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
+ <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ <menuItem title="Help" id="wpr-3q-Mcd">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
+ <items>
+ <menuItem title="ZeroTier One Help" keyEquivalent="?" id="FKE-Sm-Kum">
+ <connections>
+ <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
+ </connections>
+ </menuItem>
+ </items>
+ </menu>
+ </menuItem>
+ </items>
+ </menu>
+ <window title="ZeroTier One" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="335" y="390" width="480" height="360"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1716" height="1024"/>
+ <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </view>
+ </window>
+ </objects>
+</document>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Info.plist b/macui/ZeroTier One/Info.plist
index 7f71ea22..e04b5f28 100644
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/src/MacGap/MacGap-Info.plist
+++ b/macui/ZeroTier One/Info.plist
@@ -5,15 +5,15 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
- <string>ZeroTier One</string>
+ <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
- <string>ZeroTierIcon</string>
+ <string>ZeroTierIcon.icns</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
- <string>ZeroTier One</string>
+ <string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@@ -21,19 +21,21 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
- <string>1</string>
- <key>LSApplicationCategoryType</key>
- <string>public.app-category.utilities</string>
+ <string>15</string>
<key>LSMinimumSystemVersion</key>
- <string>${MACOSX_DEPLOYMENT_TARGET}</string>
- <key>NSMainNibFile</key>
- <string>MainMenu</string>
- <key>NSPrincipalClass</key>
- <string>NSApplication</string>
+ <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+ <key>LSUIElement</key>
+ <true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2016 ZeroTier, Inc. All rights reserved.</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
</dict>
</plist>
diff --git a/macui/ZeroTier One/JoinNetworkViewController.h b/macui/ZeroTier One/JoinNetworkViewController.h
new file mode 100644
index 00000000..428959fb
--- /dev/null
+++ b/macui/ZeroTier One/JoinNetworkViewController.h
@@ -0,0 +1,40 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+extern NSString * const JoinedNetworksKey;
+
+@class AppDelegate;
+
+@interface JoinNetworkViewController : NSViewController <NSComboBoxDelegate, NSComboBoxDataSource>
+
+@property (nonatomic, weak) IBOutlet NSComboBox *network;
+@property (nonatomic, weak) IBOutlet NSButton *joinButton;
+@property (nonatomic, weak) IBOutlet NSButton *allowManagedCheckBox;
+@property (nonatomic, weak) IBOutlet NSButton *allowGlobalCheckBox;
+@property (nonatomic, weak) IBOutlet NSButton *allowDefaultCheckBox;
+@property (nonatomic, weak) IBOutlet AppDelegate *appDelegate;
+
+@property (nonatomic) NSMutableArray<NSString*> *values;
+
+- (IBAction)onJoinClicked:(id)sender;
+
+
+@end
diff --git a/macui/ZeroTier One/JoinNetworkViewController.m b/macui/ZeroTier One/JoinNetworkViewController.m
new file mode 100644
index 00000000..cae26541
--- /dev/null
+++ b/macui/ZeroTier One/JoinNetworkViewController.m
@@ -0,0 +1,184 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "JoinNetworkViewController.h"
+#import "ServiceCom.h"
+#import "AppDelegate.h"
+
+
+NSString * const JoinedNetworksKey = @"com.zerotier.one.joined-networks";
+
+@interface NSString (extra)
+
+- (BOOL)contains:(NSString*)find;
+
+@end
+
+@implementation NSString (extra)
+
+- (BOOL)contains:(NSString*)find {
+ NSRange range = [self rangeOfString:find];
+ return range.location != NSNotFound;
+}
+
+@end
+
+
+@implementation JoinNetworkViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+ // Do view setup here.
+ [self.network setDelegate:self];
+ [self.network setDataSource:self];
+}
+
+- (void)viewWillAppear {
+ [super viewWillAppear];
+
+ self.allowManagedCheckBox.state = NSOnState;
+ self.allowGlobalCheckBox.state = NSOffState;
+ self.allowDefaultCheckBox.state = NSOffState;
+
+ self.network.stringValue = @"";
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ NSMutableArray<NSString*> *vals = [[defaults stringArrayForKey:JoinedNetworksKey] mutableCopy];
+
+ if(vals) {
+ self.values = vals;
+ }
+}
+
+- (void)viewWillDisappear {
+ [super viewWillDisappear];
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ [defaults setObject:self.values forKey:JoinedNetworksKey];
+}
+
+- (IBAction)onJoinClicked:(id)sender {
+ NSString *networkId = self.network.stringValue;
+
+ NSError *error = nil;
+ [[ServiceCom sharedInstance] joinNetwork:networkId
+ allowManaged:(self.allowManagedCheckBox.state == NSOnState)
+ allowGlobal:(self.allowGlobalCheckBox.state == NSOnState)
+ allowDefault:(self.allowDefaultCheckBox.state == NSOnState)
+ error:&error];
+
+ if(error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ return;
+ }
+
+ self.network.stringValue = @"";
+
+ if(![self.values containsObject:networkId]) {
+ [self.values insertObject:networkId atIndex:0];
+
+ while([self.values count] > 20) {
+ [self.values removeLastObject];
+ }
+ }
+
+ [self.appDelegate closeJoinNetworkPopover];
+}
+
+// NSComboBoxDelegate methods
+
+- (void)controlTextDidChange:(NSNotification *)obj {
+ NSComboBox *cb = (NSComboBox*)obj.object;
+ NSString *value = cb.stringValue;
+
+ NSString *allowedCharacters = @"abcdefABCDEF0123456789";
+
+ NSString *outValue = @"";
+
+ for(int i = 0; i < [value length]; ++i) {
+ if(![allowedCharacters contains:[NSString stringWithFormat:@"%C", [value characterAtIndex:i]]]) {
+ NSBeep();
+ }
+ else {
+ outValue = [outValue stringByAppendingString:[NSString stringWithFormat:@"%C", [value characterAtIndex:i]]];
+ }
+ }
+
+ if([outValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding] == 16) {
+ self.joinButton.enabled = YES;
+ }
+ else {
+ if([outValue lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > 16) {
+ NSRange range = {0, 16};
+ range = [outValue rangeOfComposedCharacterSequencesForRange:range];
+ outValue = [outValue substringWithRange:range];
+ NSBeep();
+ self.joinButton.enabled = YES;
+ }
+ else {
+ self.joinButton.enabled = NO;
+ }
+ }
+
+ cb.stringValue = outValue;
+}
+
+// end NSComboBoxDelegate methods
+
+// NSComboBoxDataSource methods
+
+- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox {
+ return [self.values count];
+}
+
+- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index {
+ return [self.values objectAtIndex:index];
+}
+
+- (NSUInteger)comboBox:(NSComboBox *)aComboBox indexOfItemWithStringValue:(NSString *)string {
+ NSUInteger counter = 0;
+
+ for(NSString *val in self.values) {
+ if([val isEqualToString:string]) {
+ return counter;
+ }
+
+ counter += 1;
+ }
+
+ return NSNotFound;
+}
+
+- (NSString*)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)string {
+ for(NSString *val in self.values) {
+ if([val hasPrefix:string]) {
+ return val;
+ }
+ }
+ return nil;
+}
+
+// end NSComboBoxDataSource methods
+
+@end
diff --git a/macui/ZeroTier One/JoinNetworkViewController.xib b/macui/ZeroTier One/JoinNetworkViewController.xib
new file mode 100644
index 00000000..59161093
--- /dev/null
+++ b/macui/ZeroTier One/JoinNetworkViewController.xib
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="JoinNetworkViewController" customModule="ZeroTier_One" customModuleProvider="target">
+ <connections>
+ <outlet property="allowDefaultCheckBox" destination="rz3-0a-oNA" id="mYu-Wq-MHW"/>
+ <outlet property="allowGlobalCheckBox" destination="BH2-2O-Qeu" id="alx-Je-q9I"/>
+ <outlet property="allowManagedCheckBox" destination="OQS-QZ-zcY" id="7pz-vO-3IC"/>
+ <outlet property="joinButton" destination="BGy-vd-NQX" id="LGE-2G-7ND"/>
+ <outlet property="network" destination="BQy-d9-BNg" id="Yf7-BG-c84"/>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="397" height="123"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="puT-Yk-CWC">
+ <rect key="frame" x="18" y="83" width="107" height="17"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Enter Network ID" id="oYH-gS-BX5">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BGy-vd-NQX">
+ <rect key="frame" x="302" y="13" width="81" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="push" title="Join" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6Rp-TA-XLl">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onJoinClicked:" target="-2" id="kzC-GT-JKb"/>
+ </connections>
+ </button>
+ <comboBox verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BQy-d9-BNg">
+ <rect key="frame" x="131" y="79" width="249" height="26"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesDataSource="YES" numberOfVisibleItems="5" id="n71-4S-AaI">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </comboBoxCell>
+ </comboBox>
+ <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OQS-QZ-zcY">
+ <rect key="frame" x="18" y="59" width="115" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="check" title="Allow Managed" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="QEN-MJ-xaj">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BH2-2O-Qeu">
+ <rect key="frame" x="137" y="59" width="97" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="check" title="Allow Global" bezelStyle="regularSquare" imagePosition="left" inset="2" id="epO-Uh-aHN">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rz3-0a-oNA">
+ <rect key="frame" x="238" y="59" width="141" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="check" title="Allow Default Route" bezelStyle="regularSquare" imagePosition="left" inset="2" id="Lkd-XI-Kcu">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ </subviews>
+ <point key="canvasLocation" x="263.5" y="372.5"/>
+ </customView>
+ </objects>
+</document>
diff --git a/macui/ZeroTier One/Network.h b/macui/ZeroTier One/Network.h
new file mode 100644
index 00000000..957ff8d5
--- /dev/null
+++ b/macui/ZeroTier One/Network.h
@@ -0,0 +1,68 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+enum NetworkStatus {
+ REQUESTING_CONFIGURATION,
+ OK,
+ ACCESS_DENIED,
+ NOT_FOUND,
+ PORT_ERROR,
+ CLIENT_TOO_OLD,
+};
+
+enum NetworkType {
+ PUBLIC,
+ PRIVATE,
+};
+
+@interface Network : NSObject <NSCoding>
+
+@property (readonly) NSArray<NSString*> *assignedAddresses;
+@property (readonly) BOOL bridge;
+@property (readonly) BOOL broadcastEnabled;
+@property (readonly) BOOL dhcp;
+@property (readonly) NSString *mac;
+@property (readonly) int mtu;
+@property (readonly) int netconfRevision;
+@property (readonly) NSString *name;
+@property (readonly) UInt64 nwid;
+@property (readonly) NSString *portDeviceName;
+@property (readonly) int portError;
+@property (readonly) enum NetworkStatus status;
+@property (readonly) enum NetworkType type;
+@property (readonly) BOOL allowManaged;
+@property (readonly) BOOL allowGlobal;
+@property (readonly) BOOL allowDefault;
+@property (readonly) BOOL connected; // not persisted. set to YES if loaded via json
+
+- (id)initWithJsonData:(NSDictionary*)jsonData;
+- (id)initWithCoder:(NSCoder *)aDecoder;
+- (void)encodeWithCoder:(NSCoder *)aCoder;
++ (BOOL)defaultRouteExists:(NSArray<Network *>*)netList;
+- (NSString*)statusString;
+- (NSString*)typeString;
+
+- (BOOL)hasSameNetworkId:(UInt64)networkId;
+
+- (BOOL)isEqualToNetwork:(Network*)network;
+- (BOOL)isEqual:(id)object;
+- (NSUInteger)hash;
+
+@end
diff --git a/macui/ZeroTier One/Network.m b/macui/ZeroTier One/Network.m
new file mode 100644
index 00000000..8474acaa
--- /dev/null
+++ b/macui/ZeroTier One/Network.m
@@ -0,0 +1,337 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "Network.h"
+
+NSString *NetworkAddressesKey = @"addresses";
+NSString *NetworkBridgeKey = @"bridge";
+NSString *NetworkBroadcastKey = @"broadcast";
+NSString *NetworkDhcpKey = @"dhcp";
+NSString *NetworkMacKey = @"mac";
+NSString *NetworkMtuKey = @"mtu";
+NSString *NetworkMulticastKey = @"multicast";
+NSString *NetworkNameKey = @"name";
+NSString *NetworkNetconfKey = @"netconf";
+NSString *NetworkNwidKey = @"nwid";
+NSString *NetworkPortNameKey = @"port";
+NSString *NetworkPortErrorKey = @"portError";
+NSString *NetworkStatusKey = @"status";
+NSString *NetworkTypeKey = @"type";
+NSString *NetworkAllowManagedKey = @"allowManaged";
+NSString *NetworkAllowGlobalKey = @"allowGlobal";
+NSString *NetworkAllowDefaultKey = @"allowDefault";
+
+@implementation Network
+
+- (id)initWithJsonData:(NSDictionary*)jsonData
+{
+ self = [super init];
+
+ if(self) {
+ if([jsonData objectForKey:@"assignedAddresses"]) {
+ _assignedAddresses = (NSArray<NSString*>*)[jsonData objectForKey:@"assignedAddresses"];
+ }
+
+ if([jsonData objectForKey:@"bridge"]) {
+ _bridge = [(NSNumber*)[jsonData objectForKey:@"bridge"] boolValue];
+ }
+
+ if([jsonData objectForKey:@"broadcastEnabled"]) {
+ _broadcastEnabled = [(NSNumber*)[jsonData objectForKey:@"broadcastEnabled"] boolValue];
+ }
+
+ if([jsonData objectForKey:@"dhcp"]) {
+ _dhcp = [(NSNumber*)[jsonData objectForKey:@"dhcp"] boolValue];
+ }
+
+ if([jsonData objectForKey:@"mac"]) {
+ _mac = (NSString*)[jsonData objectForKey:@"mac"];
+ }
+
+ if([jsonData objectForKey:@"mtu"]) {
+ _mtu = [(NSNumber*)[jsonData objectForKey:@"mtu"] intValue];
+ }
+
+ if([jsonData objectForKey:@"name"]) {
+ _name = (NSString*)[jsonData objectForKey:@"name"];
+ }
+
+ if([jsonData objectForKey:@"netconfRevision"]) {
+ _netconfRevision = [(NSNumber*)[jsonData objectForKey:@"netconfRevision"] intValue];
+ }
+
+ if([jsonData objectForKey:@"nwid"]) {
+ NSString *networkid = (NSString*)[jsonData objectForKey:@"nwid"];
+
+ NSScanner *scanner = [NSScanner scannerWithString:networkid];
+ [scanner scanHexLongLong:&_nwid];
+ }
+
+ if([jsonData objectForKey:@"portDeviceName"]) {
+ _portDeviceName = (NSString*)[jsonData objectForKey:@"portDeviceName"];
+ }
+
+ if([jsonData objectForKey:@"portError"]) {
+ _portError = [(NSNumber*)[jsonData objectForKey:@"portError"] intValue];
+ }
+
+ if([jsonData objectForKey:@"allowManaged"]) {
+ _allowManaged = [(NSNumber*)[jsonData objectForKey:@"allowManaged"] boolValue];
+ }
+
+ if([jsonData objectForKey:@"allowGlobal"]) {
+ _allowGlobal = [(NSNumber*)[jsonData objectForKey:@"allowGlobal"] boolValue];
+ }
+
+ if([jsonData objectForKey:@"allowDefault"]) {
+ _allowDefault = [(NSNumber*)[jsonData objectForKey:@"allowDefault"] boolValue];
+ }
+
+ if([jsonData objectForKey:@"status"]) {
+ NSString *statusStr = (NSString*)[jsonData objectForKey:@"status"];
+ if([statusStr isEqualToString:@"REQUESTING_CONFIGURATION"]) {
+ _status = REQUESTING_CONFIGURATION;
+ }
+ else if([statusStr isEqualToString:@"OK"]) {
+ _status = OK;
+ }
+ else if([statusStr isEqualToString:@"ACCESS_DENIED"]) {
+ _status = ACCESS_DENIED;
+ }
+ else if([statusStr isEqualToString:@"NOT_FOUND"]) {
+ _status = NOT_FOUND;
+ }
+ else if([statusStr isEqualToString:@"PORT_ERROR"]) {
+ _status = PORT_ERROR;
+ }
+ else if([statusStr isEqualToString:@"CLIENT_TOO_OLD"]) {
+ _status = CLIENT_TOO_OLD;
+ }
+ }
+
+ if([jsonData objectForKey:@"type"]) {
+ NSString *typeStr = (NSString*)[jsonData objectForKey:@"type"];
+ if([typeStr isEqualToString:@"PRIVATE"]) {
+ _type = PRIVATE;
+ }
+ else if([typeStr isEqualToString:@"PUBLIC"]) {
+ _type = PUBLIC;
+ }
+ }
+
+ _connected = YES;
+ }
+
+ return self;
+}
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super init];
+
+ if(self) {
+ if([aDecoder containsValueForKey:NetworkAddressesKey]) {
+ _assignedAddresses = (NSArray<NSString*>*)[aDecoder decodeObjectForKey:NetworkAddressesKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkBridgeKey]) {
+ _bridge = [aDecoder decodeBoolForKey:NetworkBridgeKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkBroadcastKey]) {
+ _broadcastEnabled = [aDecoder decodeBoolForKey:NetworkBroadcastKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkDhcpKey]) {
+ _dhcp = [aDecoder decodeBoolForKey:NetworkDhcpKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkMacKey]) {
+ _mac = (NSString*)[aDecoder decodeObjectForKey:NetworkMacKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkMtuKey]) {
+ _mtu = (int)[aDecoder decodeIntegerForKey:NetworkMtuKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkNameKey]) {
+ _name = (NSString*)[aDecoder decodeObjectForKey:NetworkNameKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkNetconfKey]) {
+ _netconfRevision = (int)[aDecoder decodeIntegerForKey:NetworkNetconfKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkNwidKey]) {
+ _nwid = [(NSNumber*)[aDecoder decodeObjectForKey:NetworkNwidKey] unsignedLongLongValue];
+ }
+
+ if([aDecoder containsValueForKey:NetworkPortNameKey]) {
+ _portDeviceName = (NSString*)[aDecoder decodeObjectForKey:NetworkPortNameKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkPortErrorKey]) {
+ _portError = (int)[aDecoder decodeIntegerForKey:NetworkPortErrorKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkStatusKey]) {
+ _status = (enum NetworkStatus)[aDecoder decodeIntegerForKey:NetworkStatusKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkTypeKey]) {
+ _type = (enum NetworkType)[aDecoder decodeIntegerForKey:NetworkTypeKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkAllowManagedKey]) {
+ _allowManaged = [aDecoder decodeBoolForKey:NetworkAllowManagedKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkAllowGlobalKey]) {
+ _allowGlobal = [aDecoder decodeBoolForKey:NetworkAllowGlobalKey];
+ }
+
+ if([aDecoder containsValueForKey:NetworkAllowDefaultKey]) {
+ _allowDefault = [aDecoder decodeBoolForKey:NetworkAllowDefaultKey];
+ }
+
+ _connected = NO;
+ }
+
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder
+{
+ [aCoder encodeObject:_assignedAddresses forKey:NetworkAddressesKey];
+ [aCoder encodeBool:_bridge forKey:NetworkBridgeKey];
+ [aCoder encodeBool:_broadcastEnabled forKey:NetworkBroadcastKey];
+ [aCoder encodeBool:_dhcp forKey:NetworkDhcpKey];
+ [aCoder encodeObject:_mac forKey:NetworkMacKey];
+ [aCoder encodeInteger:_mtu forKey:NetworkMtuKey];
+ [aCoder encodeObject:_name forKey:NetworkNameKey];
+ [aCoder encodeInteger:_netconfRevision forKey:NetworkNetconfKey];
+ [aCoder encodeObject:[NSNumber numberWithUnsignedLongLong:_nwid]
+ forKey:NetworkNwidKey];
+ [aCoder encodeObject:_portDeviceName forKey:NetworkPortNameKey];
+ [aCoder encodeInteger:_portError forKey:NetworkPortErrorKey];
+ [aCoder encodeInteger:_status forKey:NetworkStatusKey];
+ [aCoder encodeInteger:_type forKey:NetworkTypeKey];
+ [aCoder encodeBool:_allowManaged forKey:NetworkAllowManagedKey];
+ [aCoder encodeBool:_allowGlobal forKey:NetworkAllowGlobalKey];
+ [aCoder encodeBool:_allowDefault forKey:NetworkAllowDefaultKey];
+}
+
++ (BOOL)defaultRouteExists:(NSArray<Network *>*)netList
+{
+ for(Network *net in netList) {
+ if (net.allowDefault && net.connected) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (NSString*)statusString {
+ switch(_status) {
+ case REQUESTING_CONFIGURATION:
+ return @"REQUESTING_CONFIGURATION";
+ case OK:
+ return @"OK";
+ case ACCESS_DENIED:
+ return @"ACCESS_DENIED";
+ case NOT_FOUND:
+ return @"NOT_FOUND";
+ case PORT_ERROR:
+ return @"PORT_ERROR";
+ case CLIENT_TOO_OLD:
+ return @"CLIENT_TOO_OLD";
+ default:
+ return @"";
+ }
+}
+
+- (NSString*)typeString {
+ switch(_type) {
+ case PUBLIC:
+ return @"PUBLIC";
+ case PRIVATE:
+ return @"PRIVATE";
+ default:
+ return @"";
+ }
+}
+
+- (BOOL)hasSameNetworkId:(UInt64)networkId
+{
+ return self.nwid == networkId;
+}
+
+- (BOOL)isEqualToNetwork:(Network*)network
+{
+ return [self.assignedAddresses isEqualToArray:network.assignedAddresses] &&
+ self.bridge == network.bridge &&
+ self.broadcastEnabled == network.broadcastEnabled &&
+ self.dhcp == network.dhcp &&
+ [self.mac isEqualToString:network.mac] &&
+ self.mtu == network.mtu &&
+ self.netconfRevision == network.netconfRevision &&
+ [self.name isEqualToString:network.name] &&
+ self.nwid == network.nwid &&
+ [self.portDeviceName isEqualToString:network.portDeviceName] &&
+ self.status == network.status &&
+ self.type == network.type &&
+ self.allowManaged == network.allowManaged &&
+ self.allowGlobal == network.allowGlobal &&
+ self.allowDefault == network.allowDefault &&
+ self.connected == network.connected;
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if (self == object) {
+ return YES;
+ }
+
+ if (![object isKindOfClass:[Network class]]) {
+ return NO;
+ }
+
+ return [self isEqualToNetwork:object];
+}
+
+- (NSUInteger)hash
+{
+ return [self.assignedAddresses hash] ^
+ self.bridge ^
+ self.broadcastEnabled ^
+ self.dhcp ^
+ [self.mac hash] ^
+ self.mtu ^
+ self.netconfRevision ^
+ [self.name hash] ^
+ self.nwid ^
+ [self.portDeviceName hash] ^
+ self.portError ^
+ self.status ^
+ self.type ^
+ self.allowManaged ^
+ self.allowGlobal ^
+ self.allowDefault ^
+ self.connected;
+}
+
+@end
diff --git a/macui/ZeroTier One/NetworkInfoCell.h b/macui/ZeroTier One/NetworkInfoCell.h
new file mode 100644
index 00000000..be9345d7
--- /dev/null
+++ b/macui/ZeroTier One/NetworkInfoCell.h
@@ -0,0 +1,50 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class ShowNetworksViewController;
+
+@interface NetworkInfoCell : NSTableCellView
+
+@property (weak, nonatomic) ShowNetworksViewController *parent;
+
+@property (weak, nonatomic) IBOutlet NSTextField *networkIdField;
+@property (weak, nonatomic) IBOutlet NSTextField *networkNameField;
+@property (weak, nonatomic) IBOutlet NSTextField *statusField;
+@property (weak, nonatomic) IBOutlet NSTextField *typeField;
+@property (weak, nonatomic) IBOutlet NSTextField *macField;
+@property (weak, nonatomic) IBOutlet NSTextField *mtuField;
+@property (weak, nonatomic) IBOutlet NSTextField *broadcastField;
+@property (weak, nonatomic) IBOutlet NSTextField *bridgingField;
+@property (weak, nonatomic) IBOutlet NSTextField *deviceField;
+@property (weak, nonatomic) IBOutlet NSTextField *addressesField;
+@property (weak, nonatomic) IBOutlet NSButton *allowManaged;
+@property (weak, nonatomic) IBOutlet NSButton *allowGlobal;
+@property (weak, nonatomic) IBOutlet NSButton *allowDefault;
+@property (weak, nonatomic) IBOutlet NSButton *connectedCheckbox;
+@property (weak, nonatomic) IBOutlet NSButton *deleteButton;
+
+- (IBAction)onConnectCheckStateChanged:(NSButton*)sender;
+- (IBAction)deleteNetwork:(NSButton*)sender;
+- (IBAction)onAllowStatusChanged:(NSButton*)sender;
+
+- (void)joinNetwork:(NSString*)nwid;
+- (void)leaveNetwork:(NSString*)nwid;
+
+@end
diff --git a/macui/ZeroTier One/NetworkInfoCell.m b/macui/ZeroTier One/NetworkInfoCell.m
new file mode 100644
index 00000000..dc75cab3
--- /dev/null
+++ b/macui/ZeroTier One/NetworkInfoCell.m
@@ -0,0 +1,85 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "NetworkInfoCell.h"
+#import "ServiceCom.h"
+#import "ShowNetworksViewController.h"
+#import "Network.h"
+
+@implementation NetworkInfoCell
+
+- (void)drawRect:(NSRect)dirtyRect {
+ [super drawRect:dirtyRect];
+
+ // Drawing code here.
+}
+
+- (IBAction)onConnectCheckStateChanged:(NSButton*)sender
+{
+ if(sender.state == NSOnState) {
+ [self joinNetwork:self.networkIdField.stringValue];
+ }
+ else {
+ [self leaveNetwork:self.networkIdField.stringValue];
+ }
+}
+
+- (IBAction)deleteNetwork:(NSButton*)sender;
+{
+ [self leaveNetwork:self.networkIdField.stringValue];
+ [self.parent deleteNetworkFromList:self.networkIdField.stringValue];
+}
+
+- (IBAction)onAllowStatusChanged:(NSButton*)sender
+{
+ [self joinNetwork:self.networkIdField.stringValue];
+}
+
+- (void)joinNetwork:(NSString*)nwid
+{
+ NSError *error = nil;
+ [[ServiceCom sharedInstance] joinNetwork:nwid
+ allowManaged:(self.allowManaged.state == NSOnState)
+ allowGlobal:(self.allowGlobal.state == NSOnState)
+ allowDefault:![Network defaultRouteExists:_parent.networkList] && (self.allowDefault.state == NSOnState)
+ error:&error];
+
+ if (error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ }
+}
+
+- (void)leaveNetwork:(NSString*)nwid
+{
+ NSError *error = nil;
+ [[ServiceCom sharedInstance] leaveNetwork:nwid error:&error];
+
+ if (error) {
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Ok"];
+
+ [alert runModal];
+ }
+}
+
+@end
diff --git a/macui/ZeroTier One/NetworkMonitor.h b/macui/ZeroTier One/NetworkMonitor.h
new file mode 100644
index 00000000..8cdec4ed
--- /dev/null
+++ b/macui/ZeroTier One/NetworkMonitor.h
@@ -0,0 +1,45 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+extern NSString * const NetworkUpdateKey;
+extern NSString * const StatusUpdateKey;
+
+@class Network;
+
+@interface NetworkMonitor : NSObject
+{
+ NSMutableArray<Network*> *_savedNetworks;
+ NSArray<Network*> *_receivedNetworks;
+ NSMutableArray<Network*> *_allNetworks;
+
+ NSTimer *_timer;
+}
+
+- (id)init;
+- (void)dealloc;
+
+- (void)start;
+- (void)stop;
+
+- (void)updateNetworkInfo;
+
+- (void)deleteSavedNetwork:(NSString*)networkId;
+
+@end
diff --git a/macui/ZeroTier One/NetworkMonitor.m b/macui/ZeroTier One/NetworkMonitor.m
new file mode 100644
index 00000000..7ed22c4a
--- /dev/null
+++ b/macui/ZeroTier One/NetworkMonitor.m
@@ -0,0 +1,253 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "NetworkMonitor.h"
+#import "Network.h"
+#import "ServiceCom.h"
+#import "NodeStatus.h"
+
+@import AppKit;
+
+
+NSString * const NetworkUpdateKey = @"com.zerotier.one.network-list";
+NSString * const StatusUpdateKey = @"com.zerotier.one.status";
+
+@interface NetworkMonitor (private)
+
+- (NSString*)dataFile;
+- (void)internal_updateNetworkInfo;
+- (NSInteger)findNetworkWithID:(UInt64)networkId;
+- (NSInteger)findSavedNetworkWithID:(UInt64)networkId;
+- (void)saveNetworks;
+
+@end
+
+@implementation NetworkMonitor
+
+- (id)init
+{
+ self = [super init];
+ if(self)
+ {
+ _savedNetworks = [NSMutableArray<Network*> array];
+ _receivedNetworks = [NSArray<Network*> array];
+ _allNetworks = [NSMutableArray<Network*> array];
+ _timer = nil;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_timer invalidate];
+}
+
+- (void)start
+{
+ NSLog(@"ZeroTier monitor started");
+ _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
+ target:self
+ selector:@selector(updateNetworkInfo)
+ userInfo:nil
+ repeats:YES];
+}
+
+- (void)stop
+{
+ NSLog(@"ZeroTier monitor stopped");
+ [_timer invalidate];
+ _timer = nil;
+}
+
+- (void)updateNetworkInfo
+{
+ NSString *filePath = [self dataFile];
+
+ if([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ NSArray<Network*> *networks = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
+
+ if(networks != nil) {
+ _savedNetworks = [networks mutableCopy];
+ }
+ }
+
+ NSError *error = nil;
+
+ [[ServiceCom sharedInstance] getNetworklist:^(NSArray<Network *> *networkList) {
+ _receivedNetworks = networkList;
+
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ [self internal_updateNetworkInfo];
+ } ];
+ } error:&error];
+
+ if(error) {
+ [self stop];
+
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res = [alert runModal];
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ }
+ else if(res == NSAlertSecondButtonReturn) {
+ [self start];
+ return;
+ }
+ }
+
+ [[ServiceCom sharedInstance] getNodeStatus:^(NodeStatus *status) {
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:status forKey:@"status"];
+
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:StatusUpdateKey
+ object:nil
+ userInfo:userInfo];
+ }];
+ } error:&error];
+
+ if (error) {
+ [self stop];
+
+ NSAlert *alert = [NSAlert alertWithError:error];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res = [alert runModal];
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ }
+ else if(res == NSAlertSecondButtonReturn) {
+ [self start];
+ return;
+ }
+ }
+}
+
+- (void)deleteSavedNetwork:(NSString*)networkId
+{
+ UInt64 nwid = 0;
+ NSScanner *scanner = [NSScanner scannerWithString:networkId];
+ [scanner scanHexLongLong:&nwid];
+
+ NSInteger index = [self findNetworkWithID:nwid];
+
+ if(index != NSNotFound) {
+ [_allNetworks removeObjectAtIndex:index];
+ }
+
+ index = [self findSavedNetworkWithID:nwid];
+
+ if(index != NSNotFound) {
+ [_savedNetworks removeObjectAtIndex:index];
+ }
+
+ [self saveNetworks];
+}
+
+@end
+
+@implementation NetworkMonitor (private)
+- (NSString*)dataFile
+{
+ NSURL *appSupport = [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
+ inDomains:NSUserDomainMask] objectAtIndex:0];
+
+ appSupport = [[[appSupport URLByAppendingPathComponent:@"ZeroTier"] URLByAppendingPathComponent:@"One"] URLByAppendingPathComponent:@"networkinfo.dat"];
+ return appSupport.path;
+}
+
+- (void)internal_updateNetworkInfo
+{
+ NSMutableArray<Network*> *networks = [_savedNetworks mutableCopy];
+
+ for(Network *nw in _receivedNetworks) {
+ NSInteger index = [self findSavedNetworkWithID:nw.nwid];
+
+ if(index != NSNotFound) {
+ [networks setObject:nw atIndexedSubscript:index];
+ }
+ else {
+ [networks addObject:nw];
+ }
+ }
+
+ [networks sortUsingComparator:^NSComparisonResult(Network *obj1, Network *obj2) {
+ if(obj1.nwid > obj2.nwid) {
+ return true;
+ }
+ return false;
+ }];
+
+ @synchronized(_allNetworks) {
+ _allNetworks = networks;
+ }
+
+ [self saveNetworks];
+
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:networks forKey:@"networks"];
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:NetworkUpdateKey
+ object:nil
+ userInfo:userInfo];
+}
+
+- (NSInteger)findNetworkWithID:(UInt64)networkId
+{
+ for(int i = 0; i < [_allNetworks count]; ++i) {
+ Network *nw = [_allNetworks objectAtIndex:i];
+
+ if(nw.nwid == networkId) {
+ return i;
+ }
+ }
+
+ return NSNotFound;
+}
+
+
+- (NSInteger)findSavedNetworkWithID:(UInt64)networkId
+{
+ for(int i = 0; i < [_savedNetworks count]; ++i) {
+ Network *nw = [_savedNetworks objectAtIndex:i];
+
+ if(nw.nwid == networkId) {
+ return i;
+ }
+ }
+
+ return NSNotFound;
+}
+
+- (void)saveNetworks
+{
+ NSString *filePath = [self dataFile];
+
+ @synchronized(_allNetworks) {
+ [NSKeyedArchiver archiveRootObject:_allNetworks toFile:filePath];
+ }
+}
+
+@end
diff --git a/macui/ZeroTier One/NodeStatus.h b/macui/ZeroTier One/NodeStatus.h
new file mode 100644
index 00000000..eab5bfe4
--- /dev/null
+++ b/macui/ZeroTier One/NodeStatus.h
@@ -0,0 +1,35 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface NodeStatus : NSObject
+
+@property (readonly) NSString *address;
+@property (readonly) NSString *publicIdentity;
+@property (readonly) BOOL online;
+@property (readonly) BOOL tcpFallbackActive;
+@property (readonly) int versionMajor;
+@property (readonly) int versionMinor;
+@property (readonly) int versionRev;
+@property (readonly) NSString *version;
+@property (readonly) UInt64 clock;
+
+- (id)initWithJsonData:(NSDictionary*)jsonData;
+
+@end
diff --git a/macui/ZeroTier One/NodeStatus.m b/macui/ZeroTier One/NodeStatus.m
new file mode 100644
index 00000000..3bae3c7d
--- /dev/null
+++ b/macui/ZeroTier One/NodeStatus.m
@@ -0,0 +1,40 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#import "NodeStatus.h"
+
+@implementation NodeStatus
+
+- (id)initWithJsonData:(NSDictionary*)jsonData
+{
+ self = [super init];
+
+ if(self) {
+ _address = (NSString*)[jsonData objectForKey:@"address"];
+ _publicIdentity = (NSString*)[jsonData objectForKey:@"publicIdentity"];
+ _online = [(NSNumber*)[jsonData objectForKey:@"online"] boolValue];
+ _tcpFallbackActive = [(NSNumber*)[jsonData objectForKey:@"tcpFallbackActive"] boolValue];
+ _versionMajor = [(NSNumber*)[jsonData objectForKey:@"versionMajor"] intValue];
+ _versionMinor = [(NSNumber*)[jsonData objectForKey:@"versionMinor"] intValue];
+ _versionRev = [(NSNumber*)[jsonData objectForKey:@"versionRev"] intValue];
+ _version = (NSString*)[jsonData objectForKey:@"version"];
+ _clock = [(NSNumber*)[jsonData objectForKey:@"clock"] unsignedLongLongValue];
+ }
+
+ return self;
+}
+@end
diff --git a/node/NonCopyable.hpp b/macui/ZeroTier One/PreferencesViewController.h
index 6d4daa86..56d0fdb8 100644
--- a/node/NonCopyable.hpp
+++ b/macui/ZeroTier One/PreferencesViewController.h
@@ -16,23 +16,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ZT_NONCOPYABLE_HPP__
-#define ZT_NONCOPYABLE_HPP__
+#import <Cocoa/Cocoa.h>
-namespace ZeroTier {
+@interface PreferencesViewController : NSViewController
-/**
- * A simple concept that belongs in the C++ language spec
- */
-class NonCopyable
-{
-protected:
- NonCopyable() throw() {}
-private:
- NonCopyable(const NonCopyable&);
- const NonCopyable& operator=(const NonCopyable&);
-};
+@property (nonatomic, weak) IBOutlet NSButton *startupCheckBox;
+
+- (IBAction)onStartupCheckBoxChanged:(NSButton*)sender;
-} // namespace ZeroTier
+- (BOOL)isLaunchAtStartup;
+- (LSSharedFileListItemRef)itemRefInLoginItems;
+- (void)setLaunchAtLoginEnabled:(BOOL)enabled;
-#endif
+@end
diff --git a/macui/ZeroTier One/PreferencesViewController.m b/macui/ZeroTier One/PreferencesViewController.m
new file mode 100644
index 00000000..13927fba
--- /dev/null
+++ b/macui/ZeroTier One/PreferencesViewController.m
@@ -0,0 +1,112 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "PreferencesViewController.h"
+
+@interface PreferencesViewController ()
+
+@end
+
+@implementation PreferencesViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ if([self isLaunchAtStartup]) {
+ self.startupCheckBox.state = NSOnState;
+ }
+ else {
+ self.startupCheckBox.state = NSOffState;
+ }
+}
+
+- (IBAction)onStartupCheckBoxChanged:(NSButton *)sender
+{
+ if(sender.state == NSOnState) {
+ [self setLaunchAtLoginEnabled:YES];
+ }
+ else {
+ [self setLaunchAtLoginEnabled:NO];
+ }
+
+}
+
+- (void)setLaunchAtLoginEnabled:(BOOL)enabled
+{
+ LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
+
+ if (enabled) {
+ // Add the app to the LoginItems list.
+ CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
+ LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
+ if (itemRef) CFRelease(itemRef);
+ }
+ else {
+ // Remove the app from the LoginItems list.
+ LSSharedFileListItemRef itemRef = [self itemRefInLoginItems];
+ LSSharedFileListItemRemove(loginItemsRef,itemRef);
+ if (itemRef != nil) CFRelease(itemRef);
+ }
+}
+
+
+- (BOOL)isLaunchAtStartup {
+ // See if the app is currently in LoginItems.
+ LSSharedFileListItemRef itemRef = [self itemRefInLoginItems];
+ // Store away that boolean.
+ BOOL isInList = itemRef != nil;
+ // Release the reference if it exists.
+ if (itemRef != nil) CFRelease(itemRef);
+
+ return isInList;
+}
+
+- (LSSharedFileListItemRef)itemRefInLoginItems {
+ LSSharedFileListItemRef itemRef = nil;
+
+ NSString * appPath = [[NSBundle mainBundle] bundlePath];
+
+ // This will retrieve the path for the application
+ // For example, /Applications/test.app
+ CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:appPath];
+
+ // Create a reference to the shared file list.
+ LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
+
+ if (loginItems) {
+ UInt32 seedValue;
+ //Retrieve the list of Login Items and cast them to
+ // a NSArray so that it will be easier to iterate.
+ NSArray *loginItemsArray = (__bridge NSArray *)LSSharedFileListCopySnapshot(loginItems, &seedValue);
+ for(int i = 0; i< [loginItemsArray count]; i++){
+ LSSharedFileListItemRef currentItemRef = (__bridge LSSharedFileListItemRef)[loginItemsArray
+ objectAtIndex:i];
+ //Resolve the item with URL
+ if (LSSharedFileListItemResolve(currentItemRef, 0, (CFURLRef*) &url, NULL) == noErr) {
+ NSString * urlPath = [(__bridge NSURL*)url path];
+ if ([urlPath compare:appPath] == NSOrderedSame){
+ itemRef = currentItemRef;
+ }
+ }
+ }
+ }
+ CFRelease(loginItems);
+ return itemRef;
+}
+
+@end
diff --git a/macui/ZeroTier One/PreferencesViewController.xib b/macui/ZeroTier One/PreferencesViewController.xib
new file mode 100644
index 00000000..62aef4c0
--- /dev/null
+++ b/macui/ZeroTier One/PreferencesViewController.xib
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="PreferencesViewController" customModule="ZeroTier_One" customModuleProvider="target">
+ <connections>
+ <outlet property="startupCheckBox" destination="XSk-jN-ner" id="nvL-b1-gza"/>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="284" height="54"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XSk-jN-ner">
+ <rect key="frame" x="18" y="18" width="248" height="18"/>
+ <buttonCell key="cell" type="check" title="Start ZeroTier One on system startup" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="VkJ-h4-tHf">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onStartupCheckBoxChanged:" target="-2" id="zAQ-DJ-c3w"/>
+ </connections>
+ </button>
+ </subviews>
+ <point key="canvasLocation" x="365" y="208"/>
+ </customView>
+ </objects>
+</document>
diff --git a/macui/ZeroTier One/ServiceCom.h b/macui/ZeroTier One/ServiceCom.h
new file mode 100644
index 00000000..c2b4692f
--- /dev/null
+++ b/macui/ZeroTier One/ServiceCom.h
@@ -0,0 +1,40 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Foundation/Foundation.h>
+
+@class NodeStatus;
+@class Network;
+
+@interface ServiceCom : NSObject
+{
+ NSString *baseURL;
+ NSURLSession *session;
+ BOOL _isQuitting;
+ BOOL _resetKey;
+}
++ (ServiceCom*)sharedInstance;
+
+- (id)init;
+
+- (void)getNetworklist:(void (^)(NSArray<Network*>*))completionHandler error:(NSError* __autoreleasing *)error;
+- (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler error:(NSError*__autoreleasing*)error;
+- (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault error:(NSError*__autoreleasing*)error;
+- (void)leaveNetwork:(NSString*)networkId error:(NSError*__autoreleasing*)error;
+
+@end
diff --git a/macui/ZeroTier One/ServiceCom.m b/macui/ZeroTier One/ServiceCom.m
new file mode 100644
index 00000000..75b98502
--- /dev/null
+++ b/macui/ZeroTier One/ServiceCom.m
@@ -0,0 +1,516 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "ServiceCom.h"
+#import "AuthtokenCopy.h"
+#import "Network.h"
+#import "NodeStatus.h"
+@import AppKit;
+
+@interface ServiceCom (Private)
+
+- (NSString*)key;
+
+@end
+
+@implementation ServiceCom
+
++ (ServiceCom*)sharedInstance {
+ static ServiceCom *sc = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ sc = [[ServiceCom alloc] init];
+ });
+ return sc;
+}
+
+- (id)init
+{
+ self = [super init];
+ if(self) {
+ baseURL = @"http://127.0.0.1:9993";
+ session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
+ _isQuitting = NO;
+ _resetKey = NO;
+ }
+
+ return self;
+}
+
+- (NSString*)key:(NSError* __autoreleasing *)err
+{
+ static NSString *k = nil;
+ static NSUInteger resetCount = 0;
+
+ @synchronized (self) {
+ if (_isQuitting) {
+ return @"";
+ }
+
+ if (_resetKey && k != nil) {
+ k = nil;
+ ++resetCount;
+ NSLog(@"ResetCount: %lu", (unsigned long)resetCount);
+ if (resetCount > 10) {
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithMessageText:@"Error obtaining Auth Token"
+ defaultButton:@"Quit"
+ alternateButton:@"Retry"
+ otherButton:nil
+ informativeTextWithFormat:@"Please ensure ZeroTier is installed correctly"];
+ alert.alertStyle = NSCriticalAlertStyle;
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == 1) {
+ _isQuitting = YES;
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ }
+ }];
+ return @"";
+ }
+ }
+
+ if (k == nil) {
+ NSError *error = nil;
+ NSURL *appSupportDir = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:false error:&error];
+
+ if (error) {
+ NSLog(@"Error: %@", error);
+ return @"";
+ }
+
+ appSupportDir = [[appSupportDir URLByAppendingPathComponent:@"ZeroTier"] URLByAppendingPathComponent:@"One"];
+ NSURL *authtokenURL = [appSupportDir URLByAppendingPathComponent:@"authtoken.secret"];
+
+ if (!_resetKey && [[NSFileManager defaultManager] fileExistsAtPath:[authtokenURL path]]) {
+ k = [NSString stringWithContentsOfURL:authtokenURL
+ encoding:NSUTF8StringEncoding
+ error:&error];
+
+ k = [k stringByReplacingOccurrencesOfString:@"\n" withString:@""];
+
+ if (error) {
+ NSLog(@"Error: %@", error);
+ k = nil;
+ *err = error;
+ return @"";
+ }
+ }
+ else {
+ _resetKey = NO;
+ NSURL *sysAppSupportDir = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSSystemDomainMask appropriateForURL:nil create:false error:nil];
+
+ sysAppSupportDir = [[sysAppSupportDir URLByAppendingPathComponent:@"ZeroTier"] URLByAppendingPathComponent:@"One"];
+ NSURL *sysAuthtokenURL = [sysAppSupportDir URLByAppendingPathComponent:@"authtoken.secret"];
+
+ if(![[NSFileManager defaultManager] fileExistsAtPath:[sysAuthtokenURL path]]) {
+
+ }
+
+ [[NSFileManager defaultManager] createDirectoryAtURL:appSupportDir
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error];
+
+ if (error) {
+ NSLog(@"Error: %@", error);
+ *err = error;
+ k = nil;
+ return @"";
+ }
+
+ AuthorizationRef authRef;
+ OSStatus status = AuthorizationCreate(nil, nil, kAuthorizationFlagDefaults, &authRef);
+
+ if (status != errAuthorizationSuccess) {
+ NSLog(@"Authorization Failed! %d", status);
+
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't create AuthorizationRef", nil),
+ };
+ *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo];
+
+ return @"";
+ }
+
+ AuthorizationItem authItem;
+ authItem.name = kAuthorizationRightExecute;
+ authItem.valueLength = 0;
+ authItem.flags = 0;
+
+ AuthorizationRights authRights;
+ authRights.count = 1;
+ authRights.items = &authItem;
+
+ AuthorizationFlags authFlags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagPreAuthorize |
+ kAuthorizationFlagExtendRights;
+
+ status = AuthorizationCopyRights(authRef, &authRights, nil, authFlags, nil);
+
+ if (status != errAuthorizationSuccess) {
+ NSLog(@"Authorization Failed! %d", status);
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Couldn't copy authorization rights", nil),
+ };
+ *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo];
+ return @"";
+ }
+
+ NSString *localKey = getAdminAuthToken(authRef);
+ AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
+
+ if (localKey != nil && [localKey lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > 0) {
+ k = localKey;
+
+ [localKey writeToURL:authtokenURL
+ atomically:YES
+ encoding:NSUTF8StringEncoding
+ error:&error];
+
+ if (error) {
+ NSLog(@"Error writing token to disk: %@", error);
+ *err = error;
+ }
+ }
+ }
+ }
+
+ if (k == nil) {
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: NSLocalizedString(@"Unknown error finding authorization key", nil),
+ };
+ *err = [NSError errorWithDomain:@"com.zerotier.one" code:-1 userInfo:userInfo];
+
+ return @"";
+ }
+ }
+ return k;
+}
+
+- (void)getNetworklist:(void (^)(NSArray<Network *> *))completionHandler error:(NSError *__autoreleasing*)error
+{
+ NSString* key = [self key:error];
+ if(*error) {
+ return;
+ }
+
+ NSString *urlString = [[baseURL stringByAppendingString:@"/network?auth="] stringByAppendingString:key];
+
+ NSURL *url = [NSURL URLWithString:urlString];
+ NSURLSessionDataTask *task =
+ [session dataTaskWithURL:url
+ completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
+
+ if (err) {
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithError:err];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ _isQuitting = YES;
+ }
+ }];
+ return;
+ }
+
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
+ NSInteger status = [httpResponse statusCode];
+
+ NSError *err2;
+
+ if (status == 200) {
+ NSArray *json = [NSJSONSerialization JSONObjectWithData:data
+ options:0
+ error:&err2];
+ if (err) {
+ NSLog(@"Error fetching network list: %@", err2);
+
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithError:err2];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == NSAlertFirstButtonReturn) {
+ _isQuitting = YES;
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ }
+ }];
+ return;
+ }
+
+ NSMutableArray<Network*> *networks = [[NSMutableArray<Network*> alloc] init];
+ for(NSDictionary *dict in json) {
+ [networks addObject:[[Network alloc] initWithJsonData:dict]];
+ }
+
+ completionHandler(networks);
+ }
+ else if (status == 401) {
+ self->_resetKey = YES;
+ }
+ }];
+ [task resume];
+}
+
+- (void)getNodeStatus:(void (^)(NodeStatus*))completionHandler error:(NSError*__autoreleasing*)error
+{
+ NSString *key = [self key:error];
+ if(*error) {
+ return;
+ }
+
+ NSString *urlString = [[baseURL stringByAppendingString:@"/status?auth="] stringByAppendingString:key];
+
+ NSURL *url = [NSURL URLWithString:urlString];
+ NSURLSessionDataTask *task =
+ [session dataTaskWithURL:url
+ completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
+
+ if(err) {
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithError:err];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ _isQuitting = YES;
+ }
+ }];
+ return;
+ }
+
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
+ NSInteger status = [httpResponse statusCode];
+
+ NSError *err2;
+ if(status == 200) {
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data
+ options:0
+ error:&err2];
+
+ if(err2) {
+ NSLog(@"Error fetching node status: %@", err2);
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithError:err2];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ _isQuitting = YES;
+ }
+ }];
+ return;
+ }
+
+ NodeStatus *status = [[NodeStatus alloc] initWithJsonData:json];
+
+ completionHandler(status);
+ }
+ else if (status == 401) {
+ self->_resetKey = YES;
+ }
+ }];
+ [task resume];
+}
+
+- (void)joinNetwork:(NSString*)networkId allowManaged:(BOOL)allowManaged allowGlobal:(BOOL)allowGlobal allowDefault:(BOOL)allowDefault error:(NSError *__autoreleasing*)error
+{
+ NSString *key = [self key:error];
+ if(*error) {
+ return;
+ }
+
+ NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"]
+ stringByAppendingString:networkId]
+ stringByAppendingString:@"?auth="]
+ stringByAppendingString:key];
+
+ NSURL *url = [NSURL URLWithString:urlString];
+
+ NSMutableDictionary *jsonDict = [NSMutableDictionary dictionary];
+ [jsonDict setObject:[NSNumber numberWithBool:allowManaged] forKey:@"allowManaged"];
+ [jsonDict setObject:[NSNumber numberWithBool:allowGlobal] forKey:@"allowGlobal"];
+ [jsonDict setObject:[NSNumber numberWithBool:allowDefault] forKey:@"allowDefault"];
+
+ NSError *err = nil;
+
+ NSData *json = [NSJSONSerialization dataWithJSONObject:jsonDict
+ options:0
+ error:&err];
+
+ if(err) {
+ NSLog(@"Error creating json data: %@", err);
+ *error = err;
+ return;
+ }
+
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
+ request.HTTPMethod = @"POST";
+ request.HTTPBody = json;
+ [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+
+ NSURLSessionDataTask *task =
+ [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
+ if(err) {
+ NSLog(@"Error posting join request: %@", err);
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithError:err];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ _isQuitting = YES;
+ }
+ }];
+ }
+
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
+ NSInteger status = [httpResponse statusCode];
+
+ if(status == 200) {
+ NSLog(@"join ok");
+ }
+ else if (status == 401) {
+ self->_resetKey = YES;
+ }
+ else {
+ NSLog(@"join error: %ld", (long)status);
+ }
+ }];
+ [task resume];
+}
+
+- (void)leaveNetwork:(NSString*)networkId error:(NSError*__autoreleasing*)error
+{
+ NSString *key = [self key:error];
+ if(*error) {
+ return;
+ }
+
+ NSString *urlString = [[[[baseURL stringByAppendingString:@"/network/"]
+ stringByAppendingString:networkId]
+ stringByAppendingString:@"?auth="]
+ stringByAppendingString:key];
+
+ NSURL *url = [NSURL URLWithString:urlString];
+
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
+ request.HTTPMethod = @"DELETE";
+
+ NSURLSessionDataTask *task =
+ [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable err) {
+ if(err) {
+ NSLog(@"Error posting delete request: %@", err);
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+ NSAlert *alert = [NSAlert alertWithError:err];
+ alert.alertStyle = NSCriticalAlertStyle;
+ [alert addButtonWithTitle:@"Quit"];
+ [alert addButtonWithTitle:@"Retry"];
+
+ NSModalResponse res;
+ if (!_isQuitting) {
+ res = [alert runModal];
+ }
+ else {
+ return;
+ }
+
+ if(res == NSAlertFirstButtonReturn) {
+ [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0];
+ _isQuitting = YES;
+ }
+ }];
+ return;
+ }
+
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
+ NSInteger status = httpResponse.statusCode;
+
+ if(status == 200) {
+ NSLog(@"leave ok");
+ }
+ else if (status == 401) {
+ self->_resetKey = YES;
+ }
+ else {
+ NSLog(@"leave error: %ld", status);
+ }
+ }];
+ [task resume];
+}
+
+@end
diff --git a/macui/ZeroTier One/ShowNetworksViewController.h b/macui/ZeroTier One/ShowNetworksViewController.h
new file mode 100644
index 00000000..6138958d
--- /dev/null
+++ b/macui/ZeroTier One/ShowNetworksViewController.h
@@ -0,0 +1,36 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@class NetworkMonitor;
+@class Network;
+
+@interface ShowNetworksViewController : NSViewController<NSTableViewDelegate, NSTableViewDataSource>
+
+@property (nonatomic) NSMutableArray<Network*> *networkList;
+@property (nonatomic) NetworkMonitor *netMonitor;
+@property (nonatomic) BOOL visible;
+
+@property (weak, nonatomic) IBOutlet NSTableView *tableView;
+
+- (void)deleteNetworkFromList:(NSString*)nwid;
+- (void)setNetworks:(NSArray<Network*>*)list;
+
+
+@end
diff --git a/macui/ZeroTier One/ShowNetworksViewController.m b/macui/ZeroTier One/ShowNetworksViewController.m
new file mode 100644
index 00000000..903a4b44
--- /dev/null
+++ b/macui/ZeroTier One/ShowNetworksViewController.m
@@ -0,0 +1,181 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import "ShowNetworksViewController.h"
+#import "NetworkMonitor.h"
+#import "NetworkInfoCell.h"
+#import "Network.h"
+
+BOOL hasNetworkWithID(NSArray<Network*> *list, UInt64 nwid)
+{
+ for(Network *n in list) {
+ if(n.nwid == nwid) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+@interface ShowNetworksViewController ()
+
+@end
+
+@implementation ShowNetworksViewController
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.networkList = [NSMutableArray array];
+
+ [self.tableView setDelegate:self];
+ [self.tableView setDataSource:self];
+ [self.tableView setBackgroundColor:[NSColor clearColor]];
+}
+
+- (void)viewWillAppear {
+ [super viewWillAppear];
+ self.visible = YES;
+}
+
+- (void)viewWillDisappear {
+ [super viewWillDisappear];
+ self.visible = NO;
+}
+
+- (NSInteger)findNetworkWithID:(UInt64)networkId
+{
+ for(int i = 0; i < [_networkList count]; ++i) {
+ Network *nw = [_networkList objectAtIndex:i];
+
+ if(nw.nwid == networkId) {
+ return i;
+ }
+ }
+
+ return NSNotFound;
+}
+
+
+- (void)deleteNetworkFromList:(NSString *)nwid {
+ [self.netMonitor deleteSavedNetwork:nwid];
+
+ UInt64 netid = 0;
+ NSScanner *scanner = [NSScanner scannerWithString:nwid];
+ [scanner scanHexLongLong:&netid];
+ for (Network *n in _networkList) {
+ if (n.nwid == netid) {
+ NSInteger index = [self findNetworkWithID:netid];
+
+ if (index != NSNotFound) {
+ [_networkList removeObjectAtIndex:index];
+ [_tableView reloadData];
+ }
+ }
+ }
+}
+
+- (void)setNetworks:(NSArray<Network *> *)list {
+ for (Network *n in list) {
+ if ([_networkList containsObject:n]) {
+ // don't need to do anything here. Already an identical object in the list
+ continue;
+ }
+ else {
+ // network not in the list based on equality. Did an object change? or is it a new item?
+ if (hasNetworkWithID(_networkList, n.nwid)) {
+
+ for (int i = 0; i < [_networkList count]; ++i) {
+ Network *n2 = [_networkList objectAtIndex:i];
+ if (n.nwid == n2.nwid)
+ {
+ [_networkList replaceObjectAtIndex:i withObject:n];
+ [_tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:i]
+ columnIndexes:[NSIndexSet indexSetWithIndex:0]];
+ }
+ }
+ }
+ else {
+ [_networkList addObject:n];
+ [_tableView reloadData];
+ }
+ }
+ }
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
+ return [_networkList count];
+}
+
+- (NSView*)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
+{
+ NetworkInfoCell *cell = (NetworkInfoCell*)[tableView makeViewWithIdentifier:@"NetworkInfoCell"
+ owner:nil];
+ Network *network = [_networkList objectAtIndex:row];
+ cell.parent = self;
+ cell.networkIdField.stringValue = [NSString stringWithFormat:@"%10llx", network.nwid];
+ cell.networkNameField.stringValue = network.name;
+ cell.statusField.stringValue = [network statusString];
+ cell.typeField.stringValue = [network typeString];
+ cell.mtuField.stringValue = [NSString stringWithFormat:@"%d", network.mtu];
+ cell.macField.stringValue = network.mac;
+ cell.broadcastField.stringValue = network.broadcastEnabled ? @"ENABLED" : @"DISABLED";
+ cell.bridgingField.stringValue = network.bridge ? @"ENABLED" : @"DISABLED";
+ cell.deviceField.stringValue = network.portDeviceName;
+
+ if(network.connected) {
+ cell.connectedCheckbox.state = NSOnState;
+
+ if(network.allowDefault) {
+ cell.allowDefault.enabled = YES;
+ cell.allowDefault.state = NSOnState;
+ }
+ else {
+ cell.allowDefault.state = NSOffState;
+
+ if([Network defaultRouteExists:_networkList]) {
+ cell.allowDefault.enabled = NO;
+ }
+ else {
+ cell.allowDefault.enabled = YES;
+ }
+ }
+
+ cell.allowGlobal.enabled = YES;
+ cell.allowManaged.enabled = YES;
+ }
+ else {
+ cell.connectedCheckbox.state = NSOffState;
+ cell.allowDefault.enabled = NO;
+ cell.allowGlobal.enabled = NO;
+ cell.allowManaged.enabled = NO;
+ }
+
+ cell.allowGlobal.state = network.allowGlobal ? NSOnState : NSOffState;
+ cell.allowManaged.state = network.allowManaged ? NSOnState : NSOffState;
+
+ cell.addressesField.stringValue = @"";
+
+ for(NSString *addr in network.assignedAddresses) {
+ cell.addressesField.stringValue = [[cell.addressesField.stringValue stringByAppendingString:addr] stringByAppendingString:@"\n"];
+ }
+
+ return cell;
+}
+
+@end
diff --git a/macui/ZeroTier One/ShowNetworksViewController.xib b/macui/ZeroTier One/ShowNetworksViewController.xib
new file mode 100644
index 00000000..62ac289a
--- /dev/null
+++ b/macui/ZeroTier One/ShowNetworksViewController.xib
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="ShowNetworksViewController" customModule="ZeroTier_One" customModuleProvider="target">
+ <connections>
+ <outlet property="tableView" destination="w5O-vn-cYB" id="Ud6-Bs-UFB"/>
+ <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="Hz6-mo-xeY">
+ <rect key="frame" x="0.0" y="0.0" width="570" height="521"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <subviews>
+ <scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="388" horizontalPageScroll="10" verticalLineScroll="388" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="miH-DQ-i4L">
+ <rect key="frame" x="20" y="20" width="530" height="481"/>
+ <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KXW-dX-mx6">
+ <rect key="frame" x="0.0" y="0.0" width="530" height="481"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="none" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="386" rowSizeStyle="automatic" viewBased="YES" id="w5O-vn-cYB">
+ <rect key="frame" x="0.0" y="0.0" width="530" height="481"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <size key="intercellSpacing" width="3" height="2"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/>
+ <tableViewGridLines key="gridStyleMask" horizontal="YES"/>
+ <color key="gridColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
+ <tableColumns>
+ <tableColumn width="527" minWidth="40" maxWidth="1000" id="ztK-JB-A6Q">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="WAX-7a-M9n">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <prototypeCellViews>
+ <tableCellView identifier="NetworkInfoCell" focusRingType="none" id="rmb-il-W5I" customClass="NetworkInfoCell">
+ <rect key="frame" x="1" y="1" width="527" height="386"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="EUT-1A-lgY">
+ <rect key="frame" x="480" y="364" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Label" id="bf8-gi-tpp">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UAC-7B-K8Y">
+ <rect key="frame" x="55" y="339" width="43" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Status" id="KgQ-AO-0Us">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FeX-UV-AFG">
+ <rect key="frame" x="64" y="314" width="34" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Type" id="8OM-iG-575">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LIA-GW-caM">
+ <rect key="frame" x="65" y="289" width="33" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="MAC" id="UK0-f8-L0U">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="EWq-81-Y6j">
+ <rect key="frame" x="64" y="264" width="34" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="MTU" id="5Wi-02-yNW">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Mi3-aV-yWg">
+ <rect key="frame" x="32" y="239" width="66" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Broadcast" id="b7V-FV-HH7">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="EKG-la-snQ">
+ <rect key="frame" x="43" y="214" width="55" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Bridging" id="bkm-pp-3tu">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="syI-eg-Zka">
+ <rect key="frame" x="52" y="114" width="46" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Device" id="3tY-hy-qbu">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6Af-7O-CaP">
+ <rect key="frame" x="16" y="89" width="82" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Managed IPs" id="wHt-u2-7XG">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wl2-R8-KQO">
+ <rect key="frame" x="480" y="338" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="fkd-z8-2sv">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AiO-ZR-9A3">
+ <rect key="frame" x="-3" y="13" width="134" height="32"/>
+ <buttonCell key="cell" type="push" title="Delete Network" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7Dg-VG-hjC">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="deleteNetwork:" target="rmb-il-W5I" id="gBI-fk-OoF"/>
+ </connections>
+ </button>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6rE-7R-shM">
+ <rect key="frame" x="1" y="189" width="97" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Allow Managed" id="AOv-4I-rQj">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dke-gy-mG0">
+ <rect key="frame" x="14" y="139" width="84" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Allow Default" id="SIi-Uu-Uzw">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="ulU-Mk-uV9">
+ <rect key="frame" x="504" y="188" width="22" height="18"/>
+ <buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Zhe-BT-cXO">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onAllowStatusChanged:" target="rmb-il-W5I" id="FW8-5N-vt1"/>
+ </connections>
+ </button>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="8CS-6g-NFI">
+ <rect key="frame" x="504" y="139" width="22" height="18"/>
+ <buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="S2d-lU-JhT">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onAllowStatusChanged:" target="rmb-il-W5I" id="ahh-N8-nhW"/>
+ </connections>
+ </button>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="URn-qw-7jG">
+ <rect key="frame" x="504" y="163" width="22" height="18"/>
+ <buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="yBQ-2g-3t5">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onAllowStatusChanged:" target="rmb-il-W5I" id="XGy-pE-Dzf"/>
+ </connections>
+ </button>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jdz-W3-UwS">
+ <rect key="frame" x="19" y="164" width="79" height="17"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Allow Global" id="PLj-4F-ROh">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dou-E5-8PF">
+ <rect key="frame" x="480" y="313" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="3Dq-d7-Gt3">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oD0-Qh-7L4">
+ <rect key="frame" x="480" y="288" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="35o-HJ-KYh">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="98C-J0-Y03">
+ <rect key="frame" x="480" y="263" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="63c-hy-MHZ">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="la4-uE-ffj">
+ <rect key="frame" x="480" y="238" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="rtd-6E-MsN">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="qHh-oP-Tdb">
+ <rect key="frame" x="480" y="213" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="Hex-iJ-2by">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="C0q-lJ-5dX">
+ <rect key="frame" x="480" y="113" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Label" id="5Ms-FS-k0W">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="GEJ-6D-gWU">
+ <rect key="frame" x="102" y="86" width="424" height="19"/>
+ <textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="right" title="Multiline Label" id="A3M-ZZ-6h7">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lO9-Jg-9f8">
+ <rect key="frame" x="1" y="364" width="44" height="19"/>
+ <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Label" id="p7O-rs-RvR">
+ <font key="font" size="13" name="AndaleMono"/>
+ <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ </textField>
+ <button translatesAutoresizingMaskIntoConstraints="NO" id="KRe-B1-51k">
+ <rect key="frame" x="437" y="22" width="89" height="18"/>
+ <buttonCell key="cell" type="check" title="Connected" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="DWJ-ZT-UIa">
+ <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="onConnectCheckStateChanged:" target="rmb-il-W5I" id="Ov2-bq-67i"/>
+ </connections>
+ </button>
+ </subviews>
+ <constraints>
+ <constraint firstItem="LIA-GW-caM" firstAttribute="trailing" secondItem="EWq-81-Y6j" secondAttribute="trailing" id="0vU-gw-esB"/>
+ <constraint firstItem="FeX-UV-AFG" firstAttribute="trailing" secondItem="LIA-GW-caM" secondAttribute="trailing" id="1xy-N5-B6U"/>
+ <constraint firstItem="FeX-UV-AFG" firstAttribute="top" secondItem="UAC-7B-K8Y" secondAttribute="bottom" constant="8" id="2Vh-D6-T45"/>
+ <constraint firstItem="qHh-oP-Tdb" firstAttribute="centerY" secondItem="EKG-la-snQ" secondAttribute="centerY" id="4xy-tk-eA7"/>
+ <constraint firstItem="LIA-GW-caM" firstAttribute="top" secondItem="FeX-UV-AFG" secondAttribute="bottom" constant="8" id="5Vd-2M-2yv"/>
+ <constraint firstItem="dou-E5-8PF" firstAttribute="centerY" secondItem="FeX-UV-AFG" secondAttribute="centerY" id="7iQ-Vx-9AJ"/>
+ <constraint firstAttribute="trailing" secondItem="wl2-R8-KQO" secondAttribute="trailing" constant="5" id="CaI-uw-8uL"/>
+ <constraint firstItem="Mi3-aV-yWg" firstAttribute="top" secondItem="EWq-81-Y6j" secondAttribute="bottom" constant="8" id="Cen-A6-Hdr"/>
+ <constraint firstItem="URn-qw-7jG" firstAttribute="centerY" secondItem="jdz-W3-UwS" secondAttribute="centerY" id="DEW-3F-MXB"/>
+ <constraint firstAttribute="trailing" secondItem="GEJ-6D-gWU" secondAttribute="trailing" constant="3" id="FC0-J7-2QF"/>
+ <constraint firstItem="EWq-81-Y6j" firstAttribute="trailing" secondItem="Mi3-aV-yWg" secondAttribute="trailing" id="FDe-x8-ERi"/>
+ <constraint firstAttribute="trailing" secondItem="qHh-oP-Tdb" secondAttribute="trailing" constant="5" id="Fcy-KL-LsD"/>
+ <constraint firstAttribute="trailing" secondItem="C0q-lJ-5dX" secondAttribute="trailing" constant="5" id="I3g-Oy-geM"/>
+ <constraint firstItem="EUT-1A-lgY" firstAttribute="top" secondItem="rmb-il-W5I" secondAttribute="top" constant="3" id="II4-Ta-EeQ"/>
+ <constraint firstItem="98C-J0-Y03" firstAttribute="centerY" secondItem="EWq-81-Y6j" secondAttribute="centerY" id="Ibd-3u-9fm"/>
+ <constraint firstAttribute="trailing" secondItem="8CS-6g-NFI" secondAttribute="trailing" constant="3" id="Jqv-Kb-lOT"/>
+ <constraint firstAttribute="trailing" secondItem="dou-E5-8PF" secondAttribute="trailing" constant="5" id="LPR-aD-BAP"/>
+ <constraint firstItem="jdz-W3-UwS" firstAttribute="top" secondItem="6rE-7R-shM" secondAttribute="bottom" constant="8" id="Lit-Zb-6pq"/>
+ <constraint firstItem="UAC-7B-K8Y" firstAttribute="top" secondItem="lO9-Jg-9f8" secondAttribute="bottom" constant="8" id="Mn0-LO-gSb"/>
+ <constraint firstItem="la4-uE-ffj" firstAttribute="centerY" secondItem="Mi3-aV-yWg" secondAttribute="centerY" id="Nhp-8t-OoR"/>
+ <constraint firstItem="lO9-Jg-9f8" firstAttribute="leading" secondItem="rmb-il-W5I" secondAttribute="leading" constant="3" id="Rzw-sT-s3c"/>
+ <constraint firstAttribute="trailing" secondItem="la4-uE-ffj" secondAttribute="trailing" constant="5" id="SQo-oO-lbs"/>
+ <constraint firstItem="AiO-ZR-9A3" firstAttribute="top" secondItem="6Af-7O-CaP" secondAttribute="bottom" constant="48" id="SbW-ma-xhO"/>
+ <constraint firstItem="wl2-R8-KQO" firstAttribute="centerY" secondItem="UAC-7B-K8Y" secondAttribute="centerY" id="TIS-fV-H3y"/>
+ <constraint firstItem="C0q-lJ-5dX" firstAttribute="centerY" secondItem="syI-eg-Zka" secondAttribute="centerY" id="TXe-Dr-AUr"/>
+ <constraint firstItem="lO9-Jg-9f8" firstAttribute="top" secondItem="rmb-il-W5I" secondAttribute="top" constant="3" id="TqS-9j-AZ2"/>
+ <constraint firstAttribute="trailing" secondItem="oD0-Qh-7L4" secondAttribute="trailing" constant="5" id="U1G-04-Zjb"/>
+ <constraint firstItem="jdz-W3-UwS" firstAttribute="trailing" secondItem="6rE-7R-shM" secondAttribute="trailing" id="UWD-yZ-0gu"/>
+ <constraint firstItem="EWq-81-Y6j" firstAttribute="top" secondItem="LIA-GW-caM" secondAttribute="bottom" constant="8" id="WS4-Xr-Uvt"/>
+ <constraint firstItem="GEJ-6D-gWU" firstAttribute="top" secondItem="C0q-lJ-5dX" secondAttribute="bottom" constant="8" id="WhD-XW-q7s"/>
+ <constraint firstAttribute="trailing" secondItem="KRe-B1-51k" secondAttribute="trailing" constant="3" id="WsQ-kS-Luf"/>
+ <constraint firstItem="8CS-6g-NFI" firstAttribute="centerY" secondItem="dke-gy-mG0" secondAttribute="centerY" id="X1u-9J-gLq"/>
+ <constraint firstItem="EKG-la-snQ" firstAttribute="top" secondItem="Mi3-aV-yWg" secondAttribute="bottom" constant="8" id="XEZ-4d-Yl9"/>
+ <constraint firstItem="6Af-7O-CaP" firstAttribute="top" secondItem="syI-eg-Zka" secondAttribute="bottom" constant="8" id="Xi3-aV-ZPe"/>
+ <constraint firstAttribute="trailing" secondItem="EUT-1A-lgY" secondAttribute="trailing" constant="5" id="cDU-PD-iMu"/>
+ <constraint firstItem="6rE-7R-shM" firstAttribute="leading" secondItem="rmb-il-W5I" secondAttribute="leading" constant="3" id="cIK-gU-COR"/>
+ <constraint firstItem="6Af-7O-CaP" firstAttribute="trailing" secondItem="syI-eg-Zka" secondAttribute="trailing" id="gP2-re-FqN"/>
+ <constraint firstItem="syI-eg-Zka" firstAttribute="trailing" secondItem="dke-gy-mG0" secondAttribute="trailing" id="gdf-bR-0Qk"/>
+ <constraint firstAttribute="trailing" secondItem="98C-J0-Y03" secondAttribute="trailing" constant="5" id="ghX-kA-tEa"/>
+ <constraint firstItem="UAC-7B-K8Y" firstAttribute="trailing" secondItem="FeX-UV-AFG" secondAttribute="trailing" id="hOJ-rF-Xn8"/>
+ <constraint firstItem="GEJ-6D-gWU" firstAttribute="leading" secondItem="6Af-7O-CaP" secondAttribute="trailing" constant="8" id="iBi-dY-iZi"/>
+ <constraint firstItem="KRe-B1-51k" firstAttribute="baseline" secondItem="AiO-ZR-9A3" secondAttribute="baseline" id="jBe-rC-QAF"/>
+ <constraint firstItem="ulU-Mk-uV9" firstAttribute="centerY" secondItem="6rE-7R-shM" secondAttribute="centerY" id="l5g-Dr-Bht"/>
+ <constraint firstItem="6rE-7R-shM" firstAttribute="top" secondItem="EKG-la-snQ" secondAttribute="bottom" constant="8" id="lvp-Dn-1m0"/>
+ <constraint firstItem="syI-eg-Zka" firstAttribute="top" secondItem="dke-gy-mG0" secondAttribute="bottom" constant="8" id="pSb-5E-l16"/>
+ <constraint firstAttribute="trailing" secondItem="KRe-B1-51k" secondAttribute="trailing" constant="3" id="pZ7-4k-n3s"/>
+ <constraint firstItem="AiO-ZR-9A3" firstAttribute="leading" secondItem="rmb-il-W5I" secondAttribute="leading" constant="3" id="pau-hs-aaF"/>
+ <constraint firstItem="dke-gy-mG0" firstAttribute="trailing" secondItem="jdz-W3-UwS" secondAttribute="trailing" id="qd7-UW-evp"/>
+ <constraint firstItem="dke-gy-mG0" firstAttribute="top" secondItem="jdz-W3-UwS" secondAttribute="bottom" constant="8" id="sHm-Jz-ko8"/>
+ <constraint firstItem="EKG-la-snQ" firstAttribute="trailing" secondItem="6rE-7R-shM" secondAttribute="trailing" id="tNY-Wb-Aig"/>
+ <constraint firstAttribute="trailing" secondItem="URn-qw-7jG" secondAttribute="trailing" constant="3" id="tb8-cb-vhf"/>
+ <constraint firstAttribute="trailing" secondItem="ulU-Mk-uV9" secondAttribute="trailing" constant="3" id="vCN-jZ-bH7"/>
+ <constraint firstItem="oD0-Qh-7L4" firstAttribute="centerY" secondItem="LIA-GW-caM" secondAttribute="centerY" id="vol-qx-psh"/>
+ <constraint firstItem="Mi3-aV-yWg" firstAttribute="trailing" secondItem="EKG-la-snQ" secondAttribute="trailing" id="yWV-G4-bDS"/>
+ </constraints>
+ <connections>
+ <outlet property="addressesField" destination="GEJ-6D-gWU" id="MDn-cx-TNx"/>
+ <outlet property="allowDefault" destination="8CS-6g-NFI" id="YLt-Gp-HNa"/>
+ <outlet property="allowGlobal" destination="URn-qw-7jG" id="VUy-Mh-nK4"/>
+ <outlet property="allowManaged" destination="ulU-Mk-uV9" id="poZ-5E-m0m"/>
+ <outlet property="bridgingField" destination="qHh-oP-Tdb" id="eSw-Dw-QoN"/>
+ <outlet property="broadcastField" destination="la4-uE-ffj" id="A89-yC-MhR"/>
+ <outlet property="connectedCheckbox" destination="KRe-B1-51k" id="Sz5-Fq-jWL"/>
+ <outlet property="deleteButton" destination="AiO-ZR-9A3" id="rYp-Vt-gHl"/>
+ <outlet property="deviceField" destination="C0q-lJ-5dX" id="bc1-Fh-zT8"/>
+ <outlet property="macField" destination="oD0-Qh-7L4" id="4TS-1J-vRK"/>
+ <outlet property="mtuField" destination="98C-J0-Y03" id="KTM-C4-bEQ"/>
+ <outlet property="networkIdField" destination="lO9-Jg-9f8" id="UlQ-yf-ZYk"/>
+ <outlet property="networkNameField" destination="EUT-1A-lgY" id="Kwa-OJ-LTB"/>
+ <outlet property="statusField" destination="wl2-R8-KQO" id="fH2-Wl-DtW"/>
+ <outlet property="typeField" destination="dou-E5-8PF" id="v6U-sw-kzs"/>
+ </connections>
+ </tableCellView>
+ </prototypeCellViews>
+ </tableColumn>
+ </tableColumns>
+ </tableView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="ySn-XX-fde">
+ <rect key="frame" x="1" y="256" width="478" height="15"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="be6-Ft-7fo">
+ <rect key="frame" x="224" y="17" width="15" height="102"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ </scrollView>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="trailing" secondItem="miH-DQ-i4L" secondAttribute="trailing" constant="20" id="453-vf-nVL"/>
+ <constraint firstItem="miH-DQ-i4L" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" constant="20" id="IS6-3U-KHq"/>
+ <constraint firstAttribute="bottom" secondItem="miH-DQ-i4L" secondAttribute="bottom" constant="20" id="YIB-OP-keG"/>
+ <constraint firstItem="miH-DQ-i4L" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" constant="20" id="ceZ-7I-uoc"/>
+ </constraints>
+ <point key="canvasLocation" x="220" y="323.5"/>
+ </customView>
+ </objects>
+</document>
diff --git a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/ZeroTierIcon.icns b/macui/ZeroTier One/ZeroTierIcon.icns
index 17e60d58..17e60d58 100644
--- a/ext/installfiles/mac/mac-ui-macgap1-wrapper/bin/ZeroTier One.app/Contents/Resources/ZeroTierIcon.icns
+++ b/macui/ZeroTier One/ZeroTierIcon.icns
Binary files differ
diff --git a/macui/ZeroTier One/about.html b/macui/ZeroTier One/about.html
new file mode 100644
index 00000000..09f6eb36
--- /dev/null
+++ b/macui/ZeroTier One/about.html
@@ -0,0 +1,58 @@
+<html>
+<head>
+<style type="text/css">
+html,body {
+ background: #ffffff;
+ margin: 0;
+ padding: 0;
+ font-family: sans-serif;
+ font-size: 12pt;
+ height: 100%;
+ width: 100%;
+}
+div.icon {
+ background: #ffb354;
+ color: #000000;
+ font-size: 100pt;
+ border-radius: 2.5rem;
+ display: inline-block;
+ width: 1.3em;
+ height: 1.3em;
+ padding: 0;
+ margin: 0;
+ line-height: 1.4em;
+ vertical-align: middle;
+ text-align: center;
+}
+div.icon_container {
+ font-weight: bold;
+}
+a,p,h1,h2,h3,h4,span,div,strong,center,lead,nav,ol,ul,li,img,button,input,textarea,form {
+ font-family: sans-serif;
+}
+.code {
+ font-family: "Menlo",monospace !important;
+}
+a:link {
+ text-decoration: none;
+}
+</style>
+</head>
+<body>
+ <center><div class="icon_container"><div class="icon">&#x23c1;</div></div></center>
+ <div>
+ <h2>Welcome to ZeroTier</h2>
+ <h4>Getting Started</h4>
+ <p>Networks are identified by 16-digit network IDs. If someone invited you to join theirs you probably received one. If not you can create your own at <a href="https://my.zerotier.com/">my.zerotier.com</a> or by running <a href="https://github.com/zerotier/ZeroTierOne/tree/master/controller">running your own network controller</a>.
+ <p>Your computer is identified by a 10-digit ZeroTier address. You can find it at the top of the ZeroTier app's pull-down menu or by typing <span class="code">"sudo zerotier-cli info"</span> in a terminal window. This number is unique to your system and is how network administrators can recognize you. If someone invited you to a network, give them this ID so they can authorize you to join.</p>
+ <h4>Starting, Stopping, and Uninstalling</h4>
+ <p>The ZeroTier service is separate from the UI app and starts on system boot. The app can be started on login or only when needed. To stop the ZeroTier service use:<br><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="code">sudo launchctl unload /Library/LaunchDaemons/com.zerotier.one.plist</span><br><br>
+ Replace "unload" with "load" to start it again.</p>
+ <p>ZeroTier can be uninstalled with:<br><br>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="code">sudo '/Library/Application Support/ZeroTier/One/uninstall.sh'
+ </p>
+ <p><b>For more information, visit <a href="http://www.zerotier.com">zerotier.com</a>.</b></p>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/macui/ZeroTier One/main.m b/macui/ZeroTier One/main.m
new file mode 100644
index 00000000..108a6bd1
--- /dev/null
+++ b/macui/ZeroTier One/main.m
@@ -0,0 +1,23 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, const char * argv[]) {
+ return NSApplicationMain(argc, argv);
+}
diff --git a/make-bsd.mk b/make-bsd.mk
new file mode 100644
index 00000000..713cafc0
--- /dev/null
+++ b/make-bsd.mk
@@ -0,0 +1,173 @@
+# This requires GNU make, which is typically "gmake" on BSD systems
+
+INCLUDES=
+DEFS=
+LIBS=
+
+include objects.mk
+ONE_OBJS+=osdep/BSDEthernetTap.o ext/http-parser/http_parser.o
+
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+ SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+endif
+# "make debug" is a shortcut for this
+ifeq ($(ZT_DEBUG),1)
+ CFLAGS+=-Wall -Werror -g -pthread $(INCLUDES) $(DEFS)
+ LDFLAGS+=
+ STRIP=echo
+ ZT_TRACE=1
+ # The following line enables optimization for the crypto code, since
+ # C25519 in particular is almost UNUSABLE in heavy testing without it.
+node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
+else
+ CFLAGS?=-O3 -fstack-protector
+ CFLAGS+=-Wall -fPIE -fvisibility=hidden -fstack-protector -pthread $(INCLUDES) -DNDEBUG $(DEFS)
+ LDFLAGS+=-pie -Wl,-z,relro,-z,now
+ STRIP=strip --strip-all
+endif
+
+ifeq ($(ZT_TRACE),1)
+ DEFS+=-DZT_TRACE
+endif
+
+# Determine system build architecture from compiler target
+CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
+ZT_ARCHITECTURE=999
+ifeq ($(CC_MACH),x86_64)
+ ZT_ARCHITECTURE=2
+ ZT_USE_X64_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),amd64)
+ ZT_ARCHITECTURE=2
+ ZT_USE_X64_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),i386)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i686)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),arm)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),armel)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),armhf)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),armv6)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),armv6zk)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),armv6kz)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),armv7)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),arm64)
+ ZT_ARCHITECTURE=4
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),aarch64)
+ ZT_ARCHITECTURE=4
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mipsel)
+ ZT_ARCHITECTURE=5
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mips)
+ ZT_ARCHITECTURE=5
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mips64)
+ ZT_ARCHITECTURE=6
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mips64el)
+ ZT_ARCHITECTURE=6
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+
+# Fail if system architecture could not be determined
+ifeq ($(ZT_ARCHITECTURE),999)
+ERR=$(error FATAL: architecture could not be determined from $(CC) -dumpmachine: $CC_MACH)
+.PHONY: err
+err: ; $(ERR)
+endif
+
+# Build faster crypto on some targets
+ifeq ($(ZT_USE_X64_ASM_SALSA2012),1)
+ override DEFS+=-DZT_USE_X64_ASM_SALSA2012
+ override CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o
+endif
+ifeq ($(ZT_USE_ARM32_NEON_ASM_SALSA2012),1)
+ override DEFS+=-DZT_USE_ARM32_NEON_ASM_SALSA2012
+ override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
+endif
+
+override DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
+
+CXXFLAGS+=$(CFLAGS) -fno-rtti -std=c++11 #-D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_C99_MATH -D_GLIBCXX_USE_C99_MATH_TR1
+
+all: one
+
+one: $(CORE_OBJS) $(ONE_OBJS) one.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
+ $(STRIP) zerotier-one
+ ln -sf zerotier-one zerotier-idtool
+ ln -sf zerotier-one zerotier-cli
+
+zerotier-one: one
+
+zerotier-idtool: one
+
+zerotier-cli: one
+
+libzerotiercore.a: $(CORE_OBJS)
+ ar rcs libzerotiercore.a $(CORE_OBJS)
+ ranlib libzerotiercore.a
+
+core: libzerotiercore.a
+
+selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LIBS)
+ $(STRIP) zerotier-selftest
+
+zerotier-selftest: selftest
+
+clean:
+ rm -rf *.a *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli $(ONE_OBJS) $(CORE_OBJS)
+
+debug: FORCE
+ gmake -j 4 ZT_DEBUG=1
+
+install: one
+ rm -f /usr/local/sbin/zerotier-one
+ cp zerotier-one /usr/local/sbin
+ ln -sf /usr/local/sbin/zerotier-one /usr/local/sbin/zerotier-cli
+ ln -sf /usr/local/sbin/zerotier-one /usr/local/bin/zerotier-idtool
+
+uninstall: FORCE
+ rm -rf /usr/local/sbin/zerotier-one /usr/local/sbin/zerotier-cli /usr/local/bin/zerotier-idtool /var/db/zerotier-one/zerotier-one.port /var/db/zerotier-one/zerotier-one.pid /var/db/zerotier-one/iddb.d
+
+FORCE:
diff --git a/make-freebsd.mk b/make-freebsd.mk
deleted file mode 100644
index e7bd9fd2..00000000
--- a/make-freebsd.mk
+++ /dev/null
@@ -1,65 +0,0 @@
-CC=cc
-CXX=c++
-
-INCLUDES=
-DEFS=
-LIBS=
-
-include objects.mk
-OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/json-parser/json.o ext/http-parser/http_parser.o
-
-# "make official" is a shortcut for this
-ifeq ($(ZT_OFFICIAL_RELEASE),1)
- DEFS+=-DZT_OFFICIAL_RELEASE
-endif
-
-# Build with ZT_ENABLE_CLUSTER=1 to build with cluster support
-ifeq ($(ZT_ENABLE_CLUSTER),1)
- DEFS+=-DZT_ENABLE_CLUSTER
-endif
-
-# "make debug" is a shortcut for this
-ifeq ($(ZT_DEBUG),1)
- DEFS+=-DZT_TRACE
- CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
- LDFLAGS+=
- STRIP=echo
- # The following line enables optimization for the crypto code, since
- # C25519 in particular is almost UNUSABLE in heavy testing without it.
-ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
-else
- CFLAGS?=-O3 -fstack-protector
- CFLAGS+=-Wall -fPIE -fvisibility=hidden -fstack-protector -pthread $(INCLUDES) -DNDEBUG $(DEFS)
- LDFLAGS+=-pie -Wl,-z,relro,-z,now
- STRIP=strip --strip-all
-endif
-
-CXXFLAGS+=$(CFLAGS) -fno-rtti
-
-all: one
-
-one: $(OBJS) service/OneService.o one.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
- $(STRIP) zerotier-one
- ln -sf zerotier-one zerotier-idtool
- ln -sf zerotier-one zerotier-cli
-
-selftest: $(OBJS) selftest.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
- $(STRIP) zerotier-selftest
-
-# No installer on FreeBSD yet
-#installer: one FORCE
-# ./buildinstaller.sh
-
-clean:
- rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-*
-
-debug: FORCE
- make -j 4 ZT_DEBUG=1
-
-#official: FORCE
-# make -j 4 ZT_OFFICIAL_RELEASE=1
-# ./buildinstaller.sh
-
-FORCE:
diff --git a/make-linux.mk b/make-linux.mk
index acc22a63..2e6a8632 100644
--- a/make-linux.mk
+++ b/make-linux.mk
@@ -1,154 +1,303 @@
-#
-# Makefile for ZeroTier One on Linux
-#
-# This is confirmed to work on distributions newer than CentOS 6 (the
-# one used for reference builds) and on 32 and 64 bit x86 and ARM
-# machines. It should also work on other 'normal' machines and recent
-# distributions. Editing might be required for tiny devices or weird
-# distros.
-#
-# Targets
-# one: zerotier-one and symlinks (cli and idtool)
-# manpages: builds manpages, requires 'ronn' or nodeJS (will use either)
-# all: builds 'one' and 'manpages'
-# selftest: zerotier-selftest
-# debug: builds 'one' and 'selftest' with tracing and debug flags
-# clean: removes all built files, objects, other trash
-# distclean: removes a few other things that might be present
-# debian: build DEB packages; deb dev tools must be present
-# redhat: build RPM packages; rpm dev tools must be present
-#
-
# Automagically pick clang or gcc, with preference for clang
# This is only done if we have not overridden these with an environment or CLI variable
ifeq ($(origin CC),default)
- CC=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
+ CC:=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
+ CC:=$(shell if [ -e /opt/intel/bin/icc ]; then echo /opt/intel/bin/icc -ipo -ansi-alias; else echo $(CC); fi)
endif
ifeq ($(origin CXX),default)
- CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
+ CXX:=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
+ CXX:=$(shell if [ -e /opt/intel/bin/icc ]; then echo /opt/intel/bin/icc -ipo -ansi-alias; else echo $(CXX); fi)
endif
-#UNAME_M=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
-
INCLUDES?=
-DEFS?=-D_FORTIFY_SOURCE=2
+DEFS?=
LDLIBS?=
DESTDIR?=
+
include objects.mk
+ONE_OBJS+=osdep/LinuxEthernetTap.o
-# On Linux we auto-detect the presence of some libraries and if present we
-# link against the system version. This works with our package build images.
-ifeq ($(wildcard /usr/include/lz4.h),)
- OBJS+=ext/lz4/lz4.o
-else
- LDLIBS+=-llz4
- DEFS+=-DZT_USE_SYSTEM_LZ4
-endif
-ifeq ($(wildcard /usr/include/http_parser.h),)
- OBJS+=ext/http-parser/http_parser.o
+# Auto-detect miniupnpc and nat-pmp as well and use system libs if present,
+# otherwise build into binary as done on Mac and Windows.
+ONE_OBJS+=osdep/PortMapper.o
+override DEFS+=-DZT_USE_MINIUPNPC
+MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2..*"' /usr/include/miniupnpc/miniupnpc.h && echo 1)
+#MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
+ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
+ override DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
+ LDLIBS+=-lminiupnpc
else
- LDLIBS+=-lhttp_parser
- DEFS+=-DZT_USE_SYSTEM_HTTP_PARSER
+ override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
+ ONE_OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
endif
-ifeq ($(wildcard /usr/include/json-parser/json.h),)
- OBJS+=ext/json-parser/json.o
+ifeq ($(wildcard /usr/include/natpmp.h),)
+ ONE_OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
else
- LDLIBS+=-ljsonparser
- DEFS+=-DZT_USE_SYSTEM_JSON_PARSER
+ LDLIBS+=-lnatpmp
+ override DEFS+=-DZT_USE_SYSTEM_NATPMP
endif
-ifeq ($(ZT_USE_MINIUPNPC),1)
- OBJS+=osdep/PortMapper.o
-
- DEFS+=-DZT_USE_MINIUPNPC
-
- # Auto-detect libminiupnpc at least v2.0
- MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
- ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
- DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
- LDLIBS+=-lminiupnpc
- else
- DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
- OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
- endif
+# Use bundled http-parser since distribution versions are NOT API-stable or compatible!
+# Trying to use dynamically linked libhttp-parser causes tons of compatibility problems.
+ONE_OBJS+=ext/http-parser/http_parser.o
- # Auto-detect libnatpmp
- ifeq ($(wildcard /usr/include/natpmp.h),)
- OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
- else
- LDLIBS+=-lnatpmp
- DEFS+=-DZT_USE_SYSTEM_NATPMP
- endif
+ifeq ($(ZT_SYNOLOGY), 1)
+ override DEFS+=-D__SYNOLOGY__
endif
-ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
- DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER
- LDLIBS+=-L/usr/local/lib -lsqlite3
- OBJS+=controller/SqliteNetworkController.o
+ifeq ($(ZT_QNAP), 1)
+ override DEFS+=-D__QNAP__
endif
-ifeq ($(ZT_ENABLE_CLUSTER),1)
- DEFS+=-DZT_ENABLE_CLUSTER
+ifeq ($(ZT_TRACE),1)
+ override DEFS+=-DZT_TRACE
endif
-ifeq ($(ZT_TRACE),1)
- DEFS+=-DZT_TRACE
+ifeq ($(ZT_RULES_ENGINE_DEBUGGING),1)
+ override DEFS+=-DZT_RULES_ENGINE_DEBUGGING
endif
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+ SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+endif
ifeq ($(ZT_DEBUG),1)
- DEFS+=-DZT_TRACE
- override CFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
- override CXXFLAGS+=-Wall -g -O -pthread $(INCLUDES) $(DEFS)
- LDFLAGS=
+ override CFLAGS+=-Wall -Wno-deprecated -Werror -g -pthread $(INCLUDES) $(DEFS)
+ override CXXFLAGS+=-Wall -Wno-deprecated -Werror -g -std=c++11 -pthread $(INCLUDES) $(DEFS)
+ ZT_TRACE=1
STRIP?=echo
# The following line enables optimization for the crypto code, since
# C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box!
-ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
+node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CXXFLAGS=-Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
else
- CFLAGS?=-O3 -fstack-protector-strong
- override CFLAGS+=-Wall -fPIE -pthread $(INCLUDES) -DNDEBUG $(DEFS)
- CXXFLAGS?=-O3 -fstack-protector-strong
- override CXXFLAGS+=-Wall -Wno-unused-result -Wreorder -fPIE -fno-rtti -pthread $(INCLUDES) -DNDEBUG $(DEFS)
+ CFLAGS?=-O3 -fstack-protector -fPIE
+ override CFLAGS+=-Wall -Wno-deprecated -pthread $(INCLUDES) -DNDEBUG $(DEFS)
+ CXXFLAGS?=-O3 -fstack-protector -fPIE
+ override CXXFLAGS+=-Wall -Wno-deprecated -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
LDFLAGS=-pie -Wl,-z,relro,-z,now
STRIP?=strip
STRIP+=--strip-all
endif
+ifeq ($(ZT_TRACE),1)
+ override DEFS+=-DZT_TRACE
+endif
+
+ifeq ($(ZT_USE_TEST_TAP),1)
+ override DEFS+=-DZT_USE_TEST_TAP
+endif
+
# Uncomment for gprof profile build
#CFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
#CXXFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
#LDFLAGS=
#STRIP=echo
-all: one manpages
+# Determine system build architecture from compiler target
+CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
+ZT_ARCHITECTURE=999
+ifeq ($(CC_MACH),x86_64)
+ ZT_ARCHITECTURE=2
+ ZT_USE_X64_ASM_SALSA=1
+ ZT_USE_X64_ASM_ED25519=1
+endif
+ifeq ($(CC_MACH),amd64)
+ ZT_ARCHITECTURE=2
+ ZT_USE_X64_ASM_SALSA=1
+ ZT_USE_X64_ASM_ED25519=1
+endif
+ifeq ($(CC_MACH),powerpc64le)
+ ZT_ARCHITECTURE=8
+endif
+ifeq ($(CC_MACH),ppc64le)
+ ZT_ARCHITECTURE=8
+endif
+ifeq ($(CC_MACH),ppc64el)
+ ZT_ARCHITECTURE=8
+endif
+ifeq ($(CC_MACH),i386)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i486)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i586)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i686)
+ ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),arm)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armel)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armhf)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv6)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv6zk)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv6kz)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv7)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv7l)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=1
+endif
+ifeq ($(CC_MACH),armv7l)
+ ZT_ARCHITECTURE=3
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+ ZT_USE_ARM32_NEON_ASM_SALSA2012=1
+endif
+ifeq ($(CC_MACH),arm64)
+ ZT_ARCHITECTURE=4
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),aarch64)
+ ZT_ARCHITECTURE=4
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mipsel)
+ ZT_ARCHITECTURE=5
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mips)
+ ZT_ARCHITECTURE=5
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mips64)
+ ZT_ARCHITECTURE=6
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),mips64el)
+ ZT_ARCHITECTURE=6
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+ifeq ($(CC_MACH),powerpc64le)
+ ZT_ARCHITECTURE=7
+ override DEFS+=-DZT_NO_TYPE_PUNNING
+endif
+
+# Fail if system architecture could not be determined
+ifeq ($(ZT_ARCHITECTURE),999)
+ERR=$(error FATAL: architecture could not be determined from $(CC) -dumpmachine: $CC_MACH)
+.PHONY: err
+err: ; $(ERR)
+endif
+
+# Disable software updates by default on Linux since that is normally done with package management
+override DEFS+=-DZT_BUILD_PLATFORM=1 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
+
+# This forces libstdc++ not to include these abominations, especially mt and pool
+override DEFS+=-D_MT_ALLOCATOR_H -D_POOL_ALLOCATOR_H -D_EXTPTR_ALLOCATOR_H -D_DEBUG_ALLOCATOR_H
+
+# Static builds, which are currently done for a number of Linux targets
+ifeq ($(ZT_STATIC),1)
+ override LDFLAGS+=-static
+endif
+
+# For building an official semi-static binary on CentOS 7
+ifeq ($(ZT_OFFICIAL),1)
+ CORE_OBJS+=ext/misc/linux-old-glibc-compat.o
+ override LDFLAGS+=-Wl,--wrap=memcpy -static-libstdc++
+endif
-one: $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o osdep/LinuxEthernetTap.o $(LDLIBS)
+# ARM32 hell -- use conservative CFLAGS
+ifeq ($(ZT_ARCHITECTURE),3)
+ ifeq ($(shell if [ -e /usr/bin/dpkg ]; then dpkg --print-architecture; fi),armel)
+ override CFLAGS+=-march=armv5 -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
+ override CXXFLAGS+=-march=armv5 -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
+ ZT_USE_ARM32_NEON_ASM_CRYPTO=0
+ else
+ override CFLAGS+=-march=armv5 -mno-unaligned-access -marm
+ override CXXFLAGS+=-march=armv5 -mno-unaligned-access -marm
+ endif
+endif
+
+# Build faster crypto on some targets
+ifeq ($(ZT_USE_X64_ASM_SALSA),1)
+ override DEFS+=-DZT_USE_X64_ASM_SALSA2012
+ override CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o
+endif
+ifeq ($(ZT_USE_X64_ASM_ED25519),1)
+ override DEFS+=-DZT_USE_FAST_X64_ED25519
+ override CORE_OBJS+=ext/ed25519-amd64-asm/choose_t.o ext/ed25519-amd64-asm/consts.o ext/ed25519-amd64-asm/fe25519_add.o ext/ed25519-amd64-asm/fe25519_freeze.o ext/ed25519-amd64-asm/fe25519_mul.o ext/ed25519-amd64-asm/fe25519_square.o ext/ed25519-amd64-asm/fe25519_sub.o ext/ed25519-amd64-asm/ge25519_add_p1p1.o ext/ed25519-amd64-asm/ge25519_dbl_p1p1.o ext/ed25519-amd64-asm/ge25519_nielsadd2.o ext/ed25519-amd64-asm/ge25519_nielsadd_p1p1.o ext/ed25519-amd64-asm/ge25519_p1p1_to_p2.o ext/ed25519-amd64-asm/ge25519_p1p1_to_p3.o ext/ed25519-amd64-asm/ge25519_pnielsadd_p1p1.o ext/ed25519-amd64-asm/heap_rootreplaced.o ext/ed25519-amd64-asm/heap_rootreplaced_1limb.o ext/ed25519-amd64-asm/heap_rootreplaced_2limbs.o ext/ed25519-amd64-asm/heap_rootreplaced_3limbs.o ext/ed25519-amd64-asm/sc25519_add.o ext/ed25519-amd64-asm/sc25519_barrett.o ext/ed25519-amd64-asm/sc25519_lt.o ext/ed25519-amd64-asm/sc25519_sub_nored.o ext/ed25519-amd64-asm/ull4_mul.o ext/ed25519-amd64-asm/fe25519_getparity.o ext/ed25519-amd64-asm/fe25519_invert.o ext/ed25519-amd64-asm/fe25519_iseq.o ext/ed25519-amd64-asm/fe25519_iszero.o ext/ed25519-amd64-asm/fe25519_neg.o ext/ed25519-amd64-asm/fe25519_pack.o ext/ed25519-amd64-asm/fe25519_pow2523.o ext/ed25519-amd64-asm/fe25519_setint.o ext/ed25519-amd64-asm/fe25519_unpack.o ext/ed25519-amd64-asm/ge25519_add.o ext/ed25519-amd64-asm/ge25519_base.o ext/ed25519-amd64-asm/ge25519_double.o ext/ed25519-amd64-asm/ge25519_double_scalarmult.o ext/ed25519-amd64-asm/ge25519_isneutral.o ext/ed25519-amd64-asm/ge25519_multi_scalarmult.o ext/ed25519-amd64-asm/ge25519_pack.o ext/ed25519-amd64-asm/ge25519_scalarmult_base.o ext/ed25519-amd64-asm/ge25519_unpackneg.o ext/ed25519-amd64-asm/hram.o ext/ed25519-amd64-asm/index_heap.o ext/ed25519-amd64-asm/sc25519_from32bytes.o ext/ed25519-amd64-asm/sc25519_from64bytes.o ext/ed25519-amd64-asm/sc25519_from_shortsc.o ext/ed25519-amd64-asm/sc25519_iszero.o ext/ed25519-amd64-asm/sc25519_mul.o ext/ed25519-amd64-asm/sc25519_mul_shortsc.o ext/ed25519-amd64-asm/sc25519_slide.o ext/ed25519-amd64-asm/sc25519_to32bytes.o ext/ed25519-amd64-asm/sc25519_window4.o ext/ed25519-amd64-asm/sign.o
+endif
+ifeq ($(ZT_USE_ARM32_NEON_ASM_CRYPTO),1)
+ override DEFS+=-DZT_USE_ARM32_NEON_ASM_SALSA2012
+ override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
+endif
+
+all: one
+
+one: $(CORE_OBJS) $(ONE_OBJS) one.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LDLIBS)
$(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
-selftest: $(OBJS) selftest.o
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LDLIBS)
+zerotier-one: one
+
+zerotier-idtool: one
+
+zerotier-cli: one
+
+libzerotiercore.a: FORCE
+ make CFLAGS="-O3 -fstack-protector -fPIC" CXXFLAGS="-O3 -std=c++11 -fstack-protector -fPIC" $(CORE_OBJS)
+ ar rcs libzerotiercore.a $(CORE_OBJS)
+ ranlib libzerotiercore.a
+
+core: libzerotiercore.a
+
+selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LDLIBS)
$(STRIP) zerotier-selftest
+zerotier-selftest: selftest
+
manpages: FORCE
cd doc ; ./build.sh
doc: manpages
clean: FORCE
- rm -rf *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend doc/*.1 doc/*.2 doc/*.8 debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one
+ rm -rf *.a *.so *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/miniupnpc/*.o ext/libnatpmp/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-cli zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm .depend debian/files debian/zerotier-one*.debhelper debian/zerotier-one.substvars debian/*.log debian/zerotier-one doc/node_modules ext/misc/*.o
distclean: clean
- rm -rf doc/node_modules
- find linux-build-farm -type f -name '*.deb' -print0 | xargs -0 rm -fv
- find linux-build-farm -type f -name '*.rpm' -print0 | xargs -0 rm -fv
- find linux-build-farm -type f -name 'zt1-src.tar.gz' | xargs rm -fv
realclean: distclean
+official: FORCE
+ make -j4 ZT_OFFICIAL=1 all
+
+central-controller: FORCE
+ cd ext/librethinkdbxx ; make
+ make -j4 LDLIBS="ext/librethinkdbxx/build/librethinkdb++.a" DEFS="-DZT_CONTROLLER_USE_RETHINKDB" ZT_OFFICIAL=1 ZT_USE_X64_ASM_ED25519=1 one
+
debug: FORCE
make ZT_DEBUG=1 one
make ZT_DEBUG=1 selftest
@@ -201,10 +350,10 @@ uninstall: FORCE
# These are just for convenience for building Linux packages
-debian: distclean
- debuild -I -i -us -uc
+debian: FORCE
+ debuild -I -i -us -uc -nc -b
-redhat: distclean
+redhat: FORCE
rpmbuild -ba zerotier-one.spec
FORCE:
diff --git a/make-mac.mk b/make-mac.mk
index e821c4cf..60aa465a 100644
--- a/make-mac.mk
+++ b/make-mac.mk
@@ -1,108 +1,130 @@
-ifeq ($(origin CC),default)
- CC=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
-endif
-ifeq ($(origin CXX),default)
- CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
-endif
-
+CC=clang
+CXX=clang++
INCLUDES=
DEFS=
LIBS=
-ARCH_FLAGS=-arch x86_64
-
-include objects.mk
-OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/json-parser/json.o ext/http-parser/http_parser.o
-
-# Disable codesign since open source users will not have ZeroTier's certs
+ARCH_FLAGS=
CODESIGN=echo
PRODUCTSIGN=echo
CODESIGN_APP_CERT=
CODESIGN_INSTALLER_CERT=
-# Build with libminiupnpc by default for Mac -- desktops/laptops almost always want this
-ZT_USE_MINIUPNPC?=1
+ZT_BUILD_PLATFORM=3
+ZT_BUILD_ARCHITECTURE=2
+ZT_VERSION_MAJOR=$(shell cat version.h | grep -F VERSION_MAJOR | cut -d ' ' -f 3)
+ZT_VERSION_MINOR=$(shell cat version.h | grep -F VERSION_MINOR | cut -d ' ' -f 3)
+ZT_VERSION_REV=$(shell cat version.h | grep -F VERSION_REVISION | cut -d ' ' -f 3)
+ZT_VERSION_BUILD=$(shell cat version.h | grep -F VERSION_BUILD | cut -d ' ' -f 3)
+
+DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_BUILD_ARCHITECTURE)
-# For internal use only -- signs everything with ZeroTier's developer cert
+include objects.mk
+ONE_OBJS+=osdep/OSXEthernetTap.o ext/http-parser/http_parser.o
+
+# Official releases are signed with our Apple cert and apply software updates by default
ifeq ($(ZT_OFFICIAL_RELEASE),1)
- DEFS+=-DZT_OFFICIAL_RELEASE -DZT_AUTO_UPDATE
+ DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"apply\""
ZT_USE_MINIUPNPC=1
CODESIGN=codesign
PRODUCTSIGN=productsign
- CODESIGN_APP_CERT="Developer ID Application: ZeroTier Networks LLC (8ZD9JUCZ4V)"
- CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier Networks LLC (8ZD9JUCZ4V)"
-endif
-
-ifeq ($(ZT_ENABLE_CLUSTER),1)
- DEFS+=-DZT_ENABLE_CLUSTER
+ CODESIGN_APP_CERT="Developer ID Application: ZeroTier, Inc (8ZD9JUCZ4V)"
+ CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier, Inc (8ZD9JUCZ4V)"
+else
+ DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="\"download\""
endif
-ifeq ($(ZT_AUTO_UPDATE),1)
- DEFS+=-DZT_AUTO_UPDATE
-endif
+# Use fast ASM Salsa20/12 for x64 processors
+DEFS+=-DZT_USE_X64_ASM_SALSA2012
+CORE_OBJS+=ext/x64-salsa2012-asm/salsa2012.o
-ifeq ($(ZT_USE_MINIUPNPC),1)
- DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
- OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
-endif
+# Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources
+DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
+ONE_OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
-ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
- DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER
- LIBS+=-L/usr/local/lib -lsqlite3
- OBJS+=controller/SqliteNetworkController.o
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+ SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
endif
-
# Debug mode -- dump trace output, build binary with -g
ifeq ($(ZT_DEBUG),1)
- DEFS+=-DZT_TRACE
- CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS)
+ ZT_TRACE=1
+ CFLAGS+=-Wall -Werror -g $(INCLUDES) $(DEFS)
STRIP=echo
# The following line enables optimization for the crypto code, since
# C25519 in particular is almost UNUSABLE in heavy testing without it.
-ext/lz4/lz4.o node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
+node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g $(INCLUDES) $(DEFS)
else
CFLAGS?=-Ofast -fstack-protector-strong
- CFLAGS+=$(ARCH_FLAGS) -Wall -flto -fPIE -pthread -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
+ CFLAGS+=$(ARCH_FLAGS) -Wall -Werror -flto -fPIE -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
STRIP=strip
endif
-CXXFLAGS=$(CFLAGS) -fno-rtti
+ifeq ($(ZT_TRACE),1)
+ DEFS+=-DZT_TRACE
+endif
-all: one
+CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++
-one: $(OBJS) service/OneService.o one.o
- $(CXX) $(CXXFLAGS) -o zerotier-one $(OBJS) service/OneService.o one.o $(LIBS)
+all: one macui
+
+ext/x64-salsa2012-asm/salsa2012.o:
+ $(CC) $(CFLAGS) -c ext/x64-salsa2012-asm/salsa2012.s -o ext/x64-salsa2012-asm/salsa2012.o
+
+one: $(CORE_OBJS) $(ONE_OBJS) one.o
+ $(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
$(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
$(CODESIGN) -f -s $(CODESIGN_APP_CERT) zerotier-one
- $(CODESIGN) -vvv zerotier-one
-cli: FORCE
- $(CXX) -Os -mmacosx-version-min=10.7 -std=c++11 -stdlib=libc++ -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl
- $(STRIP) zerotier
+zerotier-one: one
+
+zerotier-idtool: one
-selftest: $(OBJS) selftest.o
- $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
+zerotier-cli: one
+
+libzerotiercore.a: $(CORE_OBJS)
+ ar rcs libzerotiercore.a $(CORE_OBJS)
+ ranlib libzerotiercore.a
+
+core: libzerotiercore.a
+
+macui: FORCE
+ cd macui && xcodebuild -target "ZeroTier One" -configuration Release
+ $(CODESIGN) -f -s $(CODESIGN_APP_CERT) "macui/build/Release/ZeroTier One.app"
+
+#cli: FORCE
+# $(CXX) $(CXXFLAGS) -o zerotier cli/zerotier.cpp osdep/OSUtils.cpp node/InetAddress.cpp node/Utils.cpp node/Salsa20.cpp node/Identity.cpp node/SHA512.cpp node/C25519.cpp -lcurl
+# $(STRIP) zerotier
+
+selftest: $(CORE_OBJS) $(ONE_OBJS) selftest.o
+ $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(CORE_OBJS) $(ONE_OBJS) $(LIBS)
$(STRIP) zerotier-selftest
+zerotier-selftest: selftest
+
# Requires Packages: http://s.sudre.free.fr/Software/Packages/about.html
mac-dist-pkg: FORCE
packagesbuild "ext/installfiles/mac/ZeroTier One.pkgproj"
rm -f "ZeroTier One Signed.pkg"
$(PRODUCTSIGN) --sign $(CODESIGN_INSTALLER_CERT) "ZeroTier One.pkg" "ZeroTier One Signed.pkg"
if [ -f "ZeroTier One Signed.pkg" ]; then mv -f "ZeroTier One Signed.pkg" "ZeroTier One.pkg"; fi
+ rm -f zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_*
+ cat ext/installfiles/mac-update/updater.tmpl.sh "ZeroTier One.pkg" >zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_$(ZT_VERSION_MAJOR).$(ZT_VERSION_MINOR).$(ZT_VERSION_REV)_$(ZT_VERSION_BUILD).exe
# For ZeroTier, Inc. to build official signed packages
official: FORCE
make clean
- make ZT_OFFICIAL_RELEASE=1 -j 4 one
+ make ZT_OFFICIAL_RELEASE=1 -j 8 one
+ make ZT_OFFICIAL_RELEASE=1 macui
make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg
clean:
- rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o $(OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier ZeroTierOneInstaller-* mkworld doc/node_modules
+ rm -rf *.dSYM build-* *.a *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o $(CORE_OBJS) $(ONE_OBJS) zerotier-one zerotier-idtool zerotier-selftest zerotier-cli zerotier doc/node_modules macui/build zt1_update_$(ZT_BUILD_PLATFORM)_$(ZT_BUILD_ARCHITECTURE)_*
distclean: clean
- rm -rf doc/node_modules
+
+realclean: clean
# For those building from source -- installs signed binary tap driver in system ZT home
install-mac-tap: FORCE
diff --git a/node/Address.hpp b/node/Address.hpp
index 9bf5605a..41977af2 100644
--- a/node/Address.hpp
+++ b/node/Address.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ADDRESS_HPP
@@ -38,57 +46,26 @@ namespace ZeroTier {
class Address
{
public:
- Address()
- throw() :
- _a(0)
- {
- }
-
- Address(const Address &a)
- throw() :
- _a(a._a)
- {
- }
-
- Address(uint64_t a)
- throw() :
- _a(a & 0xffffffffffULL)
- {
- }
-
- Address(const char *s)
- throw()
- {
- unsigned char foo[ZT_ADDRESS_LENGTH];
- setTo(foo,Utils::unhex(s,foo,ZT_ADDRESS_LENGTH));
- }
-
- Address(const std::string &s)
- throw()
- {
- unsigned char foo[ZT_ADDRESS_LENGTH];
- setTo(foo,Utils::unhex(s.c_str(),foo,ZT_ADDRESS_LENGTH));
- }
+ Address() : _a(0) {}
+ Address(const Address &a) : _a(a._a) {}
+ Address(uint64_t a) : _a(a & 0xffffffffffULL) {}
/**
* @param bits Raw address -- 5 bytes, big-endian byte order
* @param len Length of array
*/
Address(const void *bits,unsigned int len)
- throw()
{
setTo(bits,len);
}
inline Address &operator=(const Address &a)
- throw()
{
_a = a._a;
return *this;
}
inline Address &operator=(const uint64_t a)
- throw()
{
_a = (a & 0xffffffffffULL);
return *this;
@@ -99,7 +76,6 @@ public:
* @param len Length of array
*/
inline void setTo(const void *bits,unsigned int len)
- throw()
{
if (len < ZT_ADDRESS_LENGTH) {
_a = 0;
@@ -119,7 +95,6 @@ public:
* @param len Length of array
*/
inline void copyTo(void *bits,unsigned int len) const
- throw()
{
if (len < ZT_ADDRESS_LENGTH)
return;
@@ -138,7 +113,6 @@ public:
*/
template<unsigned int C>
inline void appendTo(Buffer<C> &b) const
- throw(std::out_of_range)
{
unsigned char *p = (unsigned char *)b.appendField(ZT_ADDRESS_LENGTH);
*(p++) = (unsigned char)((_a >> 32) & 0xff);
@@ -152,7 +126,6 @@ public:
* @return Integer containing address (0 to 2^40)
*/
inline uint64_t toInt() const
- throw()
{
return _a;
}
@@ -161,7 +134,6 @@ public:
* @return Hash code for use with Hashtable
*/
inline unsigned long hashCode() const
- throw()
{
return (unsigned long)_a;
}
@@ -169,31 +141,20 @@ public:
/**
* @return Hexadecimal string
*/
- inline std::string toString() const
- {
- char buf[16];
- Utils::snprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a);
- return std::string(buf);
- };
-
- /**
- * @param buf Buffer to fill
- * @param len Length of buffer
- */
- inline void toString(char *buf,unsigned int len) const
+ inline char *toString(char buf[11]) const
{
- Utils::snprintf(buf,len,"%.10llx",(unsigned long long)_a);
+ return Utils::hex10(_a,buf);
}
/**
* @return True if this address is not zero
*/
- inline operator bool() const throw() { return (_a != 0); }
+ inline operator bool() const { return (_a != 0); }
/**
* Set to null/zero
*/
- inline void zero() throw() { _a = 0; }
+ inline void zero() { _a = 0; }
/**
* Check if this address is reserved
@@ -205,7 +166,6 @@ public:
* @return True if address is reserved and may not be used
*/
inline bool isReserved() const
- throw()
{
return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX));
}
@@ -214,21 +174,21 @@ public:
* @param i Value from 0 to 4 (inclusive)
* @return Byte at said position (address interpreted in big-endian order)
*/
- inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
-
- inline bool operator==(const uint64_t &a) const throw() { return (_a == (a & 0xffffffffffULL)); }
- inline bool operator!=(const uint64_t &a) const throw() { return (_a != (a & 0xffffffffffULL)); }
- inline bool operator>(const uint64_t &a) const throw() { return (_a > (a & 0xffffffffffULL)); }
- inline bool operator<(const uint64_t &a) const throw() { return (_a < (a & 0xffffffffffULL)); }
- inline bool operator>=(const uint64_t &a) const throw() { return (_a >= (a & 0xffffffffffULL)); }
- inline bool operator<=(const uint64_t &a) const throw() { return (_a <= (a & 0xffffffffffULL)); }
-
- inline bool operator==(const Address &a) const throw() { return (_a == a._a); }
- inline bool operator!=(const Address &a) const throw() { return (_a != a._a); }
- inline bool operator>(const Address &a) const throw() { return (_a > a._a); }
- inline bool operator<(const Address &a) const throw() { return (_a < a._a); }
- inline bool operator>=(const Address &a) const throw() { return (_a >= a._a); }
- inline bool operator<=(const Address &a) const throw() { return (_a <= a._a); }
+ inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
+
+ inline bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); }
+ inline bool operator!=(const uint64_t &a) const { return (_a != (a & 0xffffffffffULL)); }
+ inline bool operator>(const uint64_t &a) const { return (_a > (a & 0xffffffffffULL)); }
+ inline bool operator<(const uint64_t &a) const { return (_a < (a & 0xffffffffffULL)); }
+ inline bool operator>=(const uint64_t &a) const { return (_a >= (a & 0xffffffffffULL)); }
+ inline bool operator<=(const uint64_t &a) const { return (_a <= (a & 0xffffffffffULL)); }
+
+ inline bool operator==(const Address &a) const { return (_a == a._a); }
+ inline bool operator!=(const Address &a) const { return (_a != a._a); }
+ inline bool operator>(const Address &a) const { return (_a > a._a); }
+ inline bool operator<(const Address &a) const { return (_a < a._a); }
+ inline bool operator>=(const Address &a) const { return (_a >= a._a); }
+ inline bool operator<=(const Address &a) const { return (_a <= a._a); }
private:
uint64_t _a;
diff --git a/node/Array.hpp b/node/Array.hpp
deleted file mode 100644
index 19b29eb3..00000000
--- a/node/Array.hpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_ARRAY_HPP
-#define ZT_ARRAY_HPP
-
-#include <string>
-#include <algorithm>
-
-namespace ZeroTier {
-
-/**
- * Static array -- a simple thing that's belonged in STL since the time of the dinosaurs
- */
-template<typename T,std::size_t S>
-class Array
-{
-public:
- Array() throw() {}
-
- Array(const Array &a)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = a.data[i];
- }
-
- Array(const T *ptr)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = ptr[i];
- }
-
- inline Array &operator=(const Array &a)
- {
- for(std::size_t i=0;i<S;++i)
- data[i] = a.data[i];
- return *this;
- }
-
- typedef T value_type;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T& reference;
- typedef const T& const_reference;
- typedef T* iterator;
- typedef const T* const_iterator;
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
- typedef std::reverse_iterator<iterator> reverse_iterator;
- typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
-
- inline iterator begin() throw() { return data; }
- inline iterator end() throw() { return &(data[S]); }
- inline const_iterator begin() const throw() { return data; }
- inline const_iterator end() const throw() { return &(data[S]); }
-
- inline reverse_iterator rbegin() throw() { return reverse_iterator(begin()); }
- inline reverse_iterator rend() throw() { return reverse_iterator(end()); }
- inline const_reverse_iterator rbegin() const throw() { return const_reverse_iterator(begin()); }
- inline const_reverse_iterator rend() const throw() { return const_reverse_iterator(end()); }
-
- inline std::size_t size() const throw() { return S; }
- inline std::size_t max_size() const throw() { return S; }
-
- inline reference operator[](const std::size_t n) throw() { return data[n]; }
- inline const_reference operator[](const std::size_t n) const throw() { return data[n]; }
-
- inline reference front() throw() { return data[0]; }
- inline const_reference front() const throw() { return data[0]; }
- inline reference back() throw() { return data[S-1]; }
- inline const_reference back() const throw() { return data[S-1]; }
-
- inline bool operator==(const Array &k) const throw()
- {
- for(unsigned long i=0;i<S;++i) {
- if (data[i] != k.data[i])
- return false;
- }
- return true;
- }
- inline bool operator<(const Array &k) const throw() { return std::lexicographical_compare(begin(),end(),k.begin(),k.end()); }
- inline bool operator!=(const Array &k) const throw() { return !(*this == k); }
- inline bool operator>(const Array &k) const throw() { return (k < *this); }
- inline bool operator<=(const Array &k) const throw() { return !(k < *this); }
- inline bool operator>=(const Array &k) const throw() { return !(*this < k); }
-
- T data[S];
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp
index b4993771..a42a18d4 100644
--- a/node/AtomicCounter.hpp
+++ b/node/AtomicCounter.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,17 +14,22 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ATOMICCOUNTER_HPP
#define ZT_ATOMICCOUNTER_HPP
#include "Constants.hpp"
-#include "Mutex.hpp"
-#include "NonCopyable.hpp"
-#ifdef __WINDOWS__
-// <atomic> will replace this whole class eventually once it's ubiquitous
+#ifndef __GNUC__
#include <atomic>
#endif
@@ -33,78 +38,46 @@ namespace ZeroTier {
/**
* Simple atomic counter supporting increment and decrement
*/
-class AtomicCounter : NonCopyable
+class AtomicCounter
{
public:
- /**
- * Initialize counter at zero
- */
- AtomicCounter()
- throw()
- {
- _v = 0;
- }
+ AtomicCounter() { _v = 0; }
- inline operator int() const
- throw()
+ inline int load() const
{
#ifdef __GNUC__
- return __sync_or_and_fetch(const_cast <volatile int *>(&_v),0);
+ return __sync_or_and_fetch(const_cast<int *>(&_v),0);
#else
-#ifdef __WINDOWS__
- return (int)_v;
-#else
- _l.lock();
- int v = _v;
- _l.unlock();
- return v;
-#endif
+ return _v.load();
#endif
}
inline int operator++()
- throw()
{
#ifdef __GNUC__
return __sync_add_and_fetch(&_v,1);
#else
-#ifdef __WINDOWS__
return ++_v;
-#else
- _l.lock();
- int v = ++_v;
- _l.unlock();
- return v;
-#endif
#endif
}
inline int operator--()
- throw()
{
#ifdef __GNUC__
return __sync_sub_and_fetch(&_v,1);
#else
-#ifdef __WINDOWS__
return --_v;
-#else
- _l.lock();
- int v = --_v;
- _l.unlock();
- return v;
-#endif
#endif
}
private:
-#ifdef __WINDOWS__
- std::atomic_int _v;
-#else
+ AtomicCounter(const AtomicCounter &) {}
+ const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
+
+#ifdef __GNUC__
int _v;
-#ifndef __GNUC__
-#warning Neither __WINDOWS__ nor __GNUC__ so AtomicCounter using Mutex
- Mutex _l;
-#endif
+#else
+ std::atomic_int _v;
#endif
};
diff --git a/node/BinarySemaphore.hpp b/node/BinarySemaphore.hpp
deleted file mode 100644
index 315d2b00..00000000
--- a/node/BinarySemaphore.hpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_BINARYSEMAPHORE_HPP
-#define ZT_BINARYSEMAPHORE_HPP
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "Constants.hpp"
-#include "NonCopyable.hpp"
-
-#ifdef __WINDOWS__
-
-#include <Windows.h>
-
-namespace ZeroTier {
-
-class BinarySemaphore : NonCopyable
-{
-public:
- BinarySemaphore() throw() { _sem = CreateSemaphore(NULL,0,1,NULL); }
- ~BinarySemaphore() { CloseHandle(_sem); }
- inline void wait() { WaitForSingleObject(_sem,INFINITE); }
- inline void post() { ReleaseSemaphore(_sem,1,NULL); }
-private:
- HANDLE _sem;
-};
-
-} // namespace ZeroTier
-
-#else // !__WINDOWS__
-
-#include <pthread.h>
-
-namespace ZeroTier {
-
-class BinarySemaphore : NonCopyable
-{
-public:
- BinarySemaphore()
- {
- pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
- pthread_cond_init(&_cond,(const pthread_condattr_t *)0);
- _f = false;
- }
-
- ~BinarySemaphore()
- {
- pthread_cond_destroy(&_cond);
- pthread_mutex_destroy(&_mh);
- }
-
- inline void wait()
- {
- pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
- while (!_f)
- pthread_cond_wait(const_cast <pthread_cond_t *>(&_cond),const_cast <pthread_mutex_t *>(&_mh));
- _f = false;
- pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
- }
-
- inline void post()
- {
- pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
- _f = true;
- pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
- pthread_cond_signal(const_cast <pthread_cond_t *>(&_cond));
- }
-
-private:
- pthread_cond_t _cond;
- pthread_mutex_t _mh;
- volatile bool _f;
-};
-
-} // namespace ZeroTier
-
-#endif // !__WINDOWS__
-
-#endif
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index 0b171592..bbf4ee37 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BUFFER_HPP
@@ -61,11 +69,11 @@ public:
// STL container idioms
typedef unsigned char value_type;
typedef unsigned char * pointer;
- typedef const unsigned char * const_pointer;
- typedef unsigned char & reference;
- typedef const unsigned char & const_reference;
- typedef unsigned char * iterator;
- typedef const unsigned char * const_iterator;
+ typedef const char * const_pointer;
+ typedef char & reference;
+ typedef const char & const_reference;
+ typedef char * iterator;
+ typedef const char * const_iterator;
typedef unsigned int size_type;
typedef int difference_type;
typedef std::reverse_iterator<iterator> reverse_iterator;
@@ -79,78 +87,61 @@ public:
inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
- Buffer()
- throw() :
+ Buffer() :
_l(0)
{
}
Buffer(unsigned int l)
- throw(std::out_of_range)
{
if (l > C)
- throw std::out_of_range("Buffer: construct with size larger than capacity");
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l = l;
}
template<unsigned int C2>
Buffer(const Buffer<C2> &b)
- throw(std::out_of_range)
{
*this = b;
}
Buffer(const void *b,unsigned int l)
- throw(std::out_of_range)
{
copyFrom(b,l);
}
- Buffer(const std::string &s)
- throw(std::out_of_range)
- {
- copyFrom(s.data(),s.length());
- }
-
template<unsigned int C2>
inline Buffer &operator=(const Buffer<C2> &b)
- throw(std::out_of_range)
- {
- if (b._l > C)
- throw std::out_of_range("Buffer: assignment from buffer larger than capacity");
- memcpy(_b,b._b,_l = b._l);
- return *this;
- }
-
- inline Buffer &operator=(const std::string &s)
- throw(std::out_of_range)
{
- copyFrom(s.data(),s.length());
+ if (unlikely(b._l > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ if (C2 == C) {
+ ZT_FAST_MEMCPY(this,&b,sizeof(Buffer<C>));
+ } else {
+ ZT_FAST_MEMCPY(_b,b._b,_l = b._l);
+ }
return *this;
}
inline void copyFrom(const void *b,unsigned int l)
- throw(std::out_of_range)
{
- if (l > C)
- throw std::out_of_range("Buffer: set from C array larger than capacity");
+ if (unlikely(l > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ ZT_FAST_MEMCPY(_b,b,l);
_l = l;
- memcpy(_b,b,l);
}
unsigned char operator[](const unsigned int i) const
- throw(std::out_of_range)
{
- if (i >= _l)
- throw std::out_of_range("Buffer: [] beyond end of data");
+ if (unlikely(i >= _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (unsigned char)_b[i];
}
unsigned char &operator[](const unsigned int i)
- throw(std::out_of_range)
{
- if (i >= _l)
- throw std::out_of_range("Buffer: [] beyond end of data");
+ if (unlikely(i >= _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return ((unsigned char *)_b)[i];
}
@@ -168,17 +159,15 @@ public:
* @throws std::out_of_range Field extends beyond data size
*/
unsigned char *field(unsigned int i,unsigned int l)
- throw(std::out_of_range)
{
- if ((i + l) > _l)
- throw std::out_of_range("Buffer: field() beyond end of data");
+ if (unlikely((i + l) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (unsigned char *)(_b + i);
}
const unsigned char *field(unsigned int i,unsigned int l) const
- throw(std::out_of_range)
{
- if ((i + l) > _l)
- throw std::out_of_range("Buffer: field() beyond end of data");
+ if (unlikely((i + l) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
return (const unsigned char *)(_b + i);
}
@@ -191,10 +180,9 @@ public:
*/
template<typename T>
inline void setAt(unsigned int i,const T v)
- throw(std::out_of_range)
{
- if ((i + sizeof(T)) > _l)
- throw std::out_of_range("Buffer: setAt() beyond end of data");
+ if (unlikely((i + sizeof(T)) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *p = reinterpret_cast<uint8_t *>(_b + i);
for(unsigned int x=1;x<=sizeof(T);++x)
@@ -214,10 +202,9 @@ public:
*/
template<typename T>
inline T at(unsigned int i) const
- throw(std::out_of_range)
{
- if ((i + sizeof(T)) > _l)
- throw std::out_of_range("Buffer: at() beyond end of data");
+ if (unlikely((i + sizeof(T)) > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
T v = 0;
const uint8_t *p = reinterpret_cast<const uint8_t *>(_b + i);
@@ -241,10 +228,9 @@ public:
*/
template<typename T>
inline void append(const T v)
- throw(std::out_of_range)
{
- if ((_l + sizeof(T)) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + sizeof(T)) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
#ifdef ZT_NO_TYPE_PUNNING
uint8_t *p = reinterpret_cast<uint8_t *>(_b + _l);
for(unsigned int x=1;x<=sizeof(T);++x)
@@ -264,40 +250,39 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void append(unsigned char c,unsigned int n)
- throw(std::out_of_range)
{
- if ((_l + n) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + n) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
for(unsigned int i=0;i<n;++i)
_b[_l++] = (char)c;
}
/**
- * Append a C-array of bytes
+ * Append secure random bytes
*
- * @param b Data
- * @param l Length
- * @throws std::out_of_range Attempt to append beyond capacity
+ * @param n Number of random bytes to append
*/
- inline void append(const void *b,unsigned int l)
- throw(std::out_of_range)
+ inline void appendRandom(unsigned int n)
{
- if ((_l + l) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
- memcpy(_b + _l,b,l);
- _l += l;
+ if (unlikely((_l + n) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ Utils::getSecureRandom(_b + _l,n);
+ _l += n;
}
/**
- * Append a string
+ * Append a C-array of bytes
*
- * @param s String to append
+ * @param b Data
+ * @param l Length
* @throws std::out_of_range Attempt to append beyond capacity
*/
- inline void append(const std::string &s)
- throw(std::out_of_range)
+ inline void append(const void *b,unsigned int l)
{
- append(s.data(),(unsigned int)s.length());
+ if (unlikely((_l + l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+ ZT_FAST_MEMCPY(_b + _l,b,l);
+ _l += l;
}
/**
@@ -307,11 +292,10 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void appendCString(const char *s)
- throw(std::out_of_range)
{
for(;;) {
- if (_l >= C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely(_l >= C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
if (!(_b[_l++] = *(s++)))
break;
}
@@ -326,7 +310,6 @@ public:
*/
template<unsigned int C2>
inline void append(const Buffer<C2> &b)
- throw(std::out_of_range)
{
append(b._b,b._l);
}
@@ -342,10 +325,9 @@ public:
* @return Pointer to beginning of appended field of length 'l'
*/
inline char *appendField(unsigned int l)
- throw(std::out_of_range)
{
- if ((_l + l) > C)
- throw std::out_of_range("Buffer: append beyond capacity");
+ if (unlikely((_l + l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
char *r = _b + _l;
_l += l;
return r;
@@ -360,10 +342,9 @@ public:
* @throws std::out_of_range Capacity exceeded
*/
inline void addSize(unsigned int i)
- throw(std::out_of_range)
{
- if ((i + _l) > C)
- throw std::out_of_range("Buffer: setSize to larger than capacity");
+ if (unlikely((i + _l) > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l += i;
}
@@ -376,10 +357,9 @@ public:
* @throws std::out_of_range Size larger than capacity
*/
inline void setSize(const unsigned int i)
- throw(std::out_of_range)
{
- if (i > C)
- throw std::out_of_range("Buffer: setSize to larger than capacity");
+ if (unlikely(i > C))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
_l = i;
}
@@ -387,15 +367,14 @@ public:
* Move everything after 'at' to the buffer's front and truncate
*
* @param at Truncate before this position
- * @throw std::out_of_range Position is beyond size of buffer
+ * @throws std::out_of_range Position is beyond size of buffer
*/
inline void behead(const unsigned int at)
- throw(std::out_of_range)
{
if (!at)
return;
- if (at > _l)
- throw std::out_of_range("Buffer: behead() beyond capacity");
+ if (unlikely(at > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
::memmove(_b,_b + at,_l -= at);
}
@@ -404,14 +383,13 @@ public:
*
* @param start Starting position
* @param length Length of block to erase
- * @throw std::out_of_range Position plus length is beyond size of buffer
+ * @throws std::out_of_range Position plus length is beyond size of buffer
*/
inline void erase(const unsigned int at,const unsigned int length)
- throw(std::out_of_range)
{
const unsigned int endr = at + length;
- if (endr > _l)
- throw std::out_of_range("Buffer: erase() range beyond end of buffer");
+ if (unlikely(endr > _l))
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
::memmove(_b + at,_b + endr,_l - endr);
_l -= length;
}
@@ -419,94 +397,77 @@ public:
/**
* Set buffer data length to zero
*/
- inline void clear()
- throw()
- {
- _l = 0;
- }
+ inline void clear() { _l = 0; }
/**
* Zero buffer up to size()
*/
- inline void zero()
- throw()
- {
- memset(_b,0,_l);
- }
+ inline void zero() { memset(_b,0,_l); }
/**
* Zero unused capacity area
*/
- inline void zeroUnused()
- throw()
- {
- memset(_b + _l,0,C - _l);
- }
+ inline void zeroUnused() { memset(_b + _l,0,C - _l); }
/**
* Unconditionally and securely zero buffer's underlying memory
*/
- inline void burn()
- throw()
- {
- Utils::burn(_b,sizeof(_b));
- }
+ inline void burn() { Utils::burn(_b,sizeof(_b)); }
/**
* @return Constant pointer to data in buffer
*/
- inline const void *data() const throw() { return _b; }
+ inline const void *data() const { return _b; }
+
+ /**
+ * @return Non-constant pointer to data in buffer
+ */
+ inline void *unsafeData() { return _b; }
/**
* @return Size of data in buffer
*/
- inline unsigned int size() const throw() { return _l; }
+ inline unsigned int size() const { return _l; }
/**
* @return Capacity of buffer
*/
- inline unsigned int capacity() const throw() { return C; }
+ inline unsigned int capacity() const { return C; }
template<unsigned int C2>
inline bool operator==(const Buffer<C2> &b) const
- throw()
{
return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
}
template<unsigned int C2>
inline bool operator!=(const Buffer<C2> &b) const
- throw()
{
return ((_l != b._l)||(memcmp(_b,b._b,_l)));
}
template<unsigned int C2>
inline bool operator<(const Buffer<C2> &b) const
- throw()
{
return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
}
template<unsigned int C2>
inline bool operator>(const Buffer<C2> &b) const
- throw()
{
return (b < *this);
}
template<unsigned int C2>
inline bool operator<=(const Buffer<C2> &b) const
- throw()
{
return !(b < *this);
}
template<unsigned int C2>
inline bool operator>=(const Buffer<C2> &b) const
- throw()
{
return !(*this < b);
}
private:
- unsigned int _l;
char ZT_VAR_MAY_ALIAS _b[C];
+ unsigned int _l;
};
} // namespace ZeroTier
diff --git a/node/C25519.cpp b/node/C25519.cpp
index e9ffecc1..77084bd8 100644
--- a/node/C25519.cpp
+++ b/node/C25519.cpp
@@ -1,5 +1,3 @@
-// Code taken from NaCl by D. J. Bernstein and others
-
/*
Matthew Dempsky
Public domain.
@@ -7,7 +5,7 @@ Derived from public domain code by D. J. Bernstein.
*/
// Modified very slightly for ZeroTier One by Adam Ierymenko
-// (no functional changes)
+// This code remains in the public domain.
#include <stdint.h>
#include <stdlib.h>
@@ -22,7 +20,7 @@ Derived from public domain code by D. J. Bernstein.
#pragma warning(disable: 4146)
#endif
-namespace ZeroTier {
+namespace {
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -33,990 +31,853 @@ namespace ZeroTier {
#define crypto_uint64 uint64_t
#define crypto_hash_sha512_BYTES 64
-static inline void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int j;
- unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
- u += a[31] + b[31]; out[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; }
+ u += a[31] + b[31]; out[31] = u;
}
-static inline void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int j;
- unsigned int u;
- u = 218;
- for (j = 0;j < 31;++j) {
- u += a[j] + 65280 - b[j];
- out[j] = u & 255;
- u >>= 8;
- }
- u += a[31] - b[31];
- out[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 218;
+ for (j = 0;j < 31;++j) {
+ u += a[j] + 65280 - b[j];
+ out[j] = u & 255;
+ u >>= 8;
+ }
+ u += a[31] - b[31];
+ out[31] = u;
}
-static inline void squeeze(unsigned int a[32])
+void squeeze(unsigned int a[32])
{
- unsigned int j;
- unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
- u += a[31]; a[31] = u & 127;
- u = 19 * (u >> 7);
- for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
- u += a[31]; a[31] = u;
+ unsigned int j;
+ unsigned int u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
+ u += a[31]; a[31] = u & 127;
+ u = 19 * (u >> 7);
+ for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; }
+ u += a[31]; a[31] = u;
}
static const unsigned int minusp[32] = {
19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128
} ;
-static inline void freeze(unsigned int a[32])
+void freeze(unsigned int a[32])
{
- unsigned int aorig[32];
- unsigned int j;
- unsigned int negative;
+ unsigned int aorig[32];
+ unsigned int j;
+ unsigned int negative;
- for (j = 0;j < 32;++j) aorig[j] = a[j];
- add(a,a,minusp);
- negative = -((a[31] >> 7) & 1);
- for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
+ for (j = 0;j < 32;++j) aorig[j] = a[j];
+ add(a,a,minusp);
+ negative = -((a[31] >> 7) & 1);
+ for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]);
}
-static inline void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
+void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32])
{
- unsigned int i;
- unsigned int j;
- unsigned int u;
+ unsigned int i;
+ unsigned int j;
+ unsigned int u;
- for (i = 0;i < 32;++i) {
- u = 0;
- for (j = 0;j <= i;++j) u += a[j] * b[i - j];
- for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
- out[i] = u;
- }
- squeeze(out);
+ for (i = 0;i < 32;++i) {
+ u = 0;
+ for (j = 0;j <= i;++j) u += a[j] * b[i - j];
+ for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j];
+ out[i] = u;
+ }
+ squeeze(out);
}
-static inline void mult121665(unsigned int out[32],const unsigned int a[32])
+void mult121665(unsigned int out[32],const unsigned int a[32])
{
- unsigned int j;
- unsigned int u;
+ unsigned int j;
+ unsigned int u;
- u = 0;
- for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
- u += 121665 * a[31]; out[31] = u & 127;
- u = 19 * (u >> 7);
- for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
- u += out[j]; out[j] = u;
+ u = 0;
+ for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; }
+ u += 121665 * a[31]; out[31] = u & 127;
+ u = 19 * (u >> 7);
+ for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; }
+ u += out[j]; out[j] = u;
}
-static inline void square(unsigned int out[32],const unsigned int a[32])
+void square(unsigned int out[32],const unsigned int a[32])
{
- unsigned int i;
- unsigned int j;
- unsigned int u;
+ unsigned int i;
+ unsigned int j;
+ unsigned int u;
- for (i = 0;i < 32;++i) {
- u = 0;
- for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
- for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
- u *= 2;
- if ((i & 1) == 0) {
- u += a[i / 2] * a[i / 2];
- u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
- }
- out[i] = u;
- }
- squeeze(out);
+ for (i = 0;i < 32;++i) {
+ u = 0;
+ for (j = 0;j < i - j;++j) u += a[j] * a[i - j];
+ for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j];
+ u *= 2;
+ if ((i & 1) == 0) {
+ u += a[i / 2] * a[i / 2];
+ u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
+ }
+ out[i] = u;
+ }
+ squeeze(out);
}
-static inline void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
+void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b)
{
- unsigned int j;
- unsigned int t;
- unsigned int bminus1;
+ unsigned int j;
+ unsigned int t;
+ unsigned int bminus1;
- bminus1 = b - 1;
- for (j = 0;j < 64;++j) {
- t = bminus1 & (r[j] ^ s[j]);
- p[j] = s[j] ^ t;
- q[j] = r[j] ^ t;
- }
+ bminus1 = b - 1;
+ for (j = 0;j < 64;++j) {
+ t = bminus1 & (r[j] ^ s[j]);
+ p[j] = s[j] ^ t;
+ q[j] = r[j] ^ t;
+ }
}
static void mainloop(unsigned int work[64],const unsigned char e[32])
{
- unsigned int xzm1[64];
- unsigned int xzm[64];
- unsigned int xzmb[64];
- unsigned int xzm1b[64];
- unsigned int xznb[64];
- unsigned int xzn1b[64];
- unsigned int a0[64];
- unsigned int a1[64];
- unsigned int b0[64];
- unsigned int b1[64];
- unsigned int c1[64];
- unsigned int r[32];
- unsigned int s[32];
- unsigned int t[32];
- unsigned int u[32];
- //unsigned int i;
- unsigned int j;
- unsigned int b;
- int pos;
-
- for (j = 0;j < 32;++j) xzm1[j] = work[j];
- xzm1[32] = 1;
- for (j = 33;j < 64;++j) xzm1[j] = 0;
-
- xzm[0] = 1;
- for (j = 1;j < 64;++j) xzm[j] = 0;
-
- for (pos = 254;pos >= 0;--pos) {
- b = e[pos / 8] >> (pos & 7);
- b &= 1;
- select(xzmb,xzm1b,xzm,xzm1,b);
- add(a0,xzmb,xzmb + 32);
- sub(a0 + 32,xzmb,xzmb + 32);
- add(a1,xzm1b,xzm1b + 32);
- sub(a1 + 32,xzm1b,xzm1b + 32);
- square(b0,a0);
- square(b0 + 32,a0 + 32);
- mult(b1,a1,a0 + 32);
- mult(b1 + 32,a1 + 32,a0);
- add(c1,b1,b1 + 32);
- sub(c1 + 32,b1,b1 + 32);
- square(r,c1 + 32);
- sub(s,b0,b0 + 32);
- mult121665(t,s);
- add(u,t,b0);
- mult(xznb,b0,b0 + 32);
- mult(xznb + 32,s,u);
- square(xzn1b,c1);
- mult(xzn1b + 32,r,work);
- select(xzm,xzm1,xznb,xzn1b,b);
- }
-
- for (j = 0;j < 64;++j) work[j] = xzm[j];
+ unsigned int xzm1[64];
+ unsigned int xzm[64];
+ unsigned int xzmb[64];
+ unsigned int xzm1b[64];
+ unsigned int xznb[64];
+ unsigned int xzn1b[64];
+ unsigned int a0[64];
+ unsigned int a1[64];
+ unsigned int b0[64];
+ unsigned int b1[64];
+ unsigned int c1[64];
+ unsigned int r[32];
+ unsigned int s[32];
+ unsigned int t[32];
+ unsigned int u[32];
+ //unsigned int i;
+ unsigned int j;
+ unsigned int b;
+ int pos;
+
+ for (j = 0;j < 32;++j) xzm1[j] = work[j];
+ xzm1[32] = 1;
+ for (j = 33;j < 64;++j) xzm1[j] = 0;
+
+ xzm[0] = 1;
+ for (j = 1;j < 64;++j) xzm[j] = 0;
+
+ for (pos = 254;pos >= 0;--pos) {
+ b = e[pos / 8] >> (pos & 7);
+ b &= 1;
+ select(xzmb,xzm1b,xzm,xzm1,b);
+ add(a0,xzmb,xzmb + 32);
+ sub(a0 + 32,xzmb,xzmb + 32);
+ add(a1,xzm1b,xzm1b + 32);
+ sub(a1 + 32,xzm1b,xzm1b + 32);
+ square(b0,a0);
+ square(b0 + 32,a0 + 32);
+ mult(b1,a1,a0 + 32);
+ mult(b1 + 32,a1 + 32,a0);
+ add(c1,b1,b1 + 32);
+ sub(c1 + 32,b1,b1 + 32);
+ square(r,c1 + 32);
+ sub(s,b0,b0 + 32);
+ mult121665(t,s);
+ add(u,t,b0);
+ mult(xznb,b0,b0 + 32);
+ mult(xznb + 32,s,u);
+ square(xzn1b,c1);
+ mult(xzn1b + 32,r,work);
+ select(xzm,xzm1,xznb,xzn1b,b);
+ }
+
+ for (j = 0;j < 64;++j) work[j] = xzm[j];
}
static void recip(unsigned int out[32],const unsigned int z[32])
{
- unsigned int z2[32];
- unsigned int z9[32];
- unsigned int z11[32];
- unsigned int z2_5_0[32];
- unsigned int z2_10_0[32];
- unsigned int z2_20_0[32];
- unsigned int z2_50_0[32];
- unsigned int z2_100_0[32];
- unsigned int t0[32];
- unsigned int t1[32];
- int i;
-
- /* 2 */ square(z2,z);
- /* 4 */ square(t1,z2);
- /* 8 */ square(t0,t1);
- /* 9 */ mult(z9,t0,z);
- /* 11 */ mult(z11,z9,z2);
- /* 22 */ square(t0,z11);
- /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
-
- /* 2^6 - 2^1 */ square(t0,z2_5_0);
- /* 2^7 - 2^2 */ square(t1,t0);
- /* 2^8 - 2^3 */ square(t0,t1);
- /* 2^9 - 2^4 */ square(t1,t0);
- /* 2^10 - 2^5 */ square(t0,t1);
- /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
-
- /* 2^11 - 2^1 */ square(t0,z2_10_0);
- /* 2^12 - 2^2 */ square(t1,t0);
- /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
-
- /* 2^21 - 2^1 */ square(t0,z2_20_0);
- /* 2^22 - 2^2 */ square(t1,t0);
- /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
-
- /* 2^41 - 2^1 */ square(t1,t0);
- /* 2^42 - 2^2 */ square(t0,t1);
- /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
- /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
-
- /* 2^51 - 2^1 */ square(t0,z2_50_0);
- /* 2^52 - 2^2 */ square(t1,t0);
- /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
-
- /* 2^101 - 2^1 */ square(t1,z2_100_0);
- /* 2^102 - 2^2 */ square(t0,t1);
- /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
- /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
-
- /* 2^201 - 2^1 */ square(t0,t1);
- /* 2^202 - 2^2 */ square(t1,t0);
- /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
- /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
-
- /* 2^251 - 2^1 */ square(t1,t0);
- /* 2^252 - 2^2 */ square(t0,t1);
- /* 2^253 - 2^3 */ square(t1,t0);
- /* 2^254 - 2^4 */ square(t0,t1);
- /* 2^255 - 2^5 */ square(t1,t0);
- /* 2^255 - 21 */ mult(out,t1,z11);
-}
-
-static inline int crypto_scalarmult(unsigned char *q,
- const unsigned char *n,
- const unsigned char *p)
-{
- unsigned int work[96];
- unsigned char e[32];
- unsigned int i;
- for (i = 0;i < 32;++i) e[i] = n[i];
- e[0] &= 248;
- e[31] &= 127;
- e[31] |= 64;
- for (i = 0;i < 32;++i) work[i] = p[i];
- mainloop(work,e);
- recip(work + 32,work + 32);
- mult(work + 64,work,work + 32);
- freeze(work + 64);
- for (i = 0;i < 32;++i) q[i] = work[64 + i];
- return 0;
+ unsigned int z2[32];
+ unsigned int z9[32];
+ unsigned int z11[32];
+ unsigned int z2_5_0[32];
+ unsigned int z2_10_0[32];
+ unsigned int z2_20_0[32];
+ unsigned int z2_50_0[32];
+ unsigned int z2_100_0[32];
+ unsigned int t0[32];
+ unsigned int t1[32];
+ int i;
+
+ /* 2 */ square(z2,z);
+ /* 4 */ square(t1,z2);
+ /* 8 */ square(t0,t1);
+ /* 9 */ mult(z9,t0,z);
+ /* 11 */ mult(z11,z9,z2);
+ /* 22 */ square(t0,z11);
+ /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ square(t0,z2_5_0);
+ /* 2^7 - 2^2 */ square(t1,t0);
+ /* 2^8 - 2^3 */ square(t0,t1);
+ /* 2^9 - 2^4 */ square(t1,t0);
+ /* 2^10 - 2^5 */ square(t0,t1);
+ /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ square(t0,z2_10_0);
+ /* 2^12 - 2^2 */ square(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ square(t0,z2_20_0);
+ /* 2^22 - 2^2 */ square(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ square(t1,t0);
+ /* 2^42 - 2^2 */ square(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); }
+ /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ square(t0,z2_50_0);
+ /* 2^52 - 2^2 */ square(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ square(t1,z2_100_0);
+ /* 2^102 - 2^2 */ square(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); }
+ /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ square(t0,t1);
+ /* 2^202 - 2^2 */ square(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); }
+ /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ square(t1,t0);
+ /* 2^252 - 2^2 */ square(t0,t1);
+ /* 2^253 - 2^3 */ square(t1,t0);
+ /* 2^254 - 2^4 */ square(t0,t1);
+ /* 2^255 - 2^5 */ square(t1,t0);
+ /* 2^255 - 21 */ mult(out,t1,z11);
+}
+
+int crypto_scalarmult(unsigned char *q,const unsigned char *n,const unsigned char *p)
+{
+ unsigned int work[96];
+ unsigned char e[32];
+ unsigned int i;
+ for (i = 0;i < 32;++i) e[i] = n[i];
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+ for (i = 0;i < 32;++i) work[i] = p[i];
+ mainloop(work,e);
+ recip(work + 32,work + 32);
+ mult(work + 64,work,work + 32);
+ freeze(work + 64);
+ for (i = 0;i < 32;++i) q[i] = work[64 + i];
+ return 0;
}
static const unsigned char base[32] = {9};
-
-static inline int crypto_scalarmult_base(unsigned char *q,
- const unsigned char *n)
+int crypto_scalarmult_base(unsigned char *q,const unsigned char *n)
{
- return crypto_scalarmult(q,n,base);
+ return crypto_scalarmult(q,n,base);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
-// This is the Ed25519 stuff from SUPERCOP:
-// http://bench.cr.yp.to/supercop.html
+// Ed25519 ref from: http://bench.cr.yp.to/supercop.html
-// Also public domain, newer version than the Ed25519 found in NaCl
-
-typedef struct
+typedef struct
{
- crypto_uint32 v[32];
+ crypto_uint32 v[32];
}
fe25519;
+typedef struct
+{
+ crypto_uint32 v[32];
+}
+sc25519;
+
+typedef struct
+{
+ crypto_uint32 v[16];
+}
+shortsc25519;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+ fe25519 t;
+} ge25519;
+
+#define ge25519_p3 ge25519
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 z;
+ fe25519 y;
+ fe25519 t;
+} ge25519_p1p1;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+ fe25519 z;
+} ge25519_p2;
+
+typedef struct
+{
+ fe25519 x;
+ fe25519 y;
+} ge25519_aff;
+
static void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y);
-static inline crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
- x -= 1; /* 4294967295: yes; 0..65534: no */
- x >>= 31; /* 1: yes; 0: no */
- return x;
+ crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
+ x -= 1; /* 4294967295: yes; 0..65534: no */
+ x >>= 31; /* 1: yes; 0: no */
+ return x;
}
-static inline crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- unsigned int x = a;
- x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
- x >>= 31; /* 0: yes; 1: no */
- x ^= 1; /* 1: yes; 0: no */
- return x;
+ unsigned int x = a;
+ x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
+ x >>= 31; /* 0: yes; 1: no */
+ x ^= 1; /* 1: yes; 0: no */
+ return x;
}
-static inline crypto_uint32 times19(crypto_uint32 a)
+crypto_uint32 times19(crypto_uint32 a)
{
- return (a << 4) + (a << 1) + a;
+ return (a << 4) + (a << 1) + a;
}
-static inline crypto_uint32 times38(crypto_uint32 a)
+crypto_uint32 times38(crypto_uint32 a)
{
- return (a << 5) + (a << 2) + (a << 1);
+ return (a << 5) + (a << 2) + (a << 1);
}
-static inline void reduce_add_sub(fe25519 *r)
+void reduce_add_sub(fe25519 *r)
{
- crypto_uint32 t;
- int i,rep;
+ crypto_uint32 t;
+ int i,rep;
- for(rep=0;rep<4;rep++)
- {
- t = r->v[31] >> 7;
- r->v[31] &= 127;
- t = times19(t);
- r->v[0] += t;
- for(i=0;i<31;i++)
- {
- t = r->v[i] >> 8;
- r->v[i+1] += t;
- r->v[i] &= 255;
- }
- }
+ for(rep=0;rep<4;rep++)
+ {
+ t = r->v[31] >> 7;
+ r->v[31] &= 127;
+ t = times19(t);
+ r->v[0] += t;
+ for(i=0;i<31;i++)
+ {
+ t = r->v[i] >> 8;
+ r->v[i+1] += t;
+ r->v[i] &= 255;
+ }
+ }
}
-static inline void reduce_mul(fe25519 *r)
+void reduce_mul(fe25519 *r)
{
- crypto_uint32 t;
- int i,rep;
+ crypto_uint32 t;
+ int i,rep;
- for(rep=0;rep<2;rep++)
- {
- t = r->v[31] >> 7;
- r->v[31] &= 127;
- t = times19(t);
- r->v[0] += t;
- for(i=0;i<31;i++)
- {
- t = r->v[i] >> 8;
- r->v[i+1] += t;
- r->v[i] &= 255;
- }
- }
+ for(rep=0;rep<2;rep++)
+ {
+ t = r->v[31] >> 7;
+ r->v[31] &= 127;
+ t = times19(t);
+ r->v[0] += t;
+ for(i=0;i<31;i++)
+ {
+ t = r->v[i] >> 8;
+ r->v[i+1] += t;
+ r->v[i] &= 255;
+ }
+ }
}
/* reduction modulo 2^255-19 */
-static inline void fe25519_freeze(fe25519 *r)
+void fe25519_freeze(fe25519 *r)
{
- int i;
- crypto_uint32 m = equal(r->v[31],127);
- for(i=30;i>0;i--)
- m &= equal(r->v[i],255);
- m &= ge(r->v[0],237);
+ int i;
+ crypto_uint32 m = equal(r->v[31],127);
+ for(i=30;i>0;i--)
+ m &= equal(r->v[i],255);
+ m &= ge(r->v[0],237);
- m = -m;
+ m = -m;
- r->v[31] -= m&127;
- for(i=30;i>0;i--)
- r->v[i] -= m&255;
- r->v[0] -= m&237;
+ r->v[31] -= m&127;
+ for(i=30;i>0;i--)
+ r->v[i] -= m&255;
+ r->v[0] -= m&237;
}
-static inline void fe25519_unpack(fe25519 *r, const unsigned char x[32])
+void fe25519_unpack(fe25519 *r, const unsigned char x[32])
{
- int i;
- for(i=0;i<32;i++) r->v[i] = x[i];
- r->v[31] &= 127;
+ int i;
+ for(i=0;i<32;i++) r->v[i] = x[i];
+ r->v[31] &= 127;
}
/* Assumes input x being reduced below 2^255 */
-static inline void fe25519_pack(unsigned char r[32], const fe25519 *x)
+void fe25519_pack(unsigned char r[32], const fe25519 *x)
{
- int i;
- fe25519 y = *x;
- fe25519_freeze(&y);
- for(i=0;i<32;i++)
- r[i] = y.v[i];
+ int i;
+ fe25519 y = *x;
+ fe25519_freeze(&y);
+ for(i=0;i<32;i++)
+ r[i] = y.v[i];
}
-#if 0
-static int fe25519_iszero(const fe25519 *x)
-{
- int i;
- int r;
- fe25519 t = *x;
- fe25519_freeze(&t);
- r = equal(t.v[0],0);
- for(i=1;i<32;i++)
- r &= equal(t.v[i],0);
- return r;
-}
-#endif
-
-static inline int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
+int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
{
- int i;
- fe25519 t1 = *x;
- fe25519 t2 = *y;
- fe25519_freeze(&t1);
- fe25519_freeze(&t2);
- for(i=0;i<32;i++)
- if(t1.v[i] != t2.v[i]) return 0;
- return 1;
+ int i;
+ fe25519 t1 = *x;
+ fe25519 t2 = *y;
+ fe25519_freeze(&t1);
+ fe25519_freeze(&t2);
+ for(i=0;i<32;i++)
+ if(t1.v[i] != t2.v[i]) return 0;
+ return 1;
}
-static inline void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
+void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
{
- int i;
- crypto_uint32 mask = b;
- mask = -mask;
- for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
+ int i;
+ crypto_uint32 mask = b;
+ mask = -mask;
+ for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
}
-static inline unsigned char fe25519_getparity(const fe25519 *x)
+unsigned char fe25519_getparity(const fe25519 *x)
{
- fe25519 t = *x;
- fe25519_freeze(&t);
- return t.v[0] & 1;
+ fe25519 t = *x;
+ fe25519_freeze(&t);
+ return t.v[0] & 1;
}
-static inline void fe25519_setone(fe25519 *r)
+void fe25519_setone(fe25519 *r)
{
- int i;
- r->v[0] = 1;
- for(i=1;i<32;i++) r->v[i]=0;
+ int i;
+ r->v[0] = 1;
+ for(i=1;i<32;i++) r->v[i]=0;
}
-static inline void fe25519_setzero(fe25519 *r)
+void fe25519_setzero(fe25519 *r)
{
- int i;
- for(i=0;i<32;i++) r->v[i]=0;
+ int i;
+ for(i=0;i<32;i++) r->v[i]=0;
}
-static inline void fe25519_neg(fe25519 *r, const fe25519 *x)
+void fe25519_neg(fe25519 *r, const fe25519 *x)
{
- fe25519 t;
- int i;
- for(i=0;i<32;i++) t.v[i]=x->v[i];
- fe25519_setzero(r);
- fe25519_sub(r, r, &t);
+ fe25519 t;
+ int i;
+ for(i=0;i<32;i++) t.v[i]=x->v[i];
+ fe25519_setzero(r);
+ fe25519_sub(r, r, &t);
}
-static inline void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i;
- for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
- reduce_add_sub(r);
+ int i;
+ for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
+ reduce_add_sub(r);
}
-static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i;
- crypto_uint32 t[32];
- t[0] = x->v[0] + 0x1da;
- t[31] = x->v[31] + 0xfe;
- for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe;
- for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i];
- reduce_add_sub(r);
+ int i;
+ crypto_uint32 t[32];
+ t[0] = x->v[0] + 0x1da;
+ t[31] = x->v[31] + 0xfe;
+ for(i=1;i<31;i++) t[i] = x->v[i] + 0x1fe;
+ for(i=0;i<32;i++) r->v[i] = t[i] - y->v[i];
+ reduce_add_sub(r);
}
-static inline void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
+void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
{
- int i,j;
- crypto_uint32 t[63];
- for(i=0;i<63;i++)t[i] = 0;
+ int i,j;
+ crypto_uint32 t[63];
+ for(i=0;i<63;i++)t[i] = 0;
- for(i=0;i<32;i++)
- for(j=0;j<32;j++)
- t[i+j] += x->v[i] * y->v[j];
+ for(i=0;i<32;i++)
+ for(j=0;j<32;j++)
+ t[i+j] += x->v[i] * y->v[j];
- for(i=32;i<63;i++)
- r->v[i-32] = t[i-32] + times38(t[i]);
- r->v[31] = t[31]; /* result now in r[0]...r[31] */
+ for(i=32;i<63;i++)
+ r->v[i-32] = t[i-32] + times38(t[i]);
+ r->v[31] = t[31]; /* result now in r[0]...r[31] */
- reduce_mul(r);
+ reduce_mul(r);
}
-static inline void fe25519_square(fe25519 *r, const fe25519 *x)
+void fe25519_square(fe25519 *r, const fe25519 *x)
{
- fe25519_mul(r, x, x);
+ fe25519_mul(r, x, x);
}
-static void fe25519_invert(fe25519 *r, const fe25519 *x)
+void fe25519_invert(fe25519 *r, const fe25519 *x)
{
- fe25519 z2;
- fe25519 z9;
- fe25519 z11;
- fe25519 z2_5_0;
- fe25519 z2_10_0;
- fe25519 z2_20_0;
- fe25519 z2_50_0;
- fe25519 z2_100_0;
- fe25519 t0;
- fe25519 t1;
- int i;
-
- /* 2 */ fe25519_square(&z2,x);
- /* 4 */ fe25519_square(&t1,&z2);
- /* 8 */ fe25519_square(&t0,&t1);
- /* 9 */ fe25519_mul(&z9,&t0,x);
- /* 11 */ fe25519_mul(&z11,&z9,&z2);
- /* 22 */ fe25519_square(&t0,&z11);
- /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9);
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t0;
+ fe25519 t1;
+ int i;
- /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0);
- /* 2^7 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^8 - 2^3 */ fe25519_square(&t0,&t1);
- /* 2^9 - 2^4 */ fe25519_square(&t1,&t0);
- /* 2^10 - 2^5 */ fe25519_square(&t0,&t1);
- /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0);
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t1,&z2);
+ /* 8 */ fe25519_square(&t0,&t1);
+ /* 9 */ fe25519_mul(&z9,&t0,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t0,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t0,&z9);
- /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0);
- /* 2^12 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0);
+ /* 2^6 - 2^1 */ fe25519_square(&t0,&z2_5_0);
+ /* 2^7 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^8 - 2^3 */ fe25519_square(&t0,&t1);
+ /* 2^9 - 2^4 */ fe25519_square(&t1,&t0);
+ /* 2^10 - 2^5 */ fe25519_square(&t0,&t1);
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t0,&z2_5_0);
- /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0);
- /* 2^22 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0);
+ /* 2^11 - 2^1 */ fe25519_square(&t0,&z2_10_0);
+ /* 2^12 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t1,&z2_10_0);
- /* 2^41 - 2^1 */ fe25519_square(&t1,&t0);
- /* 2^42 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
- /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0);
+ /* 2^21 - 2^1 */ fe25519_square(&t0,&z2_20_0);
+ /* 2^22 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t0,&t1,&z2_20_0);
- /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0);
- /* 2^52 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0);
+ /* 2^41 - 2^1 */ fe25519_square(&t1,&t0);
+ /* 2^42 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t0,&z2_10_0);
- /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0);
- /* 2^102 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
- /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0);
-
- /* 2^201 - 2^1 */ fe25519_square(&t0,&t1);
- /* 2^202 - 2^2 */ fe25519_square(&t1,&t0);
- /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
- /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0);
+ /* 2^51 - 2^1 */ fe25519_square(&t0,&z2_50_0);
+ /* 2^52 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t1,&z2_50_0);
- /* 2^251 - 2^1 */ fe25519_square(&t1,&t0);
- /* 2^252 - 2^2 */ fe25519_square(&t0,&t1);
- /* 2^253 - 2^3 */ fe25519_square(&t1,&t0);
- /* 2^254 - 2^4 */ fe25519_square(&t0,&t1);
- /* 2^255 - 2^5 */ fe25519_square(&t1,&t0);
- /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11);
-}
-
-static void fe25519_pow2523(fe25519 *r, const fe25519 *x)
-{
- fe25519 z2;
- fe25519 z9;
- fe25519 z11;
- fe25519 z2_5_0;
- fe25519 z2_10_0;
- fe25519 z2_20_0;
- fe25519 z2_50_0;
- fe25519 z2_100_0;
- fe25519 t;
- int i;
-
- /* 2 */ fe25519_square(&z2,x);
- /* 4 */ fe25519_square(&t,&z2);
- /* 8 */ fe25519_square(&t,&t);
- /* 9 */ fe25519_mul(&z9,&t,x);
- /* 11 */ fe25519_mul(&z11,&z9,&z2);
- /* 22 */ fe25519_square(&t,&z11);
- /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
-
- /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
- /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
- /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
-
- /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
- /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
- /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
-
- /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
- /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
- /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
-
- /* 2^41 - 2^1 */ fe25519_square(&t,&t);
- /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
- /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
-
- /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
- /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
- /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
-
- /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
- /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
- /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
-
- /* 2^201 - 2^1 */ fe25519_square(&t,&t);
- /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
- /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
-
- /* 2^251 - 2^1 */ fe25519_square(&t,&t);
- /* 2^252 - 2^2 */ fe25519_square(&t,&t);
- /* 2^252 - 3 */ fe25519_mul(r,&t,x);
-}
-
-typedef struct
-{
- crypto_uint32 v[32];
-}
-sc25519;
-
-typedef struct
-{
- crypto_uint32 v[16];
-}
-shortsc25519;
+ /* 2^101 - 2^1 */ fe25519_square(&t1,&z2_100_0);
+ /* 2^102 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fe25519_square(&t1,&t0); fe25519_square(&t0,&t1); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t1,&t0,&z2_100_0);
-static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+ /* 2^201 - 2^1 */ fe25519_square(&t0,&t1);
+ /* 2^202 - 2^2 */ fe25519_square(&t1,&t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fe25519_square(&t0,&t1); fe25519_square(&t1,&t0); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t0,&t1,&z2_50_0);
-static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21,
- 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
+ /* 2^251 - 2^1 */ fe25519_square(&t1,&t0);
+ /* 2^252 - 2^2 */ fe25519_square(&t0,&t1);
+ /* 2^253 - 2^3 */ fe25519_square(&t1,&t0);
+ /* 2^254 - 2^4 */ fe25519_square(&t0,&t1);
+ /* 2^255 - 2^5 */ fe25519_square(&t1,&t0);
+ /* 2^255 - 21 */ fe25519_mul(r,&t1,&z11);
+}
-static inline crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+void fe25519_pow2523(fe25519 *r, const fe25519 *x)
+{
+ fe25519 z2;
+ fe25519 z9;
+ fe25519 z11;
+ fe25519 z2_5_0;
+ fe25519 z2_10_0;
+ fe25519 z2_20_0;
+ fe25519 z2_50_0;
+ fe25519 z2_100_0;
+ fe25519 t;
+ int i;
+
+ /* 2 */ fe25519_square(&z2,x);
+ /* 4 */ fe25519_square(&t,&z2);
+ /* 8 */ fe25519_square(&t,&t);
+ /* 9 */ fe25519_mul(&z9,&t,x);
+ /* 11 */ fe25519_mul(&z11,&z9,&z2);
+ /* 22 */ fe25519_square(&t,&z11);
+ /* 2^5 - 2^0 = 31 */ fe25519_mul(&z2_5_0,&t,&z9);
+
+ /* 2^6 - 2^1 */ fe25519_square(&t,&z2_5_0);
+ /* 2^10 - 2^5 */ for (i = 1;i < 5;i++) { fe25519_square(&t,&t); }
+ /* 2^10 - 2^0 */ fe25519_mul(&z2_10_0,&t,&z2_5_0);
+
+ /* 2^11 - 2^1 */ fe25519_square(&t,&z2_10_0);
+ /* 2^20 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^20 - 2^0 */ fe25519_mul(&z2_20_0,&t,&z2_10_0);
+
+ /* 2^21 - 2^1 */ fe25519_square(&t,&z2_20_0);
+ /* 2^40 - 2^20 */ for (i = 1;i < 20;i++) { fe25519_square(&t,&t); }
+ /* 2^40 - 2^0 */ fe25519_mul(&t,&t,&z2_20_0);
+
+ /* 2^41 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^50 - 2^10 */ for (i = 1;i < 10;i++) { fe25519_square(&t,&t); }
+ /* 2^50 - 2^0 */ fe25519_mul(&z2_50_0,&t,&z2_10_0);
+
+ /* 2^51 - 2^1 */ fe25519_square(&t,&z2_50_0);
+ /* 2^100 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^100 - 2^0 */ fe25519_mul(&z2_100_0,&t,&z2_50_0);
+
+ /* 2^101 - 2^1 */ fe25519_square(&t,&z2_100_0);
+ /* 2^200 - 2^100 */ for (i = 1;i < 100;i++) { fe25519_square(&t,&t); }
+ /* 2^200 - 2^0 */ fe25519_mul(&t,&t,&z2_100_0);
+
+ /* 2^201 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^250 - 2^50 */ for (i = 1;i < 50;i++) { fe25519_square(&t,&t); }
+ /* 2^250 - 2^0 */ fe25519_mul(&t,&t,&z2_50_0);
+
+ /* 2^251 - 2^1 */ fe25519_square(&t,&t);
+ /* 2^252 - 2^2 */ fe25519_square(&t,&t);
+ /* 2^252 - 3 */ fe25519_mul(r,&t,x);
+}
+
+static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21,
+ 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
+
+crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
{
- unsigned int x = a;
- x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
- x >>= 31; /* 0: no; 1: yes */
- return x;
+ unsigned int x = a;
+ x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
+ x >>= 31; /* 0: no; 1: yes */
+ return x;
}
/* Reduce coefficients of r before calling reduce_add_sub */
-static inline void reduce_add_sub(sc25519 *r)
-{
- crypto_uint32 pb = 0;
- crypto_uint32 b;
- crypto_uint32 mask;
- int i;
- unsigned char t[32];
-
- for(i=0;i<32;i++)
- {
- pb += m[i];
- b = lt(r->v[i],pb);
- t[i] = r->v[i]-pb+(b<<8);
- pb = b;
- }
- mask = b - 1;
- for(i=0;i<32;i++)
- r->v[i] ^= mask & (r->v[i] ^ t[i]);
+void reduce_add_sub(sc25519 *r)
+{
+ crypto_uint32 pb = 0;
+ crypto_uint32 b;
+ crypto_uint32 mask;
+ int i;
+ unsigned char t[32];
+
+ for(i=0;i<32;i++)
+ {
+ pb += m[i];
+ b = lt(r->v[i],pb);
+ t[i] = r->v[i]-pb+(b<<8);
+ pb = b;
+ }
+ mask = b - 1;
+ for(i=0;i<32;i++)
+ r->v[i] ^= mask & (r->v[i] ^ t[i]);
}
/* Reduce coefficients of x before calling barrett_reduce */
-static inline void barrett_reduce(sc25519 *r, const crypto_uint32 x[64])
-{
- /* See HAC, Alg. 14.42 */
- int i,j;
- crypto_uint32 q2[66];
- crypto_uint32 *q3 = q2 + 33;
- crypto_uint32 r1[33];
- crypto_uint32 r2[33];
- crypto_uint32 carry;
- crypto_uint32 pb = 0;
- crypto_uint32 b;
-
- for (i = 0;i < 66;++i) q2[i] = 0;
- for (i = 0;i < 33;++i) r2[i] = 0;
-
- for(i=0;i<33;i++)
- for(j=0;j<33;j++)
- if(i+j >= 31) q2[i+j] += mu[i]*x[j+31];
- carry = q2[31] >> 8;
- q2[32] += carry;
- carry = q2[32] >> 8;
- q2[33] += carry;
-
- for(i=0;i<33;i++)r1[i] = x[i];
- for(i=0;i<32;i++)
- for(j=0;j<33;j++)
- if(i+j < 33) r2[i+j] += m[i]*q3[j];
-
- for(i=0;i<32;i++)
- {
- carry = r2[i] >> 8;
- r2[i+1] += carry;
- r2[i] &= 0xff;
- }
-
- for(i=0;i<32;i++)
- {
- pb += r2[i];
- b = lt(r1[i],pb);
- r->v[i] = r1[i]-pb+(b<<8);
- pb = b;
- }
-
- /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3
- * If so: Handle it here!
- */
-
- reduce_add_sub(r);
- reduce_add_sub(r);
-}
-
-static inline void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
-{
- int i;
- crypto_uint32 t[64];
- for(i=0;i<32;i++) t[i] = x[i];
- for(i=32;i<64;++i) t[i] = 0;
- barrett_reduce(r, t);
-}
-
-#if 0
-static void shortsc25519_from16bytes(shortsc25519 *r, const unsigned char x[16])
-{
- int i;
- for(i=0;i<16;i++) r->v[i] = x[i];
-}
-#endif
-
-static inline void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
-{
- int i;
- crypto_uint32 t[64];
- for(i=0;i<64;i++) t[i] = x[i];
- barrett_reduce(r, t);
-}
+void barrett_reduce(sc25519 *r, const crypto_uint32 x[64])
+{
+ /* See HAC, Alg. 14.42 */
+ int i,j;
+ crypto_uint32 q2[66];
+ crypto_uint32 *q3 = q2 + 33;
+ crypto_uint32 r1[33];
+ crypto_uint32 r2[33];
+ crypto_uint32 carry;
+ crypto_uint32 pb = 0;
+ crypto_uint32 b;
+
+ for (i = 0;i < 66;++i) q2[i] = 0;
+ for (i = 0;i < 33;++i) r2[i] = 0;
+
+ for(i=0;i<33;i++)
+ for(j=0;j<33;j++)
+ if(i+j >= 31) q2[i+j] += mu[i]*x[j+31];
+ carry = q2[31] >> 8;
+ q2[32] += carry;
+ carry = q2[32] >> 8;
+ q2[33] += carry;
+
+ for(i=0;i<33;i++)r1[i] = x[i];
+ for(i=0;i<32;i++)
+ for(j=0;j<33;j++)
+ if(i+j < 33) r2[i+j] += m[i]*q3[j];
+
+ for(i=0;i<32;i++)
+ {
+ carry = r2[i] >> 8;
+ r2[i+1] += carry;
+ r2[i] &= 0xff;
+ }
-#if 0
-static void sc25519_from_shortsc(sc25519 *r, const shortsc25519 *x)
-{
- int i;
- for(i=0;i<16;i++)
- r->v[i] = x->v[i];
- for(i=0;i<16;i++)
- r->v[16+i] = 0;
-}
-#endif
+ for(i=0;i<32;i++)
+ {
+ pb += r2[i];
+ b = lt(r1[i],pb);
+ r->v[i] = r1[i]-pb+(b<<8);
+ pb = b;
+ }
-static inline void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
-{
- int i;
- for(i=0;i<32;i++) r[i] = x->v[i];
-}
+ /* XXX: Can it really happen that r<0?, See HAC, Alg 14.42, Step 3
+ * If so: Handle it here!
+ */
-#if 0
-static int sc25519_iszero_vartime(const sc25519 *x)
-{
- int i;
- for(i=0;i<32;i++)
- if(x->v[i] != 0) return 0;
- return 1;
+ reduce_add_sub(r);
+ reduce_add_sub(r);
}
-#endif
-#if 0
-static int sc25519_isshort_vartime(const sc25519 *x)
+void sc25519_from32bytes(sc25519 *r, const unsigned char x[32])
{
- int i;
- for(i=31;i>15;i--)
- if(x->v[i] != 0) return 0;
- return 1;
+ int i;
+ crypto_uint32 t[64];
+ for(i=0;i<32;i++) t[i] = x[i];
+ for(i=32;i<64;++i) t[i] = 0;
+ barrett_reduce(r, t);
}
-#endif
-#if 0
-static int sc25519_lt_vartime(const sc25519 *x, const sc25519 *y)
+void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
{
- int i;
- for(i=31;i>=0;i--)
- {
- if(x->v[i] < y->v[i]) return 1;
- if(x->v[i] > y->v[i]) return 0;
- }
- return 0;
+ int i;
+ crypto_uint32 t[64];
+ for(i=0;i<64;i++) t[i] = x[i];
+ barrett_reduce(r, t);
}
-#endif
-static inline void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
{
- int i, carry;
- for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
- for(i=0;i<31;i++)
- {
- carry = r->v[i] >> 8;
- r->v[i+1] += carry;
- r->v[i] &= 0xff;
- }
- reduce_add_sub(r);
+ int i;
+ for(i=0;i<32;i++) r[i] = x->v[i];
}
-#if 0
-static void sc25519_sub_nored(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_add(sc25519 *r, const sc25519 *x, const sc25519 *y)
{
- crypto_uint32 b = 0;
- crypto_uint32 t;
- int i;
- for(i=0;i<32;i++)
- {
- t = x->v[i] - y->v[i] - b;
- r->v[i] = t & 255;
- b = (t >> 8) & 1;
- }
+ int i, carry;
+ for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
+ for(i=0;i<31;i++)
+ {
+ carry = r->v[i] >> 8;
+ r->v[i+1] += carry;
+ r->v[i] &= 0xff;
+ }
+ reduce_add_sub(r);
}
-#endif
-static inline void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
+void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
{
- int i,j,carry;
- crypto_uint32 t[64];
- for(i=0;i<64;i++)t[i] = 0;
+ int i,j,carry;
+ crypto_uint32 t[64];
+ for(i=0;i<64;i++)t[i] = 0;
- for(i=0;i<32;i++)
- for(j=0;j<32;j++)
- t[i+j] += x->v[i] * y->v[j];
+ for(i=0;i<32;i++)
+ for(j=0;j<32;j++)
+ t[i+j] += x->v[i] * y->v[j];
- /* Reduce coefficients */
- for(i=0;i<63;i++)
- {
- carry = t[i] >> 8;
- t[i+1] += carry;
- t[i] &= 0xff;
- }
-
- barrett_reduce(r, t);
-}
-
-#if 0
-static void sc25519_mul_shortsc(sc25519 *r, const sc25519 *x, const shortsc25519 *y)
-{
- sc25519 t;
- sc25519_from_shortsc(&t, y);
- sc25519_mul(r, x, &t);
-}
-#endif
+ /* Reduce coefficients */
+ for(i=0;i<63;i++)
+ {
+ carry = t[i] >> 8;
+ t[i+1] += carry;
+ t[i] &= 0xff;
+ }
-static inline void sc25519_window3(signed char r[85], const sc25519 *s)
-{
- char carry;
- int i;
- for(i=0;i<10;i++)
- {
- r[8*i+0] = s->v[3*i+0] & 7;
- r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
- r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
- r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
- r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
- r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
- r[8*i+5] = (s->v[3*i+1] >> 7) & 7;
- r[8*i+5] ^= (s->v[3*i+2] << 1) & 7;
- r[8*i+6] = (s->v[3*i+2] >> 2) & 7;
- r[8*i+7] = (s->v[3*i+2] >> 5) & 7;
- }
- r[8*i+0] = s->v[3*i+0] & 7;
- r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
- r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
- r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
- r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
- r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
-
- /* Making it signed */
- carry = 0;
- for(i=0;i<84;i++)
- {
- r[i] += carry;
- r[i+1] += r[i] >> 3;
- r[i] &= 7;
- carry = r[i] >> 2;
- r[i] -= carry<<3;
- }
- r[84] += carry;
-}
-
-#if 0
-static void sc25519_window5(signed char r[51], const sc25519 *s)
-{
- char carry;
- int i;
- for(i=0;i<6;i++)
- {
- r[8*i+0] = s->v[5*i+0] & 31;
- r[8*i+1] = (s->v[5*i+0] >> 5) & 31;
- r[8*i+1] ^= (s->v[5*i+1] << 3) & 31;
- r[8*i+2] = (s->v[5*i+1] >> 2) & 31;
- r[8*i+3] = (s->v[5*i+1] >> 7) & 31;
- r[8*i+3] ^= (s->v[5*i+2] << 1) & 31;
- r[8*i+4] = (s->v[5*i+2] >> 4) & 31;
- r[8*i+4] ^= (s->v[5*i+3] << 4) & 31;
- r[8*i+5] = (s->v[5*i+3] >> 1) & 31;
- r[8*i+6] = (s->v[5*i+3] >> 6) & 31;
- r[8*i+6] ^= (s->v[5*i+4] << 2) & 31;
- r[8*i+7] = (s->v[5*i+4] >> 3) & 31;
- }
- r[8*i+0] = s->v[5*i+0] & 31;
- r[8*i+1] = (s->v[5*i+0] >> 5) & 31;
- r[8*i+1] ^= (s->v[5*i+1] << 3) & 31;
- r[8*i+2] = (s->v[5*i+1] >> 2) & 31;
-
- /* Making it signed */
- carry = 0;
- for(i=0;i<50;i++)
- {
- r[i] += carry;
- r[i+1] += r[i] >> 5;
- r[i] &= 31;
- carry = r[i] >> 4;
- r[i] -= carry<<5;
- }
- r[50] += carry;
+ barrett_reduce(r, t);
+}
+
+void sc25519_window3(signed char r[85], const sc25519 *s)
+{
+ char carry;
+ int i;
+ for(i=0;i<10;i++)
+ {
+ r[8*i+0] = s->v[3*i+0] & 7;
+ r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
+ r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
+ r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
+ r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
+ r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
+ r[8*i+5] = (s->v[3*i+1] >> 7) & 7;
+ r[8*i+5] ^= (s->v[3*i+2] << 1) & 7;
+ r[8*i+6] = (s->v[3*i+2] >> 2) & 7;
+ r[8*i+7] = (s->v[3*i+2] >> 5) & 7;
+ }
+ r[8*i+0] = s->v[3*i+0] & 7;
+ r[8*i+1] = (s->v[3*i+0] >> 3) & 7;
+ r[8*i+2] = (s->v[3*i+0] >> 6) & 7;
+ r[8*i+2] ^= (s->v[3*i+1] << 2) & 7;
+ r[8*i+3] = (s->v[3*i+1] >> 1) & 7;
+ r[8*i+4] = (s->v[3*i+1] >> 4) & 7;
+
+ /* Making it signed */
+ carry = 0;
+ for(i=0;i<84;i++)
+ {
+ r[i] += carry;
+ r[i+1] += r[i] >> 3;
+ r[i] &= 7;
+ carry = r[i] >> 2;
+ r[i] -= carry<<3;
+ }
+ r[84] += carry;
}
-#endif
-static inline void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
+void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
{
- int i;
- for(i=0;i<31;i++)
- {
- r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2);
- r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2);
- r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2);
- r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2);
- }
- r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2);
- r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2);
- r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2);
+ int i;
+ for(i=0;i<31;i++)
+ {
+ r[4*i] = ( s1->v[i] & 3) ^ (( s2->v[i] & 3) << 2);
+ r[4*i+1] = ((s1->v[i] >> 2) & 3) ^ (((s2->v[i] >> 2) & 3) << 2);
+ r[4*i+2] = ((s1->v[i] >> 4) & 3) ^ (((s2->v[i] >> 4) & 3) << 2);
+ r[4*i+3] = ((s1->v[i] >> 6) & 3) ^ (((s2->v[i] >> 6) & 3) << 2);
+ }
+ r[124] = ( s1->v[31] & 3) ^ (( s2->v[31] & 3) << 2);
+ r[125] = ((s1->v[31] >> 2) & 3) ^ (((s2->v[31] >> 2) & 3) << 2);
+ r[126] = ((s1->v[31] >> 4) & 3) ^ (((s2->v[31] >> 4) & 3) << 2);
}
-typedef struct
-{
- fe25519 x;
- fe25519 y;
- fe25519 z;
- fe25519 t;
-} ge25519;
-
/* d */
-static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00,
- 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}};
+static const fe25519 ge25519_ecd = {{0xA3, 0x78, 0x59, 0x13, 0xCA, 0x4D, 0xEB, 0x75, 0xAB, 0xD8, 0x41, 0x41, 0x4D, 0x0A, 0x70, 0x00,
+ 0x98, 0xE8, 0x79, 0x77, 0x79, 0x40, 0xC7, 0x8C, 0x73, 0xFE, 0x6F, 0x2B, 0xEE, 0x6C, 0x03, 0x52}};
/* 2*d */
-static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00,
- 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}};
+static const fe25519 ge25519_ec2d = {{0x59, 0xF1, 0xB2, 0x26, 0x94, 0x9B, 0xD6, 0xEB, 0x56, 0xB1, 0x83, 0x82, 0x9A, 0x14, 0xE0, 0x00,
+ 0x30, 0xD1, 0xF3, 0xEE, 0xF2, 0x80, 0x8E, 0x19, 0xE7, 0xFC, 0xDF, 0x56, 0xDC, 0xD9, 0x06, 0x24}};
/* sqrt(-1) */
-static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F,
- 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}};
-
-#define ge25519_p3 ge25519
-
-typedef struct
-{
- fe25519 x;
- fe25519 z;
- fe25519 y;
- fe25519 t;
-} ge25519_p1p1;
-
-typedef struct
-{
- fe25519 x;
- fe25519 y;
- fe25519 z;
-} ge25519_p2;
-
-typedef struct
-{
- fe25519 x;
- fe25519 y;
-} ge25519_aff;
-
+static const fe25519 ge25519_sqrtm1 = {{0xB0, 0xA0, 0x0E, 0x4A, 0x27, 0x1B, 0xEE, 0xC4, 0x78, 0xE4, 0x2F, 0xAD, 0x06, 0x18, 0x43, 0x2F,
+ 0xA7, 0xD7, 0xFB, 0x3D, 0x99, 0x00, 0x4D, 0x2B, 0x0B, 0xDF, 0xC1, 0x4F, 0x80, 0x24, 0x83, 0x2B}};
/* Packed coordinates of the base point */
-static const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69,
- 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}},
- {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
- 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}},
- {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
- {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20,
- 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}};
+static const ge25519 ge25519_base = {{{0x1A, 0xD5, 0x25, 0x8F, 0x60, 0x2D, 0x56, 0xC9, 0xB2, 0xA7, 0x25, 0x95, 0x60, 0xC7, 0x2C, 0x69,
+ 0x5C, 0xDC, 0xD6, 0xFD, 0x31, 0xE2, 0xA4, 0xC0, 0xFE, 0x53, 0x6E, 0xCD, 0xD3, 0x36, 0x69, 0x21}},
+ {{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}},
+ {{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0xA3, 0xDD, 0xB7, 0xA5, 0xB3, 0x8A, 0xDE, 0x6D, 0xF5, 0x52, 0x51, 0x77, 0x80, 0x9F, 0xF0, 0x20,
+ 0x7D, 0xE3, 0xAB, 0x64, 0x8E, 0x4E, 0xEA, 0x66, 0x65, 0x76, 0x8B, 0xD7, 0x0F, 0x5F, 0x87, 0x67}}};
/* Multiples of the base point in affine representation */
static const ge25519_aff ge25519_base_multiples_affine[425] = {
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21}} ,
{{0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66}}},
@@ -1026,7 +887,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xd4, 0xb4, 0xf5, 0x78, 0x48, 0x68, 0xc3, 0x02, 0x04, 0x03, 0x24, 0x67, 0x17, 0xec, 0x16, 0x9f, 0xf7, 0x9e, 0x26, 0x60, 0x8e, 0xa1, 0x26, 0xa1, 0xab, 0x69, 0xee, 0x77, 0xd1, 0xb1, 0x67, 0x12}}},
{{{0x70, 0xf8, 0xc9, 0xc4, 0x57, 0xa6, 0x3a, 0x49, 0x47, 0x15, 0xce, 0x93, 0xc1, 0x9e, 0x73, 0x1a, 0xf9, 0x20, 0x35, 0x7a, 0xb8, 0xd4, 0x25, 0x83, 0x46, 0xf1, 0xcf, 0x56, 0xdb, 0xa8, 0x3d, 0x20}} ,
{{0x2f, 0x11, 0x32, 0xca, 0x61, 0xab, 0x38, 0xdf, 0xf0, 0x0f, 0x2f, 0xea, 0x32, 0x28, 0xf2, 0x4c, 0x6c, 0x71, 0xd5, 0x80, 0x85, 0xb8, 0x0e, 0x47, 0xe1, 0x95, 0x15, 0xcb, 0x27, 0xe8, 0xd0, 0x47}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc8, 0x84, 0xa5, 0x08, 0xbc, 0xfd, 0x87, 0x3b, 0x99, 0x8b, 0x69, 0x80, 0x7b, 0xc6, 0x3a, 0xeb, 0x93, 0xcf, 0x4e, 0xf8, 0x5c, 0x2d, 0x86, 0x42, 0xb6, 0x71, 0xd7, 0x97, 0x5f, 0xe1, 0x42, 0x67}} ,
{{0xb4, 0xb9, 0x37, 0xfc, 0xa9, 0x5b, 0x2f, 0x1e, 0x93, 0xe4, 0x1e, 0x62, 0xfc, 0x3c, 0x78, 0x81, 0x8f, 0xf3, 0x8a, 0x66, 0x09, 0x6f, 0xad, 0x6e, 0x79, 0x73, 0xe5, 0xc9, 0x00, 0x06, 0xd3, 0x21}}},
@@ -1036,7 +897,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf1, 0x79, 0x7b, 0xed, 0x4f, 0x44, 0xb2, 0xe7, 0x08, 0x0d, 0xc2, 0x08, 0x12, 0xd2, 0x9f, 0xdf, 0xcd, 0x93, 0x20, 0x8a, 0xcf, 0x33, 0xca, 0x6d, 0x89, 0xb9, 0x77, 0xc8, 0x93, 0x1b, 0x4e, 0x60}}},
{{{0x26, 0x4f, 0x7e, 0x97, 0xf6, 0x40, 0xdd, 0x4f, 0xfc, 0x52, 0x78, 0xf9, 0x90, 0x31, 0x03, 0xe6, 0x7d, 0x56, 0x39, 0x0b, 0x1d, 0x56, 0x82, 0x85, 0xf9, 0x1a, 0x42, 0x17, 0x69, 0x6c, 0xcf, 0x39}} ,
{{0x69, 0xd2, 0x06, 0x3a, 0x4f, 0x39, 0x2d, 0xf9, 0x38, 0x40, 0x8c, 0x4c, 0xe7, 0x05, 0x12, 0xb4, 0x78, 0x8b, 0xf8, 0xc0, 0xec, 0x93, 0xde, 0x7a, 0x6b, 0xce, 0x2c, 0xe1, 0x0e, 0xa9, 0x34, 0x44}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0b, 0xa4, 0x3c, 0xb0, 0x0f, 0x7a, 0x51, 0xf1, 0x78, 0xd6, 0xd9, 0x6a, 0xfd, 0x46, 0xe8, 0xb8, 0xa8, 0x79, 0x1d, 0x87, 0xf9, 0x90, 0xf2, 0x9c, 0x13, 0x29, 0xf8, 0x0b, 0x20, 0x64, 0xfa, 0x05}} ,
{{0x26, 0x09, 0xda, 0x17, 0xaf, 0x95, 0xd6, 0xfb, 0x6a, 0x19, 0x0d, 0x6e, 0x5e, 0x12, 0xf1, 0x99, 0x4c, 0xaa, 0xa8, 0x6f, 0x79, 0x86, 0xf4, 0x72, 0x28, 0x00, 0x26, 0xf9, 0xea, 0x9e, 0x19, 0x3d}}},
@@ -1046,7 +907,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x1c, 0x59, 0x1c, 0x65, 0x5d, 0x34, 0xa4, 0x09, 0xcd, 0x13, 0x9c, 0x70, 0x7d, 0xb1, 0x2a, 0xc5, 0x88, 0xaf, 0x0b, 0x60, 0xc7, 0x9f, 0x34, 0x8d, 0xd6, 0xb7, 0x7f, 0xea, 0x78, 0x65, 0x8d, 0x77}}},
{{{0x56, 0xa5, 0xc2, 0x0c, 0xdd, 0xbc, 0xb8, 0x20, 0x6d, 0x57, 0x61, 0xb5, 0xfb, 0x78, 0xb5, 0xd4, 0x49, 0x54, 0x90, 0x26, 0xc1, 0xcb, 0xe9, 0xe6, 0xbf, 0xec, 0x1d, 0x4e, 0xed, 0x07, 0x7e, 0x5e}} ,
{{0xc7, 0xf6, 0x6c, 0x56, 0x31, 0x20, 0x14, 0x0e, 0xa8, 0xd9, 0x27, 0xc1, 0x9a, 0x3d, 0x1b, 0x7d, 0x0e, 0x26, 0xd3, 0x81, 0xaa, 0xeb, 0xf5, 0x6b, 0x79, 0x02, 0xf1, 0x51, 0x5c, 0x75, 0x55, 0x0f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0a, 0x34, 0xcd, 0x82, 0x3c, 0x33, 0x09, 0x54, 0xd2, 0x61, 0x39, 0x30, 0x9b, 0xfd, 0xef, 0x21, 0x26, 0xd4, 0x70, 0xfa, 0xee, 0xf9, 0x31, 0x33, 0x73, 0x84, 0xd0, 0xb3, 0x81, 0xbf, 0xec, 0x2e}} ,
{{0xe8, 0x93, 0x8b, 0x00, 0x64, 0xf7, 0x9c, 0xb8, 0x74, 0xe0, 0xe6, 0x49, 0x48, 0x4d, 0x4d, 0x48, 0xb6, 0x19, 0xa1, 0x40, 0xb7, 0xd9, 0x32, 0x41, 0x7c, 0x82, 0x37, 0xa1, 0x2d, 0xdc, 0xd2, 0x54}}},
@@ -1056,7 +917,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xbc, 0x58, 0xe6, 0xc0, 0x95, 0x2a, 0x2a, 0x81, 0x9a, 0x7a, 0xf3, 0xd2, 0x06, 0xbe, 0x48, 0xbc, 0x0c, 0xc5, 0x46, 0xe0, 0x6a, 0xd4, 0xac, 0x0f, 0xd9, 0xcc, 0x82, 0x34, 0x2c, 0xaf, 0xdb, 0x1f}}},
{{{0xf7, 0x17, 0x13, 0xbd, 0xfb, 0xbc, 0xd2, 0xec, 0x45, 0xb3, 0x15, 0x31, 0xe9, 0xaf, 0x82, 0x84, 0x3d, 0x28, 0xc6, 0xfc, 0x11, 0xf5, 0x41, 0xb5, 0x8b, 0xd3, 0x12, 0x76, 0x52, 0xe7, 0x1a, 0x3c}} ,
{{0x4e, 0x36, 0x11, 0x07, 0xa2, 0x15, 0x20, 0x51, 0xc4, 0x2a, 0xc3, 0x62, 0x8b, 0x5e, 0x7f, 0xa6, 0x0f, 0xf9, 0x45, 0x85, 0x6c, 0x11, 0x86, 0xb7, 0x7e, 0xe5, 0xd7, 0xf9, 0xc3, 0x91, 0x1c, 0x05}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xea, 0xd6, 0xde, 0x29, 0x3a, 0x00, 0xb9, 0x02, 0x59, 0xcb, 0x26, 0xc4, 0xba, 0x99, 0xb1, 0x97, 0x2f, 0x8e, 0x00, 0x92, 0x26, 0x4f, 0x52, 0xeb, 0x47, 0x1b, 0x89, 0x8b, 0x24, 0xc0, 0x13, 0x7d}} ,
{{0xd5, 0x20, 0x5b, 0x80, 0xa6, 0x80, 0x20, 0x95, 0xc3, 0xe9, 0x9f, 0x8e, 0x87, 0x9e, 0x1e, 0x9e, 0x7a, 0xc7, 0xcc, 0x75, 0x6c, 0xa5, 0xf1, 0x91, 0x1a, 0xa8, 0x01, 0x2c, 0xab, 0x76, 0xa9, 0x59}}},
@@ -1066,7 +927,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x22, 0xf5, 0x5f, 0x4d, 0x15, 0xef, 0xfc, 0x4e, 0x57, 0x03, 0x36, 0x89, 0xf0, 0xeb, 0x5b, 0x91, 0xd6, 0xe2, 0xca, 0x01, 0xa5, 0xee, 0x52, 0xec, 0xa0, 0x3c, 0x8f, 0x33, 0x90, 0x5a, 0x94, 0x72}}},
{{{0x8a, 0x4b, 0xe7, 0x38, 0xbc, 0xda, 0xc2, 0xb0, 0x85, 0xe1, 0x4a, 0xfe, 0x2d, 0x44, 0x84, 0xcb, 0x20, 0x6b, 0x2d, 0xbf, 0x11, 0x9c, 0xd7, 0xbe, 0xd3, 0x3e, 0x5f, 0xbf, 0x68, 0xbc, 0xa8, 0x07}} ,
{{0x01, 0x89, 0x28, 0x22, 0x6a, 0x78, 0xaa, 0x29, 0x03, 0xc8, 0x74, 0x95, 0x03, 0x3e, 0xdc, 0xbd, 0x07, 0x13, 0xa8, 0xa2, 0x20, 0x2d, 0xb3, 0x18, 0x70, 0x42, 0xfd, 0x7a, 0xc4, 0xd7, 0x49, 0x72}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x02, 0xff, 0x32, 0x2b, 0x5c, 0x93, 0x54, 0x32, 0xe8, 0x57, 0x54, 0x1a, 0x8b, 0x33, 0x60, 0x65, 0xd3, 0x67, 0xa4, 0xc1, 0x26, 0xc4, 0xa4, 0x34, 0x1f, 0x9b, 0xa7, 0xa9, 0xf4, 0xd9, 0x4f, 0x5b}} ,
{{0x46, 0x8d, 0xb0, 0x33, 0x54, 0x26, 0x5b, 0x68, 0xdf, 0xbb, 0xc5, 0xec, 0xc2, 0xf9, 0x3c, 0x5a, 0x37, 0xc1, 0x8e, 0x27, 0x47, 0xaa, 0x49, 0x5a, 0xf8, 0xfb, 0x68, 0x04, 0x23, 0xd1, 0xeb, 0x40}}},
@@ -1076,7 +937,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x60, 0xd6, 0xdd, 0x78, 0xe6, 0xd4, 0x22, 0x42, 0x1f, 0x00, 0xf9, 0xb1, 0x6a, 0x63, 0xe2, 0x92, 0x59, 0xd1, 0x1a, 0xb7, 0x00, 0x54, 0x29, 0xc9, 0xc1, 0xf6, 0x6f, 0x7a, 0xc5, 0x3c, 0x5f, 0x65}}},
{{{0x27, 0x4f, 0xd0, 0x72, 0xb1, 0x11, 0x14, 0x27, 0x15, 0x94, 0x48, 0x81, 0x7e, 0x74, 0xd8, 0x32, 0xd5, 0xd1, 0x11, 0x28, 0x60, 0x63, 0x36, 0x32, 0x37, 0xb5, 0x13, 0x1c, 0xa0, 0x37, 0xe3, 0x74}} ,
{{0xf1, 0x25, 0x4e, 0x11, 0x96, 0x67, 0xe6, 0x1c, 0xc2, 0xb2, 0x53, 0xe2, 0xda, 0x85, 0xee, 0xb2, 0x9f, 0x59, 0xf3, 0xba, 0xbd, 0xfa, 0xcf, 0x6e, 0xf9, 0xda, 0xa4, 0xb3, 0x02, 0x8f, 0x64, 0x08}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x34, 0x94, 0xf2, 0x64, 0x54, 0x47, 0x37, 0x07, 0x40, 0x8a, 0x20, 0xba, 0x4a, 0x55, 0xd7, 0x3f, 0x47, 0xba, 0x25, 0x23, 0x14, 0xb0, 0x2c, 0xe8, 0x55, 0xa8, 0xa6, 0xef, 0x51, 0xbd, 0x6f, 0x6a}} ,
{{0x71, 0xd6, 0x16, 0x76, 0xb2, 0x06, 0xea, 0x79, 0xf5, 0xc4, 0xc3, 0x52, 0x7e, 0x61, 0xd1, 0xe1, 0xad, 0x70, 0x78, 0x1d, 0x16, 0x11, 0xf8, 0x7c, 0x2b, 0xfc, 0x55, 0x9f, 0x52, 0xf8, 0xf5, 0x16}}},
@@ -1086,7 +947,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x02, 0xcc, 0x22, 0x74, 0x4d, 0x19, 0x07, 0xc0, 0xda, 0xb5, 0x76, 0x51, 0x2a, 0xaa, 0xa6, 0x0a, 0x5f, 0x26, 0xd4, 0xbc, 0xaf, 0x48, 0x88, 0x7f, 0x02, 0xbc, 0xf2, 0xe1, 0xcf, 0xe9, 0xdd, 0x15}}},
{{{0xed, 0xb5, 0x9a, 0x8c, 0x9a, 0xdd, 0x27, 0xf4, 0x7f, 0x47, 0xd9, 0x52, 0xa7, 0xcd, 0x65, 0xa5, 0x31, 0x22, 0xed, 0xa6, 0x63, 0x5b, 0x80, 0x4a, 0xad, 0x4d, 0xed, 0xbf, 0xee, 0x49, 0xb3, 0x06}} ,
{{0xf8, 0x64, 0x8b, 0x60, 0x90, 0xe9, 0xde, 0x44, 0x77, 0xb9, 0x07, 0x36, 0x32, 0xc2, 0x50, 0xf5, 0x65, 0xdf, 0x48, 0x4c, 0x37, 0xaa, 0x68, 0xab, 0x9a, 0x1f, 0x3e, 0xff, 0x89, 0x92, 0xa0, 0x07}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x7d, 0x4f, 0x9c, 0x19, 0xc0, 0x4a, 0x31, 0xec, 0xf9, 0xaa, 0xeb, 0xb2, 0x16, 0x9c, 0xa3, 0x66, 0x5f, 0xd1, 0xd4, 0xed, 0xb8, 0x92, 0x1c, 0xab, 0xda, 0xea, 0xd9, 0x57, 0xdf, 0x4c, 0x2a, 0x48}} ,
{{0x4b, 0xb0, 0x4e, 0x6e, 0x11, 0x3b, 0x51, 0xbd, 0x6a, 0xfd, 0xe4, 0x25, 0xa5, 0x5f, 0x11, 0x3f, 0x98, 0x92, 0x51, 0x14, 0xc6, 0x5f, 0x3c, 0x0b, 0xa8, 0xf7, 0xc2, 0x81, 0x43, 0xde, 0x91, 0x73}}},
@@ -1096,7 +957,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x2e, 0x69, 0x98, 0xfd, 0xc6, 0xbd, 0xcc, 0xca, 0xdf, 0x9a, 0x44, 0x7e, 0x9d, 0xca, 0x89, 0x6d, 0xbf, 0x27, 0xc2, 0xf8, 0xcd, 0x46, 0x00, 0x2b, 0xb5, 0x58, 0x4e, 0xb7, 0x89, 0x09, 0xe9, 0x2d}}},
{{{0x54, 0xbe, 0x75, 0xcb, 0x05, 0xb0, 0x54, 0xb7, 0xe7, 0x26, 0x86, 0x4a, 0xfc, 0x19, 0xcf, 0x27, 0x46, 0xd4, 0x22, 0x96, 0x5a, 0x11, 0xe8, 0xd5, 0x1b, 0xed, 0x71, 0xc5, 0x5d, 0xc8, 0xaf, 0x45}} ,
{{0x40, 0x7b, 0x77, 0x57, 0x49, 0x9e, 0x80, 0x39, 0x23, 0xee, 0x81, 0x0b, 0x22, 0xcf, 0xdb, 0x7a, 0x2f, 0x14, 0xb8, 0x57, 0x8f, 0xa1, 0x39, 0x1e, 0x77, 0xfc, 0x0b, 0xa6, 0xbf, 0x8a, 0x0c, 0x6c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x77, 0x3a, 0xd4, 0xd8, 0x27, 0xcf, 0xe8, 0xa1, 0x72, 0x9d, 0xca, 0xdd, 0x0d, 0x96, 0xda, 0x79, 0xed, 0x56, 0x42, 0x15, 0x60, 0xc7, 0x1c, 0x6b, 0x26, 0x30, 0xf6, 0x6a, 0x95, 0x67, 0xf3, 0x0a}} ,
{{0xc5, 0x08, 0xa4, 0x2b, 0x2f, 0xbd, 0x31, 0x81, 0x2a, 0xa6, 0xb6, 0xe4, 0x00, 0x91, 0xda, 0x3d, 0xb2, 0xb0, 0x96, 0xce, 0x8a, 0xd2, 0x8d, 0x70, 0xb3, 0xd3, 0x34, 0x01, 0x90, 0x8d, 0x10, 0x21}}},
@@ -1106,7 +967,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x61, 0x36, 0xd6, 0x08, 0x90, 0xbf, 0xa7, 0x7a, 0x97, 0x6c, 0x0f, 0x84, 0xd5, 0x33, 0x2d, 0x37, 0xc9, 0x6a, 0x80, 0x90, 0x3d, 0x0a, 0xa2, 0xaa, 0xe1, 0xb8, 0x84, 0xba, 0x61, 0x36, 0xdd, 0x69}}},
{{{0x6b, 0xdb, 0x5b, 0x9c, 0xc6, 0x92, 0xbc, 0x23, 0xaf, 0xc5, 0xb8, 0x75, 0xf8, 0x42, 0xfa, 0xd6, 0xb6, 0x84, 0x94, 0x63, 0x98, 0x93, 0x48, 0x78, 0x38, 0xcd, 0xbb, 0x18, 0x34, 0xc3, 0xdb, 0x67}} ,
{{0x96, 0xf3, 0x3a, 0x09, 0x56, 0xb0, 0x6f, 0x7c, 0x51, 0x1e, 0x1b, 0x39, 0x48, 0xea, 0xc9, 0x0c, 0x25, 0xa2, 0x7a, 0xca, 0xe7, 0x92, 0xfc, 0x59, 0x30, 0xa3, 0x89, 0x85, 0xdf, 0x6f, 0x43, 0x38}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x79, 0x84, 0x44, 0x19, 0xbd, 0xe9, 0x54, 0xc4, 0xc0, 0x6e, 0x2a, 0xa8, 0xa8, 0x9b, 0x43, 0xd5, 0x71, 0x22, 0x5f, 0xdc, 0x01, 0xfa, 0xdf, 0xb3, 0xb8, 0x47, 0x4b, 0x0a, 0xa5, 0x44, 0xea, 0x29}} ,
{{0x05, 0x90, 0x50, 0xaf, 0x63, 0x5f, 0x9d, 0x9e, 0xe1, 0x9d, 0x38, 0x97, 0x1f, 0x6c, 0xac, 0x30, 0x46, 0xb2, 0x6a, 0x19, 0xd1, 0x4b, 0xdb, 0xbb, 0x8c, 0xda, 0x2e, 0xab, 0xc8, 0x5a, 0x77, 0x6c}}},
@@ -1116,7 +977,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x5d, 0x04, 0xb3, 0x40, 0x28, 0x95, 0x2d, 0x30, 0x83, 0xec, 0x5e, 0xe4, 0xff, 0x75, 0xfe, 0x79, 0x26, 0x9d, 0x1d, 0x36, 0xcd, 0x0a, 0x15, 0xd2, 0x24, 0x14, 0x77, 0x71, 0xd7, 0x8a, 0x1b, 0x04}}},
{{{0x5d, 0x93, 0xc9, 0xbe, 0xaa, 0x90, 0xcd, 0x9b, 0xfb, 0x73, 0x7e, 0xb0, 0x64, 0x98, 0x57, 0x44, 0x42, 0x41, 0xb1, 0xaf, 0xea, 0xc1, 0xc3, 0x22, 0xff, 0x60, 0x46, 0xcb, 0x61, 0x81, 0x70, 0x61}} ,
{{0x0d, 0x82, 0xb9, 0xfe, 0x21, 0xcd, 0xc4, 0xf5, 0x98, 0x0c, 0x4e, 0x72, 0xee, 0x87, 0x49, 0xf8, 0xa1, 0x95, 0xdf, 0x8f, 0x2d, 0xbd, 0x21, 0x06, 0x7c, 0x15, 0xe8, 0x12, 0x6d, 0x93, 0xd6, 0x38}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x91, 0xf7, 0x51, 0xd9, 0xef, 0x7d, 0x42, 0x01, 0x13, 0xe9, 0xb8, 0x7f, 0xa6, 0x49, 0x17, 0x64, 0x21, 0x80, 0x83, 0x2c, 0x63, 0x4c, 0x60, 0x09, 0x59, 0x91, 0x92, 0x77, 0x39, 0x51, 0xf4, 0x48}} ,
{{0x60, 0xd5, 0x22, 0x83, 0x08, 0x2f, 0xff, 0x99, 0x3e, 0x69, 0x6d, 0x88, 0xda, 0xe7, 0x5b, 0x52, 0x26, 0x31, 0x2a, 0xe5, 0x89, 0xde, 0x68, 0x90, 0xb6, 0x22, 0x5a, 0xbd, 0xd3, 0x85, 0x53, 0x31}}},
@@ -1126,7 +987,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xba, 0x38, 0x02, 0xf7, 0x81, 0x43, 0x83, 0xa3, 0x20, 0x4f, 0x01, 0x3b, 0x8a, 0x04, 0x38, 0x31, 0xc6, 0x0f, 0xc8, 0xdf, 0xd7, 0xfa, 0x2f, 0x88, 0x3f, 0xfc, 0x0c, 0x76, 0xc4, 0xa6, 0x45, 0x72}}},
{{{0xbb, 0x0c, 0xbc, 0x6a, 0xa4, 0x97, 0x17, 0x93, 0x2d, 0x6f, 0xde, 0x72, 0x10, 0x1c, 0x08, 0x2c, 0x0f, 0x80, 0x32, 0x68, 0x27, 0xd4, 0xab, 0xdd, 0xc5, 0x58, 0x61, 0x13, 0x6d, 0x11, 0x1e, 0x4d}} ,
{{0x1a, 0xb9, 0xc9, 0x10, 0xfb, 0x1e, 0x4e, 0xf4, 0x84, 0x4b, 0x8a, 0x5e, 0x7b, 0x4b, 0xe8, 0x43, 0x8c, 0x8f, 0x00, 0xb5, 0x54, 0x13, 0xc5, 0x5c, 0xb6, 0x35, 0x4e, 0x9d, 0xe4, 0x5b, 0x41, 0x6d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x15, 0x7d, 0x12, 0x48, 0x82, 0x14, 0x42, 0xcd, 0x32, 0xd4, 0x4b, 0xc1, 0x72, 0x61, 0x2a, 0x8c, 0xec, 0xe2, 0xf8, 0x24, 0x45, 0x94, 0xe3, 0xbe, 0xdd, 0x67, 0xa8, 0x77, 0x5a, 0xae, 0x5b, 0x4b}} ,
{{0xcb, 0x77, 0x9a, 0x20, 0xde, 0xb8, 0x23, 0xd9, 0xa0, 0x0f, 0x8c, 0x7b, 0xa5, 0xcb, 0xae, 0xb6, 0xec, 0x42, 0x67, 0x0e, 0x58, 0xa4, 0x75, 0x98, 0x21, 0x71, 0x84, 0xb3, 0xe0, 0x76, 0x94, 0x73}}},
@@ -1136,7 +997,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xfb, 0x86, 0xec, 0x33, 0x6b, 0x2e, 0x51, 0x2b, 0xc8, 0xfa, 0x6c, 0x70, 0x47, 0x7e, 0xce, 0x05, 0x0c, 0x71, 0xf3, 0xb4, 0x56, 0xa6, 0xdc, 0xcc, 0x78, 0x07, 0x75, 0xd0, 0xdd, 0xb2, 0x6a}}},
{{{0xc6, 0xef, 0xb9, 0xc0, 0x2b, 0x22, 0x08, 0x1e, 0x71, 0x70, 0xb3, 0x35, 0x9c, 0x7a, 0x01, 0x92, 0x44, 0x9a, 0xf6, 0xb0, 0x58, 0x95, 0xc1, 0x9b, 0x02, 0xed, 0x2d, 0x7c, 0x34, 0x29, 0x49, 0x44}} ,
{{0x45, 0x62, 0x1d, 0x2e, 0xff, 0x2a, 0x1c, 0x21, 0xa4, 0x25, 0x7b, 0x0d, 0x8c, 0x15, 0x39, 0xfc, 0x8f, 0x7c, 0xa5, 0x7d, 0x1e, 0x25, 0xa3, 0x45, 0xd6, 0xab, 0xbd, 0xcb, 0xc5, 0x5e, 0x78, 0x77}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0xd3, 0x42, 0xed, 0x1d, 0x00, 0x3c, 0x15, 0x2c, 0x9c, 0x77, 0x81, 0xd2, 0x73, 0xd1, 0x06, 0xd5, 0xc4, 0x7f, 0x94, 0xbb, 0x92, 0x2d, 0x2c, 0x4b, 0x45, 0x4b, 0xe9, 0x2a, 0x89, 0x6b, 0x2b}} ,
{{0xd2, 0x0c, 0x88, 0xc5, 0x48, 0x4d, 0xea, 0x0d, 0x4a, 0xc9, 0x52, 0x6a, 0x61, 0x79, 0xe9, 0x76, 0xf3, 0x85, 0x52, 0x5c, 0x1b, 0x2c, 0xe1, 0xd6, 0xc4, 0x0f, 0x18, 0x0e, 0x4e, 0xf6, 0x1c, 0x7f}}},
@@ -1146,7 +1007,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x4c, 0xe6, 0xb0, 0xc1, 0xa5, 0x2a, 0x82, 0x09, 0x08, 0xad, 0x79, 0x9c, 0x56, 0xf6, 0xf9, 0xc1, 0xd7, 0x7c, 0x39, 0x7f, 0x93, 0xca, 0x11, 0x55, 0xbf, 0x07, 0x1b, 0x82, 0x29, 0x69, 0x95, 0x5c}}},
{{{0x87, 0xee, 0xa6, 0x56, 0x9e, 0xc2, 0x9a, 0x56, 0x24, 0x42, 0x85, 0x4d, 0x98, 0x31, 0x1e, 0x60, 0x4d, 0x87, 0x85, 0x04, 0xae, 0x46, 0x12, 0xf9, 0x8e, 0x7f, 0xe4, 0x7f, 0xf6, 0x1c, 0x37, 0x01}} ,
{{0x73, 0x4c, 0xb6, 0xc5, 0xc4, 0xe9, 0x6c, 0x85, 0x48, 0x4a, 0x5a, 0xac, 0xd9, 0x1f, 0x43, 0xf8, 0x62, 0x5b, 0xee, 0x98, 0x2a, 0x33, 0x8e, 0x79, 0xce, 0x61, 0x06, 0x35, 0xd8, 0xd7, 0xca, 0x71}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x72, 0xd3, 0xae, 0xa6, 0xca, 0x8f, 0xcd, 0xcc, 0x78, 0x8e, 0x19, 0x4d, 0xa7, 0xd2, 0x27, 0xe9, 0xa4, 0x3c, 0x16, 0x5b, 0x84, 0x80, 0xf9, 0xd0, 0xcc, 0x6a, 0x1e, 0xca, 0x1e, 0x67, 0xbd, 0x63}} ,
{{0x7b, 0x6e, 0x2a, 0xd2, 0x87, 0x48, 0xff, 0xa1, 0xca, 0xe9, 0x15, 0x85, 0xdc, 0xdb, 0x2c, 0x39, 0x12, 0x91, 0xa9, 0x20, 0xaa, 0x4f, 0x29, 0xf4, 0x15, 0x7a, 0xd2, 0xf5, 0x32, 0xcc, 0x60, 0x04}}},
@@ -1156,7 +1017,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xbb, 0x40, 0x5e, 0xbc, 0x16, 0x92, 0x05, 0xc4, 0xc0, 0x4e, 0x72, 0x90, 0x0e, 0xab, 0xcf, 0x8a, 0xed, 0xef, 0xb9, 0x2d, 0x3b, 0xf8, 0x43, 0x5b, 0xba, 0x2d, 0xeb, 0x2f, 0x52, 0xd2, 0xd1, 0x5a}}},
{{{0x40, 0xb4, 0xab, 0xe6, 0xad, 0x9f, 0x46, 0x69, 0x4a, 0xb3, 0x8e, 0xaa, 0xea, 0x9c, 0x8a, 0x20, 0x16, 0x5d, 0x8c, 0x13, 0xbd, 0xf6, 0x1d, 0xc5, 0x24, 0xbd, 0x90, 0x2a, 0x1c, 0xc7, 0x13, 0x3b}} ,
{{0x54, 0xdc, 0x16, 0x0d, 0x18, 0xbe, 0x35, 0x64, 0x61, 0x52, 0x02, 0x80, 0xaf, 0x05, 0xf7, 0xa6, 0x42, 0xd3, 0x8f, 0x2e, 0x79, 0x26, 0xa8, 0xbb, 0xb2, 0x17, 0x48, 0xb2, 0x7a, 0x0a, 0x89, 0x14}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x20, 0xa8, 0x88, 0xe3, 0x91, 0xc0, 0x6e, 0xbb, 0x8a, 0x27, 0x82, 0x51, 0x83, 0xb2, 0x28, 0xa9, 0x83, 0xeb, 0xa6, 0xa9, 0x4d, 0x17, 0x59, 0x22, 0x54, 0x00, 0x50, 0x45, 0xcb, 0x48, 0x4b, 0x18}} ,
{{0x33, 0x7c, 0xe7, 0x26, 0xba, 0x4d, 0x32, 0xfe, 0x53, 0xf4, 0xfa, 0x83, 0xe3, 0xa5, 0x79, 0x66, 0x73, 0xef, 0x80, 0x23, 0x68, 0xc2, 0x60, 0xdd, 0xa9, 0x33, 0xdc, 0x03, 0x7a, 0xe0, 0xe0, 0x3e}}},
@@ -1166,7 +1027,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x6e, 0x44, 0xa3, 0xf9, 0x64, 0xaf, 0xe7, 0x6d, 0x7d, 0xdf, 0x1e, 0xac, 0x04, 0xea, 0x3b, 0x5f, 0x9b, 0xe8, 0x24, 0x9d, 0x0e, 0xe5, 0x2e, 0x3e, 0xdf, 0xa9, 0xf7, 0xd4, 0x50, 0x71, 0xf0, 0x78}}},
{{{0x3e, 0xa8, 0x38, 0xc2, 0x57, 0x56, 0x42, 0x9a, 0xb1, 0xe2, 0xf8, 0x45, 0xaa, 0x11, 0x48, 0x5f, 0x17, 0xc4, 0x54, 0x27, 0xdc, 0x5d, 0xaa, 0xdd, 0x41, 0xbc, 0xdf, 0x81, 0xb9, 0x53, 0xee, 0x52}} ,
{{0xc3, 0xf1, 0xa7, 0x6d, 0xb3, 0x5f, 0x92, 0x6f, 0xcc, 0x91, 0xb8, 0x95, 0x05, 0xdf, 0x3c, 0x64, 0x57, 0x39, 0x61, 0x51, 0xad, 0x8c, 0x38, 0x7b, 0xc8, 0xde, 0x00, 0x34, 0xbe, 0xa1, 0xb0, 0x7e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0x24, 0x1d, 0x8a, 0x67, 0x20, 0xee, 0x42, 0xeb, 0x38, 0xed, 0x0b, 0x8b, 0xcd, 0x46, 0x9d, 0x5e, 0x6b, 0x1e, 0x24, 0x9d, 0x12, 0x05, 0x1a, 0xcc, 0x05, 0x4e, 0x92, 0x38, 0xe1, 0x1f, 0x50}} ,
{{0x4e, 0xee, 0x1c, 0x91, 0xe6, 0x11, 0xbd, 0x8e, 0x55, 0x1a, 0x18, 0x75, 0x66, 0xaf, 0x4d, 0x7b, 0x0f, 0xae, 0x6d, 0x85, 0xca, 0x82, 0x58, 0x21, 0x9c, 0x18, 0xe0, 0xed, 0xec, 0x22, 0x80, 0x2f}}},
@@ -1176,7 +1037,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa1, 0x0c, 0xf3, 0x08, 0x43, 0xd9, 0x24, 0x1e, 0x83, 0xa7, 0xdf, 0x91, 0xca, 0xbd, 0x69, 0x47, 0x8d, 0x1b, 0xe2, 0xb9, 0x4e, 0xb5, 0xe1, 0x76, 0xb3, 0x1c, 0x93, 0x03, 0xce, 0x5f, 0xb3, 0x5a}}},
{{{0x1d, 0xda, 0xe4, 0x61, 0x03, 0x50, 0xa9, 0x8b, 0x68, 0x18, 0xef, 0xb2, 0x1c, 0x84, 0x3b, 0xa2, 0x44, 0x95, 0xa3, 0x04, 0x3b, 0xd6, 0x99, 0x00, 0xaf, 0x76, 0x42, 0x67, 0x02, 0x7d, 0x85, 0x56}} ,
{{0xce, 0x72, 0x0e, 0x29, 0x84, 0xb2, 0x7d, 0xd2, 0x45, 0xbe, 0x57, 0x06, 0xed, 0x7f, 0xcf, 0xed, 0xcd, 0xef, 0x19, 0xd6, 0xbc, 0x15, 0x79, 0x64, 0xd2, 0x18, 0xe3, 0x20, 0x67, 0x3a, 0x54, 0x0b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x52, 0xfd, 0x04, 0xc5, 0xfb, 0x99, 0xe7, 0xe8, 0xfb, 0x8c, 0xe1, 0x42, 0x03, 0xef, 0x9d, 0xd9, 0x9e, 0x4d, 0xf7, 0x80, 0xcf, 0x2e, 0xcc, 0x9b, 0x45, 0xc9, 0x7b, 0x7a, 0xbc, 0x37, 0xa8, 0x52}} ,
{{0x96, 0x11, 0x41, 0x8a, 0x47, 0x91, 0xfe, 0xb6, 0xda, 0x7a, 0x54, 0x63, 0xd1, 0x14, 0x35, 0x05, 0x86, 0x8c, 0xa9, 0x36, 0x3f, 0xf2, 0x85, 0x54, 0x4e, 0x92, 0xd8, 0x85, 0x01, 0x46, 0xd6, 0x50}}},
@@ -1186,7 +1047,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x93, 0x1c, 0x06, 0x2d, 0x66, 0x65, 0x02, 0xa4, 0x97, 0x18, 0xfd, 0x00, 0xe7, 0xab, 0x03, 0xec, 0xce, 0xc1, 0xbf, 0x37, 0xf8, 0x13, 0x53, 0xa5, 0xe5, 0x0c, 0x3a, 0xa8, 0x55, 0xb9, 0xff, 0x68}}},
{{{0xe4, 0xe6, 0x6d, 0x30, 0x7d, 0x30, 0x35, 0xc2, 0x78, 0x87, 0xf9, 0xfc, 0x6b, 0x5a, 0xc3, 0xb7, 0x65, 0xd8, 0x2e, 0xc7, 0xa5, 0x0c, 0xc6, 0xdc, 0x12, 0xaa, 0xd6, 0x4f, 0xc5, 0x38, 0xbc, 0x0e}} ,
{{0xe2, 0x3c, 0x76, 0x86, 0x38, 0xf2, 0x7b, 0x2c, 0x16, 0x78, 0x8d, 0xf5, 0xa4, 0x15, 0xda, 0xdb, 0x26, 0x85, 0xa0, 0x56, 0xdd, 0x1d, 0xe3, 0xb3, 0xfd, 0x40, 0xef, 0xf2, 0xd9, 0xa1, 0xb3, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xdb, 0x49, 0x0e, 0xe6, 0x58, 0x10, 0x7a, 0x52, 0xda, 0xb5, 0x7d, 0x37, 0x6a, 0x3e, 0xa1, 0x78, 0xce, 0xc7, 0x1c, 0x24, 0x23, 0xdb, 0x7d, 0xfb, 0x8c, 0x8d, 0xdc, 0x30, 0x67, 0x69, 0x75, 0x3b}} ,
{{0xa9, 0xea, 0x6d, 0x16, 0x16, 0x60, 0xf4, 0x60, 0x87, 0x19, 0x44, 0x8c, 0x4a, 0x8b, 0x3e, 0xfb, 0x16, 0x00, 0x00, 0x54, 0xa6, 0x9e, 0x9f, 0xef, 0xcf, 0xd9, 0xd2, 0x4c, 0x74, 0x31, 0xd0, 0x34}}},
@@ -1196,7 +1057,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xb0, 0x8b, 0xaa, 0x1e, 0xec, 0xc7, 0xa5, 0x8f, 0x1f, 0x92, 0x04, 0xc6, 0x05, 0xf6, 0xdf, 0xa1, 0xcc, 0x1f, 0x81, 0xf5, 0x0e, 0x9c, 0x57, 0xdc, 0xe3, 0xbb, 0x06, 0x87, 0x1e, 0xfe, 0x23, 0x6c}}},
{{{0xd8, 0x2b, 0x5b, 0x16, 0xea, 0x20, 0xf1, 0xd3, 0x68, 0x8f, 0xae, 0x5b, 0xd0, 0xa9, 0x1a, 0x19, 0xa8, 0x36, 0xfb, 0x2b, 0x57, 0x88, 0x7d, 0x90, 0xd5, 0xa6, 0xf3, 0xdc, 0x38, 0x89, 0x4e, 0x1f}} ,
{{0xcc, 0x19, 0xda, 0x9b, 0x3b, 0x43, 0x48, 0x21, 0x2e, 0x23, 0x4d, 0x3d, 0xae, 0xf8, 0x8c, 0xfc, 0xdd, 0xa6, 0x74, 0x37, 0x65, 0xca, 0xee, 0x1a, 0x19, 0x8e, 0x9f, 0x64, 0x6f, 0x0c, 0x8b, 0x5a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0xb9, 0xc2, 0xf0, 0x72, 0xb8, 0x15, 0x16, 0xcc, 0x8d, 0x3c, 0x6f, 0x25, 0xed, 0xf4, 0x46, 0x2e, 0x0c, 0x60, 0x0f, 0xe2, 0x84, 0x34, 0x55, 0x89, 0x59, 0x34, 0x1b, 0xf5, 0x8d, 0xfe, 0x08}} ,
{{0xf8, 0xab, 0x93, 0xbc, 0x44, 0xba, 0x1b, 0x75, 0x4b, 0x49, 0x6f, 0xd0, 0x54, 0x2e, 0x63, 0xba, 0xb5, 0xea, 0xed, 0x32, 0x14, 0xc9, 0x94, 0xd8, 0xc5, 0xce, 0xf4, 0x10, 0x68, 0xe0, 0x38, 0x27}}},
@@ -1206,7 +1067,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x6c, 0xba, 0x14, 0xc5, 0xea, 0x12, 0x9e, 0x2e, 0x82, 0x63, 0xce, 0x9b, 0x4a, 0xe7, 0x1d, 0xec, 0xf1, 0x2e, 0x51, 0x1c, 0xf4, 0xd0, 0x69, 0x15, 0x42, 0x9d, 0xa3, 0x3f, 0x0e, 0xbf, 0xe9, 0x5c}}},
{{{0xe4, 0x0d, 0xf4, 0xbd, 0xee, 0x31, 0x10, 0xed, 0xcb, 0x12, 0x86, 0xad, 0xd4, 0x2f, 0x90, 0x37, 0x32, 0xc3, 0x0b, 0x73, 0xec, 0x97, 0x85, 0xa4, 0x01, 0x1c, 0x76, 0x35, 0xfe, 0x75, 0xdd, 0x71}} ,
{{0x11, 0xa4, 0x88, 0x9f, 0x3e, 0x53, 0x69, 0x3b, 0x1b, 0xe0, 0xf7, 0xba, 0x9b, 0xad, 0x4e, 0x81, 0x5f, 0xb5, 0x5c, 0xae, 0xbe, 0x67, 0x86, 0x37, 0x34, 0x8e, 0x07, 0x32, 0x45, 0x4a, 0x67, 0x39}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x90, 0x70, 0x58, 0x20, 0x03, 0x1e, 0x67, 0xb2, 0xc8, 0x9b, 0x58, 0xc5, 0xb1, 0xeb, 0x2d, 0x4a, 0xde, 0x82, 0x8c, 0xf2, 0xd2, 0x14, 0xb8, 0x70, 0x61, 0x4e, 0x73, 0xd6, 0x0b, 0x6b, 0x0d, 0x30}} ,
{{0x81, 0xfc, 0x55, 0x5c, 0xbf, 0xa7, 0xc4, 0xbd, 0xe2, 0xf0, 0x4b, 0x8f, 0xe9, 0x7d, 0x99, 0xfa, 0xd3, 0xab, 0xbc, 0xc7, 0x83, 0x2b, 0x04, 0x7f, 0x0c, 0x19, 0x43, 0x03, 0x3d, 0x07, 0xca, 0x40}}},
@@ -1216,7 +1077,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x17, 0x62, 0x6b, 0x14, 0xa1, 0x7c, 0xd0, 0x79, 0x6e, 0xd8, 0x8a, 0xa5, 0x6d, 0x8c, 0x93, 0xd2, 0x3f, 0xec, 0x44, 0x8d, 0x6e, 0x91, 0x01, 0x8c, 0x8f, 0xee, 0x01, 0x8f, 0xc0, 0xb4, 0x85, 0x0e}}},
{{{0x02, 0x3a, 0x70, 0x41, 0xe4, 0x11, 0x57, 0x23, 0xac, 0xe6, 0xfc, 0x54, 0x7e, 0xcd, 0xd7, 0x22, 0xcb, 0x76, 0x9f, 0x20, 0xce, 0xa0, 0x73, 0x76, 0x51, 0x3b, 0xa4, 0xf8, 0xe3, 0x62, 0x12, 0x6c}} ,
{{0x7f, 0x00, 0x9c, 0x26, 0x0d, 0x6f, 0x48, 0x7f, 0x3a, 0x01, 0xed, 0xc5, 0x96, 0xb0, 0x1f, 0x4f, 0xa8, 0x02, 0x62, 0x27, 0x8a, 0x50, 0x8d, 0x9a, 0x8b, 0x52, 0x0f, 0x1e, 0xcf, 0x41, 0x38, 0x19}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf5, 0x6c, 0xd4, 0x2f, 0x0f, 0x69, 0x0f, 0x87, 0x3f, 0x61, 0x65, 0x1e, 0x35, 0x34, 0x85, 0xba, 0x02, 0x30, 0xac, 0x25, 0x3d, 0xe2, 0x62, 0xf1, 0xcc, 0xe9, 0x1b, 0xc2, 0xef, 0x6a, 0x42, 0x57}} ,
{{0x34, 0x1f, 0x2e, 0xac, 0xd1, 0xc7, 0x04, 0x52, 0x32, 0x66, 0xb2, 0x33, 0x73, 0x21, 0x34, 0x54, 0xf7, 0x71, 0xed, 0x06, 0xb0, 0xff, 0xa6, 0x59, 0x6f, 0x8a, 0x4e, 0xfb, 0x02, 0xb0, 0x45, 0x6b}}},
@@ -1226,7 +1087,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xdd, 0xb5, 0x7d, 0xab, 0x5a, 0x21, 0x41, 0x53, 0xbb, 0x17, 0x79, 0x0d, 0xd1, 0xa8, 0x0c, 0x0c, 0x20, 0x88, 0x09, 0xe9, 0x84, 0xe8, 0x25, 0x11, 0x67, 0x7a, 0x8b, 0x1a, 0xe4, 0x5d, 0xe1, 0x5d}}},
{{{0x37, 0xea, 0xfe, 0x65, 0x3b, 0x25, 0xe8, 0xe1, 0xc2, 0xc5, 0x02, 0xa4, 0xbe, 0x98, 0x0a, 0x2b, 0x61, 0xc1, 0x9b, 0xe2, 0xd5, 0x92, 0xe6, 0x9e, 0x7d, 0x1f, 0xca, 0x43, 0x88, 0x8b, 0x2c, 0x59}} ,
{{0xe0, 0xb5, 0x00, 0x1d, 0x2a, 0x6f, 0xaf, 0x79, 0x86, 0x2f, 0xa6, 0x5a, 0x93, 0xd1, 0xfe, 0xae, 0x3a, 0xee, 0xdb, 0x7c, 0x61, 0xbe, 0x7c, 0x01, 0xf9, 0xfe, 0x52, 0xdc, 0xd8, 0x52, 0xa3, 0x42}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x22, 0xaf, 0x13, 0x37, 0xbd, 0x37, 0x71, 0xac, 0x04, 0x46, 0x63, 0xac, 0xa4, 0x77, 0xed, 0x25, 0x38, 0xe0, 0x15, 0xa8, 0x64, 0x00, 0x0d, 0xce, 0x51, 0x01, 0xa9, 0xbc, 0x0f, 0x03, 0x1c, 0x04}} ,
{{0x89, 0xf9, 0x80, 0x07, 0xcf, 0x3f, 0xb3, 0xe9, 0xe7, 0x45, 0x44, 0x3d, 0x2a, 0x7c, 0xe9, 0xe4, 0x16, 0x5c, 0x5e, 0x65, 0x1c, 0xc7, 0x7d, 0xc6, 0x7a, 0xfb, 0x43, 0xee, 0x25, 0x76, 0x46, 0x72}}},
@@ -1236,7 +1097,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0d, 0xac, 0x66, 0x6d, 0xc3, 0x8b, 0xba, 0x16, 0xb5, 0xe2, 0xa0, 0x0d, 0x0c, 0xbd, 0xa4, 0x8e, 0x18, 0x6c, 0xf2, 0xdc, 0xf9, 0xdc, 0x4a, 0x86, 0x25, 0x95, 0x14, 0xcb, 0xd8, 0x1a, 0x04, 0x0f}}},
{{{0x97, 0xa5, 0xdb, 0x8b, 0x2d, 0xaa, 0x42, 0x11, 0x09, 0xf2, 0x93, 0xbb, 0xd9, 0x06, 0x84, 0x4e, 0x11, 0xa8, 0xa0, 0x25, 0x2b, 0xa6, 0x5f, 0xae, 0xc4, 0xb4, 0x4c, 0xc8, 0xab, 0xc7, 0x3b, 0x02}} ,
{{0xee, 0xc9, 0x29, 0x0f, 0xdf, 0x11, 0x85, 0xed, 0xce, 0x0d, 0x62, 0x2c, 0x8f, 0x4b, 0xf9, 0x04, 0xe9, 0x06, 0x72, 0x1d, 0x37, 0x20, 0x50, 0xc9, 0x14, 0xeb, 0xec, 0x39, 0xa7, 0x97, 0x2b, 0x4d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x69, 0xd1, 0x39, 0xbd, 0xfb, 0x33, 0xbe, 0xc4, 0xf0, 0x5c, 0xef, 0xf0, 0x56, 0x68, 0xfc, 0x97, 0x47, 0xc8, 0x72, 0xb6, 0x53, 0xa4, 0x0a, 0x98, 0xa5, 0xb4, 0x37, 0x71, 0xcf, 0x66, 0x50, 0x6d}} ,
{{0x17, 0xa4, 0x19, 0x52, 0x11, 0x47, 0xb3, 0x5c, 0x5b, 0xa9, 0x2e, 0x22, 0xb4, 0x00, 0x52, 0xf9, 0x57, 0x18, 0xb8, 0xbe, 0x5a, 0xe3, 0xab, 0x83, 0xc8, 0x87, 0x0a, 0x2a, 0xd8, 0x8c, 0xbb, 0x54}}},
@@ -1246,7 +1107,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x20, 0xd6, 0xd8, 0x91, 0x54, 0xa7, 0xf3, 0x20, 0x4b, 0x34, 0x06, 0xfa, 0x30, 0xc8, 0x6f, 0x14, 0x10, 0x65, 0x74, 0x13, 0x4e, 0xf0, 0x69, 0x26, 0xce, 0xcf, 0x90, 0xf4, 0xd0, 0xc5, 0xc8, 0x64}}},
{{{0x26, 0xa2, 0x50, 0x02, 0x24, 0x72, 0xf1, 0xf0, 0x4e, 0x2d, 0x93, 0xd5, 0x08, 0xe7, 0xae, 0x38, 0xf7, 0x18, 0xa5, 0x32, 0x34, 0xc2, 0xf0, 0xa6, 0xec, 0xb9, 0x61, 0x7b, 0x64, 0x99, 0xac, 0x71}} ,
{{0x25, 0xcf, 0x74, 0x55, 0x1b, 0xaa, 0xa9, 0x38, 0x41, 0x40, 0xd5, 0x95, 0x95, 0xab, 0x1c, 0x5e, 0xbc, 0x41, 0x7e, 0x14, 0x30, 0xbe, 0x13, 0x89, 0xf4, 0xe5, 0xeb, 0x28, 0xc0, 0xc2, 0x96, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2b, 0x77, 0x45, 0xec, 0x67, 0x76, 0x32, 0x4c, 0xb9, 0xdf, 0x25, 0x32, 0x6b, 0xcb, 0xe7, 0x14, 0x61, 0x43, 0xee, 0xba, 0x9b, 0x71, 0xef, 0xd2, 0x48, 0x65, 0xbb, 0x1b, 0x8a, 0x13, 0x1b, 0x22}} ,
{{0x84, 0xad, 0x0c, 0x18, 0x38, 0x5a, 0xba, 0xd0, 0x98, 0x59, 0xbf, 0x37, 0xb0, 0x4f, 0x97, 0x60, 0x20, 0xb3, 0x9b, 0x97, 0xf6, 0x08, 0x6c, 0xa4, 0xff, 0xfb, 0xb7, 0xfa, 0x95, 0xb2, 0x51, 0x79}}},
@@ -1256,7 +1117,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa9, 0x85, 0xf4, 0xe7, 0xb0, 0x15, 0x33, 0x84, 0x1b, 0x14, 0x1a, 0x02, 0xd9, 0x3b, 0xad, 0x0f, 0x43, 0x6c, 0xea, 0x3e, 0x0f, 0x7e, 0xda, 0xdd, 0x6b, 0x4c, 0x7f, 0x6e, 0xd4, 0x6b, 0xbf, 0x0f}}},
{{{0x47, 0x9f, 0x7c, 0x56, 0x7c, 0x43, 0x91, 0x1c, 0xbb, 0x4e, 0x72, 0x3e, 0x64, 0xab, 0xa0, 0xa0, 0xdf, 0xb4, 0xd8, 0x87, 0x3a, 0xbd, 0xa8, 0x48, 0xc9, 0xb8, 0xef, 0x2e, 0xad, 0x6f, 0x84, 0x4f}} ,
{{0x2d, 0x2d, 0xf0, 0x1b, 0x7e, 0x2a, 0x6c, 0xf8, 0xa9, 0x6a, 0xe1, 0xf0, 0x99, 0xa1, 0x67, 0x9a, 0xd4, 0x13, 0xca, 0xca, 0xba, 0x27, 0x92, 0xaa, 0xa1, 0x5d, 0x50, 0xde, 0xcc, 0x40, 0x26, 0x0a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9f, 0x3e, 0xf2, 0xb2, 0x90, 0xce, 0xdb, 0x64, 0x3e, 0x03, 0xdd, 0x37, 0x36, 0x54, 0x70, 0x76, 0x24, 0xb5, 0x69, 0x03, 0xfc, 0xa0, 0x2b, 0x74, 0xb2, 0x05, 0x0e, 0xcc, 0xd8, 0x1f, 0x6a, 0x1f}} ,
{{0x19, 0x5e, 0x60, 0x69, 0x58, 0x86, 0xa0, 0x31, 0xbd, 0x32, 0xe9, 0x2c, 0x5c, 0xd2, 0x85, 0xba, 0x40, 0x64, 0xa8, 0x74, 0xf8, 0x0e, 0x1c, 0xb3, 0xa9, 0x69, 0xe8, 0x1e, 0x40, 0x64, 0x99, 0x77}}},
@@ -1266,7 +1127,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xca, 0x59, 0x74, 0x1a, 0x11, 0xef, 0x6d, 0xf7, 0x39, 0x5c, 0x3b, 0x1f, 0xfa, 0xe3, 0x40, 0x41, 0x23, 0x9e, 0xf6, 0xd1, 0x21, 0xa2, 0xbf, 0xad, 0x65, 0x42, 0x6b, 0x59, 0x8a, 0xe8, 0xc5, 0x7f}}},
{{{0x64, 0x05, 0x7a, 0x84, 0x4a, 0x13, 0xc3, 0xf6, 0xb0, 0x6e, 0x9a, 0x6b, 0x53, 0x6b, 0x32, 0xda, 0xd9, 0x74, 0x75, 0xc4, 0xba, 0x64, 0x3d, 0x3b, 0x08, 0xdd, 0x10, 0x46, 0xef, 0xc7, 0x90, 0x1f}} ,
{{0x7b, 0x2f, 0x3a, 0xce, 0xc8, 0xa1, 0x79, 0x3c, 0x30, 0x12, 0x44, 0x28, 0xf6, 0xbc, 0xff, 0xfd, 0xf4, 0xc0, 0x97, 0xb0, 0xcc, 0xc3, 0x13, 0x7a, 0xb9, 0x9a, 0x16, 0xe4, 0xcb, 0x4c, 0x34, 0x63}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x07, 0x4e, 0xd3, 0x2d, 0x09, 0x33, 0x0e, 0xd2, 0x0d, 0xbe, 0x3e, 0xe7, 0xe4, 0xaa, 0xb7, 0x00, 0x8b, 0xe8, 0xad, 0xaa, 0x7a, 0x8d, 0x34, 0x28, 0xa9, 0x81, 0x94, 0xc5, 0xe7, 0x42, 0xac, 0x47}} ,
{{0x24, 0x89, 0x7a, 0x8f, 0xb5, 0x9b, 0xf0, 0xc2, 0x03, 0x64, 0xd0, 0x1e, 0xf5, 0xa4, 0xb2, 0xf3, 0x74, 0xe9, 0x1a, 0x16, 0xfd, 0xcb, 0x15, 0xea, 0xeb, 0x10, 0x6c, 0x35, 0xd1, 0xc1, 0xa6, 0x28}}},
@@ -1276,7 +1137,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x19, 0xa6, 0x5a, 0x65, 0x93, 0xc3, 0xb5, 0x31, 0x22, 0x4f, 0xf3, 0xf6, 0x0f, 0xeb, 0x28, 0xc3, 0x7c, 0xeb, 0xce, 0x86, 0xec, 0x67, 0x76, 0x6e, 0x35, 0x45, 0x7b, 0xd8, 0x6b, 0x92, 0x01, 0x65}}},
{{{0x3d, 0xd5, 0x9a, 0x64, 0x73, 0x36, 0xb1, 0xd6, 0x86, 0x98, 0x42, 0x3f, 0x8a, 0xf1, 0xc7, 0xf5, 0x42, 0xa8, 0x9c, 0x52, 0xa8, 0xdc, 0xf9, 0x24, 0x3f, 0x4a, 0xa1, 0xa4, 0x5b, 0xe8, 0x62, 0x1a}} ,
{{0xc5, 0xbd, 0xc8, 0x14, 0xd5, 0x0d, 0xeb, 0xe1, 0xa5, 0xe6, 0x83, 0x11, 0x09, 0x00, 0x1d, 0x55, 0x83, 0x51, 0x7e, 0x75, 0x00, 0x81, 0xb9, 0xcb, 0xd8, 0xc5, 0xe5, 0xa1, 0xd9, 0x17, 0x6d, 0x1f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xea, 0xf9, 0xe4, 0xe9, 0xe1, 0x52, 0x3f, 0x51, 0x19, 0x0d, 0xdd, 0xd9, 0x9d, 0x93, 0x31, 0x87, 0x23, 0x09, 0xd5, 0x83, 0xeb, 0x92, 0x09, 0x76, 0x6e, 0xe3, 0xf8, 0xc0, 0xa2, 0x66, 0xb5, 0x36}} ,
{{0x3a, 0xbb, 0x39, 0xed, 0x32, 0x02, 0xe7, 0x43, 0x7a, 0x38, 0x14, 0x84, 0xe3, 0x44, 0xd2, 0x5e, 0x94, 0xdd, 0x78, 0x89, 0x55, 0x4c, 0x73, 0x9e, 0xe1, 0xe4, 0x3e, 0x43, 0xd0, 0x4a, 0xde, 0x1b}}},
@@ -1286,7 +1147,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x46, 0xda, 0x9e, 0x51, 0x3a, 0xe6, 0xd1, 0xa6, 0xbb, 0x4d, 0x7b, 0x08, 0xbe, 0x8c, 0xd5, 0xf3, 0x3f, 0xfd, 0xf7, 0x44, 0x80, 0x2d, 0x53, 0x4b, 0xd0, 0x87, 0x68, 0xc1, 0xb5, 0xd8, 0xf7, 0x07}}},
{{{0xf4, 0x10, 0x46, 0xbe, 0xb7, 0xd2, 0xd1, 0xce, 0x5e, 0x76, 0xa2, 0xd7, 0x03, 0xdc, 0xe4, 0x81, 0x5a, 0xf6, 0x3c, 0xde, 0xae, 0x7a, 0x9d, 0x21, 0x34, 0xa5, 0xf6, 0xa9, 0x73, 0xe2, 0x8d, 0x60}} ,
{{0xfa, 0x44, 0x71, 0xf6, 0x41, 0xd8, 0xc6, 0x58, 0x13, 0x37, 0xeb, 0x84, 0x0f, 0x96, 0xc7, 0xdc, 0xc8, 0xa9, 0x7a, 0x83, 0xb2, 0x2f, 0x31, 0xb1, 0x1a, 0xd8, 0x98, 0x3f, 0x11, 0xd0, 0x31, 0x3b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x81, 0xd5, 0x34, 0x16, 0x01, 0xa3, 0x93, 0xea, 0x52, 0x94, 0xec, 0x93, 0xb7, 0x81, 0x11, 0x2d, 0x58, 0xf9, 0xb5, 0x0a, 0xaa, 0x4f, 0xf6, 0x2e, 0x3f, 0x36, 0xbf, 0x33, 0x5a, 0xe7, 0xd1, 0x08}} ,
{{0x1a, 0xcf, 0x42, 0xae, 0xcc, 0xb5, 0x77, 0x39, 0xc4, 0x5b, 0x5b, 0xd0, 0x26, 0x59, 0x27, 0xd0, 0x55, 0x71, 0x12, 0x9d, 0x88, 0x3d, 0x9c, 0xea, 0x41, 0x6a, 0xf0, 0x50, 0x93, 0x93, 0xdd, 0x47}}},
@@ -1296,7 +1157,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x13, 0xa8, 0x82, 0x3e, 0xe9, 0x13, 0xad, 0xeb, 0x01, 0xca, 0xcf, 0xda, 0xcd, 0xf7, 0x6c, 0xc7, 0x7a, 0xdc, 0x1e, 0x6e, 0xc8, 0x4e, 0x55, 0x62, 0x80, 0xea, 0x78, 0x0c, 0x86, 0xb9, 0x40, 0x51}}},
{{{0x27, 0xae, 0xd3, 0x0d, 0x4c, 0x8f, 0x34, 0xea, 0x7d, 0x3c, 0xe5, 0x8a, 0xcf, 0x5b, 0x92, 0xd8, 0x30, 0x16, 0xb4, 0xa3, 0x75, 0xff, 0xeb, 0x27, 0xc8, 0x5c, 0x6c, 0xc2, 0xee, 0x6c, 0x21, 0x0b}} ,
{{0xc3, 0xba, 0x12, 0x53, 0x2a, 0xaa, 0x77, 0xad, 0x19, 0x78, 0x55, 0x8a, 0x2e, 0x60, 0x87, 0xc2, 0x6e, 0x91, 0x38, 0x91, 0x3f, 0x7a, 0xc5, 0x24, 0x8f, 0x51, 0xc5, 0xde, 0xb0, 0x53, 0x30, 0x56}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x02, 0xfe, 0x54, 0x12, 0x18, 0xca, 0x7d, 0xa5, 0x68, 0x43, 0xa3, 0x6d, 0x14, 0x2a, 0x6a, 0xa5, 0x8e, 0x32, 0xe7, 0x63, 0x4f, 0xe3, 0xc6, 0x44, 0x3e, 0xab, 0x63, 0xca, 0x17, 0x86, 0x74, 0x3f}} ,
{{0x1e, 0x64, 0xc1, 0x7d, 0x52, 0xdc, 0x13, 0x5a, 0xa1, 0x9c, 0x4e, 0xee, 0x99, 0x28, 0xbb, 0x4c, 0xee, 0xac, 0xa9, 0x1b, 0x89, 0xa2, 0x38, 0x39, 0x7b, 0xc4, 0x0f, 0x42, 0xe6, 0x89, 0xed, 0x0f}}},
@@ -1306,7 +1167,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe7, 0x29, 0xf9, 0x37, 0xd9, 0x46, 0x5a, 0xcd, 0x70, 0xfe, 0x4d, 0x5b, 0xbf, 0xa5, 0xcf, 0x91, 0xf4, 0xef, 0xee, 0x8a, 0x29, 0xd0, 0xe7, 0xc4, 0x25, 0x92, 0x8a, 0xff, 0x36, 0xfc, 0xe4, 0x49}}},
{{{0xbd, 0x00, 0xb9, 0x04, 0x7d, 0x35, 0xfc, 0xeb, 0xd0, 0x0b, 0x05, 0x32, 0x52, 0x7a, 0x89, 0x24, 0x75, 0x50, 0xe1, 0x63, 0x02, 0x82, 0x8e, 0xe7, 0x85, 0x0c, 0xf2, 0x56, 0x44, 0x37, 0x83, 0x25}} ,
{{0x8f, 0xa1, 0xce, 0xcb, 0x60, 0xda, 0x12, 0x02, 0x1e, 0x29, 0x39, 0x2a, 0x03, 0xb7, 0xeb, 0x77, 0x40, 0xea, 0xc9, 0x2b, 0x2c, 0xd5, 0x7d, 0x7e, 0x2c, 0xc7, 0x5a, 0xfd, 0xff, 0xc4, 0xd1, 0x62}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x1d, 0x88, 0x98, 0x5b, 0x4e, 0xfc, 0x41, 0x24, 0x05, 0xe6, 0x50, 0x2b, 0xae, 0x96, 0x51, 0xd9, 0x6b, 0x72, 0xb2, 0x33, 0x42, 0x98, 0x68, 0xbb, 0x10, 0x5a, 0x7a, 0x8c, 0x9d, 0x07, 0xb4, 0x05}} ,
{{0x2f, 0x61, 0x9f, 0xd7, 0xa8, 0x3f, 0x83, 0x8c, 0x10, 0x69, 0x90, 0xe6, 0xcf, 0xd2, 0x63, 0xa3, 0xe4, 0x54, 0x7e, 0xe5, 0x69, 0x13, 0x1c, 0x90, 0x57, 0xaa, 0xe9, 0x53, 0x22, 0x43, 0x29, 0x23}}},
@@ -1316,7 +1177,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf5, 0xff, 0xcc, 0xf0, 0xb4, 0x12, 0x03, 0x5f, 0xc9, 0x84, 0xcb, 0x1d, 0x17, 0xe0, 0xbc, 0xcc, 0x03, 0x62, 0xa9, 0x8b, 0x94, 0xa6, 0xaa, 0x18, 0xcb, 0x27, 0x8d, 0x49, 0xa6, 0x17, 0x15, 0x07}}},
{{{0xd9, 0xb6, 0xd4, 0x9d, 0xd4, 0x6a, 0xaf, 0x70, 0x07, 0x2c, 0x10, 0x9e, 0xbd, 0x11, 0xad, 0xe4, 0x26, 0x33, 0x70, 0x92, 0x78, 0x1c, 0x74, 0x9f, 0x75, 0x60, 0x56, 0xf4, 0x39, 0xa8, 0xa8, 0x62}} ,
{{0x3b, 0xbf, 0x55, 0x35, 0x61, 0x8b, 0x44, 0x97, 0xe8, 0x3a, 0x55, 0xc1, 0xc8, 0x3b, 0xfd, 0x95, 0x29, 0x11, 0x60, 0x96, 0x1e, 0xcb, 0x11, 0x9d, 0xc2, 0x03, 0x8a, 0x1b, 0xc6, 0xd6, 0x45, 0x3d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x7e, 0x0e, 0x50, 0xb2, 0xcc, 0x0d, 0x6b, 0xa6, 0x71, 0x5b, 0x42, 0xed, 0xbd, 0xaf, 0xac, 0xf0, 0xfc, 0x12, 0xa2, 0x3f, 0x4e, 0xda, 0xe8, 0x11, 0xf3, 0x23, 0xe1, 0x04, 0x62, 0x03, 0x1c, 0x4e}} ,
{{0xc8, 0xb1, 0x1b, 0x6f, 0x73, 0x61, 0x3d, 0x27, 0x0d, 0x7d, 0x7a, 0x25, 0x5f, 0x73, 0x0e, 0x2f, 0x93, 0xf6, 0x24, 0xd8, 0x4f, 0x90, 0xac, 0xa2, 0x62, 0x0a, 0xf0, 0x61, 0xd9, 0x08, 0x59, 0x6a}}},
@@ -1326,7 +1187,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8d, 0xa6, 0xd5, 0xac, 0x9d, 0xcc, 0x68, 0x75, 0x7f, 0xc3, 0x4d, 0x4b, 0xdd, 0x6c, 0xbb, 0x11, 0x5a, 0x60, 0xe5, 0xbd, 0x7d, 0x27, 0x8b, 0xda, 0xb4, 0x95, 0xf6, 0x03, 0x27, 0xa4, 0x92, 0x3f}}},
{{{0x22, 0xd6, 0xb5, 0x17, 0x84, 0xbf, 0x12, 0xcc, 0x23, 0x14, 0x4a, 0xdf, 0x14, 0x31, 0xbc, 0xa1, 0xac, 0x6e, 0xab, 0xfa, 0x57, 0x11, 0x53, 0xb3, 0x27, 0xe6, 0xf9, 0x47, 0x33, 0x44, 0x34, 0x1e}} ,
{{0x79, 0xfc, 0xa6, 0xb4, 0x0b, 0x35, 0x20, 0xc9, 0x4d, 0x22, 0x84, 0xc4, 0xa9, 0x20, 0xec, 0x89, 0x94, 0xba, 0x66, 0x56, 0x48, 0xb9, 0x87, 0x7f, 0xca, 0x1e, 0x06, 0xed, 0xa5, 0x55, 0x59, 0x29}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x56, 0xe1, 0xf5, 0xf1, 0xd5, 0xab, 0xa8, 0x2b, 0xae, 0x89, 0xf3, 0xcf, 0x56, 0x9f, 0xf2, 0x4b, 0x31, 0xbc, 0x18, 0xa9, 0x06, 0x5b, 0xbe, 0xb4, 0x61, 0xf8, 0xb2, 0x06, 0x9c, 0x81, 0xab, 0x4c}} ,
{{0x1f, 0x68, 0x76, 0x01, 0x16, 0x38, 0x2b, 0x0f, 0x77, 0x97, 0x92, 0x67, 0x4e, 0x86, 0x6a, 0x8b, 0xe5, 0xe8, 0x0c, 0xf7, 0x36, 0x39, 0xb5, 0x33, 0xe6, 0xcf, 0x5e, 0xbd, 0x18, 0xfb, 0x10, 0x1f}}},
@@ -1336,7 +1197,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xf5, 0x1a, 0x61, 0xf7, 0x37, 0x9d, 0x00, 0xf4, 0xf2, 0x69, 0x6f, 0x4b, 0x01, 0x85, 0x19, 0x45, 0x4d, 0x7f, 0x02, 0x7c, 0x6a, 0x05, 0x47, 0x6c, 0x1f, 0x81, 0x20, 0xd4, 0xe8, 0x50, 0x27, 0x72}}},
{{{0x2c, 0x3a, 0xe5, 0xad, 0xf4, 0xdd, 0x2d, 0xf7, 0x5c, 0x44, 0xb5, 0x5b, 0x21, 0xa3, 0x89, 0x5f, 0x96, 0x45, 0xca, 0x4d, 0xa4, 0x21, 0x99, 0x70, 0xda, 0xc4, 0xc4, 0xa0, 0xe5, 0xf4, 0xec, 0x0a}} ,
{{0x07, 0x68, 0x21, 0x65, 0xe9, 0x08, 0xa0, 0x0b, 0x6a, 0x4a, 0xba, 0xb5, 0x80, 0xaf, 0xd0, 0x1b, 0xc5, 0xf5, 0x4b, 0x73, 0x50, 0x60, 0x2d, 0x71, 0x69, 0x61, 0x0e, 0xc0, 0x20, 0x40, 0x30, 0x19}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0x75, 0x57, 0x3b, 0xeb, 0x5c, 0x14, 0x56, 0x50, 0xc9, 0x4f, 0xb8, 0xb8, 0x1e, 0xa3, 0xf4, 0xab, 0xf5, 0xa9, 0x20, 0x15, 0x94, 0x82, 0xda, 0x96, 0x1c, 0x9b, 0x59, 0x8c, 0xff, 0xf4, 0x51}} ,
{{0xc1, 0x3a, 0x86, 0xd7, 0xb0, 0x06, 0x84, 0x7f, 0x1b, 0xbd, 0xd4, 0x07, 0x78, 0x80, 0x2e, 0xb1, 0xb4, 0xee, 0x52, 0x38, 0xee, 0x9a, 0xf9, 0xf6, 0xf3, 0x41, 0x6e, 0xd4, 0x88, 0x95, 0xac, 0x35}}},
@@ -1346,7 +1207,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x34, 0xe9, 0x59, 0x93, 0x9d, 0x26, 0x80, 0x54, 0xf2, 0xcc, 0x3c, 0xc2, 0x25, 0x85, 0xe3, 0x6a, 0xc1, 0x62, 0x04, 0xa7, 0x08, 0x32, 0x6d, 0xa1, 0x39, 0x84, 0x8a, 0x3b, 0x87, 0x5f, 0x11, 0x13}}},
{{{0xda, 0x03, 0x34, 0x66, 0xc4, 0x0c, 0x73, 0x6e, 0xbc, 0x24, 0xb5, 0xf9, 0x70, 0x81, 0x52, 0xe9, 0xf4, 0x7c, 0x23, 0xdd, 0x9f, 0xb8, 0x46, 0xef, 0x1d, 0x22, 0x55, 0x7d, 0x71, 0xc4, 0x42, 0x33}} ,
{{0xc5, 0x37, 0x69, 0x5b, 0xa8, 0xc6, 0x9d, 0xa4, 0xfc, 0x61, 0x6e, 0x68, 0x46, 0xea, 0xd7, 0x1c, 0x67, 0xd2, 0x7d, 0xfa, 0xf1, 0xcc, 0x54, 0x8d, 0x36, 0x35, 0xc9, 0x00, 0xdf, 0x6c, 0x67, 0x50}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9a, 0x4d, 0x42, 0x29, 0x5d, 0xa4, 0x6b, 0x6f, 0xa8, 0x8a, 0x4d, 0x91, 0x7b, 0xd2, 0xdf, 0x36, 0xef, 0x01, 0x22, 0xc5, 0xcc, 0x8d, 0xeb, 0x58, 0x3d, 0xb3, 0x50, 0xfc, 0x8b, 0x97, 0x96, 0x33}} ,
{{0x93, 0x33, 0x07, 0xc8, 0x4a, 0xca, 0xd0, 0xb1, 0xab, 0xbd, 0xdd, 0xa7, 0x7c, 0xac, 0x3e, 0x45, 0xcb, 0xcc, 0x07, 0x91, 0xbf, 0x35, 0x9d, 0xcb, 0x7d, 0x12, 0x3c, 0x11, 0x59, 0x13, 0xcf, 0x5c}}},
@@ -1356,7 +1217,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8b, 0xa4, 0xd6, 0x50, 0xb4, 0xaa, 0x5d, 0x64, 0x64, 0x76, 0x2e, 0xa1, 0xa6, 0xb3, 0xb8, 0x7c, 0x7a, 0x56, 0xf5, 0x5c, 0x4e, 0x84, 0x5c, 0xfb, 0xdd, 0xca, 0x48, 0x8b, 0x48, 0xb9, 0xba, 0x34}}},
{{{0xc5, 0xe3, 0xe8, 0xae, 0x17, 0x27, 0xe3, 0x64, 0x60, 0x71, 0x47, 0x29, 0x02, 0x0f, 0x92, 0x5d, 0x10, 0x93, 0xc8, 0x0e, 0xa1, 0xed, 0xba, 0xa9, 0x96, 0x1c, 0xc5, 0x76, 0x30, 0xcd, 0xf9, 0x30}} ,
{{0x95, 0xb0, 0xbd, 0x8c, 0xbc, 0xa7, 0x4f, 0x7e, 0xfd, 0x4e, 0x3a, 0xbf, 0x5f, 0x04, 0x79, 0x80, 0x2b, 0x5a, 0x9f, 0x4f, 0x68, 0x21, 0x19, 0x71, 0xc6, 0x20, 0x01, 0x42, 0xaa, 0xdf, 0xae, 0x2c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x90, 0x6e, 0x7e, 0x4b, 0x71, 0x93, 0xc0, 0x72, 0xed, 0xeb, 0x71, 0x24, 0x97, 0x26, 0x9c, 0xfe, 0xcb, 0x3e, 0x59, 0x19, 0xa8, 0x0f, 0x75, 0x7d, 0xbe, 0x18, 0xe6, 0x96, 0x1e, 0x95, 0x70, 0x60}} ,
{{0x89, 0x66, 0x3e, 0x1d, 0x4c, 0x5f, 0xfe, 0xc0, 0x04, 0x43, 0xd6, 0x44, 0x19, 0xb5, 0xad, 0xc7, 0x22, 0xdc, 0x71, 0x28, 0x64, 0xde, 0x41, 0x38, 0x27, 0x8f, 0x2c, 0x6b, 0x08, 0xb8, 0xb8, 0x7b}}},
@@ -1366,7 +1227,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x42, 0xa3, 0xf1, 0xc5, 0xb4, 0x0f, 0xd8, 0xc8, 0x8d, 0x15, 0x31, 0xbd, 0xf8, 0x07, 0x8b, 0xcd, 0x08, 0x8a, 0xfb, 0x18, 0x07, 0xfe, 0x8e, 0x52, 0x86, 0xef, 0xbe, 0xec, 0x49, 0x52, 0x99, 0x08}}},
{{{0x0f, 0xa9, 0xd5, 0x01, 0xaa, 0x48, 0x4f, 0x28, 0x66, 0x32, 0x1a, 0xba, 0x7c, 0xea, 0x11, 0x80, 0x17, 0x18, 0x9b, 0x56, 0x88, 0x25, 0x06, 0x69, 0x12, 0x2c, 0xea, 0x56, 0x69, 0x41, 0x24, 0x19}} ,
{{0xde, 0x21, 0xf0, 0xda, 0x8a, 0xfb, 0xb1, 0xb8, 0xcd, 0xc8, 0x6a, 0x82, 0x19, 0x73, 0xdb, 0xc7, 0xcf, 0x88, 0xeb, 0x96, 0xee, 0x6f, 0xfb, 0x06, 0xd2, 0xcd, 0x7d, 0x7b, 0x12, 0x28, 0x8e, 0x0c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x93, 0x44, 0x97, 0xce, 0x28, 0xff, 0x3a, 0x40, 0xc4, 0xf5, 0xf6, 0x9b, 0xf4, 0x6b, 0x07, 0x84, 0xfb, 0x98, 0xd8, 0xec, 0x8c, 0x03, 0x57, 0xec, 0x49, 0xed, 0x63, 0xb6, 0xaa, 0xff, 0x98, 0x28}} ,
{{0x3d, 0x16, 0x35, 0xf3, 0x46, 0xbc, 0xb3, 0xf4, 0xc6, 0xb6, 0x4f, 0xfa, 0xf4, 0xa0, 0x13, 0xe6, 0x57, 0x45, 0x93, 0xb9, 0xbc, 0xd6, 0x59, 0xe7, 0x77, 0x94, 0x6c, 0xab, 0x96, 0x3b, 0x4f, 0x09}}},
@@ -1376,7 +1237,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xcf, 0xe8, 0x0e, 0x6b, 0x96, 0x7d, 0xed, 0x27, 0xd1, 0x3c, 0xa9, 0xd9, 0x50, 0xa9, 0x98, 0x84, 0x5e, 0x86, 0xef, 0xd6, 0xf0, 0xf8, 0x0e, 0x89, 0x05, 0x2f, 0xd9, 0x5f, 0x15, 0x5f, 0x73, 0x79}}},
{{{0xc8, 0x5c, 0x16, 0xfe, 0xed, 0x9f, 0x26, 0x56, 0xf6, 0x4b, 0x9f, 0xa7, 0x0a, 0x85, 0xfe, 0xa5, 0x8c, 0x87, 0xdd, 0x98, 0xce, 0x4e, 0xc3, 0x58, 0x55, 0xb2, 0x7b, 0x3d, 0xd8, 0x6b, 0xb5, 0x4c}} ,
{{0x65, 0x38, 0xa0, 0x15, 0xfa, 0xa7, 0xb4, 0x8f, 0xeb, 0xc4, 0x86, 0x9b, 0x30, 0xa5, 0x5e, 0x4d, 0xea, 0x8a, 0x9a, 0x9f, 0x1a, 0xd8, 0x5b, 0x53, 0x14, 0x19, 0x25, 0x63, 0xb4, 0x6f, 0x1f, 0x5d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xac, 0x8f, 0xbc, 0x1e, 0x7d, 0x8b, 0x5a, 0x0b, 0x8d, 0xaf, 0x76, 0x2e, 0x71, 0xe3, 0x3b, 0x6f, 0x53, 0x2f, 0x3e, 0x90, 0x95, 0xd4, 0x35, 0x14, 0x4f, 0x8c, 0x3c, 0xce, 0x57, 0x1c, 0x76, 0x49}} ,
{{0xa8, 0x50, 0xe1, 0x61, 0x6b, 0x57, 0x35, 0xeb, 0x44, 0x0b, 0x0c, 0x6e, 0xf9, 0x25, 0x80, 0x74, 0xf2, 0x8f, 0x6f, 0x7a, 0x3e, 0x7f, 0x2d, 0xf3, 0x4e, 0x09, 0x65, 0x10, 0x5e, 0x03, 0x25, 0x32}}},
@@ -1386,7 +1247,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x97, 0x67, 0x9e, 0xeb, 0x6a, 0xf9, 0x6e, 0xd6, 0x73, 0xe8, 0x6b, 0x29, 0xec, 0x63, 0x82, 0x00, 0xa8, 0x99, 0x1c, 0x1d, 0x30, 0xc8, 0x90, 0x52, 0x90, 0xb6, 0x6a, 0x80, 0x4e, 0xff, 0x4b, 0x51}}},
{{{0x0f, 0x7d, 0x63, 0x8c, 0x6e, 0x5c, 0xde, 0x30, 0xdf, 0x65, 0xfa, 0x2e, 0xb0, 0xa3, 0x25, 0x05, 0x54, 0xbd, 0x25, 0xba, 0x06, 0xae, 0xdf, 0x8b, 0xd9, 0x1b, 0xea, 0x38, 0xb3, 0x05, 0x16, 0x09}} ,
{{0xc7, 0x8c, 0xbf, 0x64, 0x28, 0xad, 0xf8, 0xa5, 0x5a, 0x6f, 0xc9, 0xba, 0xd5, 0x7f, 0xd5, 0xd6, 0xbd, 0x66, 0x2f, 0x3d, 0xaa, 0x54, 0xf6, 0xba, 0x32, 0x22, 0x9a, 0x1e, 0x52, 0x05, 0xf4, 0x1d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xaa, 0x1f, 0xbb, 0xeb, 0xfe, 0xe4, 0x87, 0xfc, 0xb1, 0x2c, 0xb7, 0x88, 0xf4, 0xc6, 0xb9, 0xf5, 0x24, 0x46, 0xf2, 0xa5, 0x9f, 0x8f, 0x8a, 0x93, 0x70, 0x69, 0xd4, 0x56, 0xec, 0xfd, 0x06, 0x46}} ,
{{0x4e, 0x66, 0xcf, 0x4e, 0x34, 0xce, 0x0c, 0xd9, 0xa6, 0x50, 0xd6, 0x5e, 0x95, 0xaf, 0xe9, 0x58, 0xfa, 0xee, 0x9b, 0xb8, 0xa5, 0x0f, 0x35, 0xe0, 0x43, 0x82, 0x6d, 0x65, 0xe6, 0xd9, 0x00, 0x0f}}},
@@ -1396,7 +1257,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x99, 0xeb, 0x7c, 0x31, 0x73, 0x64, 0x67, 0x7f, 0x0c, 0x66, 0xaa, 0x8c, 0x69, 0x91, 0xe2, 0x26, 0xd3, 0x23, 0xe2, 0x76, 0x5d, 0x32, 0x52, 0xdf, 0x5d, 0xc5, 0x8f, 0xb7, 0x7c, 0x84, 0xb3, 0x70}}},
{{{0xeb, 0x01, 0xc7, 0x36, 0x97, 0x4e, 0xb6, 0xab, 0x5f, 0x0d, 0x2c, 0xba, 0x67, 0x64, 0x55, 0xde, 0xbc, 0xff, 0xa6, 0xec, 0x04, 0xd3, 0x8d, 0x39, 0x56, 0x5e, 0xee, 0xf8, 0xe4, 0x2e, 0x33, 0x62}} ,
{{0x65, 0xef, 0xb8, 0x9f, 0xc8, 0x4b, 0xa7, 0xfd, 0x21, 0x49, 0x9b, 0x92, 0x35, 0x82, 0xd6, 0x0a, 0x9b, 0xf2, 0x79, 0xf1, 0x47, 0x2f, 0x6a, 0x7e, 0x9f, 0xcf, 0x18, 0x02, 0x3c, 0xfb, 0x1b, 0x3e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2f, 0x8b, 0xc8, 0x40, 0x51, 0xd1, 0xac, 0x1a, 0x0b, 0xe4, 0xa9, 0xa2, 0x42, 0x21, 0x19, 0x2f, 0x7b, 0x97, 0xbf, 0xf7, 0x57, 0x6d, 0x3f, 0x3d, 0x4f, 0x0f, 0xe2, 0xb2, 0x81, 0x00, 0x9e, 0x7b}} ,
{{0x8c, 0x85, 0x2b, 0xc4, 0xfc, 0xf1, 0xab, 0xe8, 0x79, 0x22, 0xc4, 0x84, 0x17, 0x3a, 0xfa, 0x86, 0xa6, 0x7d, 0xf9, 0xf3, 0x6f, 0x03, 0x57, 0x20, 0x4d, 0x79, 0xf9, 0x6e, 0x71, 0x54, 0x38, 0x09}}},
@@ -1406,7 +1267,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x57, 0x82, 0x1c, 0xab, 0xbf, 0x62, 0x70, 0xe8, 0xc4, 0xcf, 0xf0, 0x28, 0x6e, 0x16, 0x3c, 0x08, 0x78, 0x89, 0x85, 0x46, 0x0f, 0xf6, 0x7f, 0xcf, 0xcb, 0x7e, 0xb8, 0x25, 0xe9, 0x5a, 0xfa, 0x03}}},
{{{0xfb, 0x95, 0x92, 0x63, 0x50, 0xfc, 0x62, 0xf0, 0xa4, 0x5e, 0x8c, 0x18, 0xc2, 0x17, 0x24, 0xb7, 0x78, 0xc2, 0xa9, 0xe7, 0x6a, 0x32, 0xd6, 0x29, 0x85, 0xaf, 0xcb, 0x8d, 0x91, 0x13, 0xda, 0x6b}} ,
{{0x36, 0x0a, 0xc2, 0xb6, 0x4b, 0xa5, 0x5d, 0x07, 0x17, 0x41, 0x31, 0x5f, 0x62, 0x46, 0xf8, 0x92, 0xf9, 0x66, 0x48, 0x73, 0xa6, 0x97, 0x0d, 0x7d, 0x88, 0xee, 0x62, 0xb1, 0x03, 0xa8, 0x3f, 0x2c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x4a, 0xb1, 0x70, 0x8a, 0xa9, 0xe8, 0x63, 0x79, 0x00, 0xe2, 0x25, 0x16, 0xca, 0x4b, 0x0f, 0xa4, 0x66, 0xad, 0x19, 0x9f, 0x88, 0x67, 0x0c, 0x8b, 0xc2, 0x4a, 0x5b, 0x2b, 0x6d, 0x95, 0xaf, 0x19}} ,
{{0x8b, 0x9d, 0xb6, 0xcc, 0x60, 0xb4, 0x72, 0x4f, 0x17, 0x69, 0x5a, 0x4a, 0x68, 0x34, 0xab, 0xa1, 0x45, 0x32, 0x3c, 0x83, 0x87, 0x72, 0x30, 0x54, 0x77, 0x68, 0xae, 0xfb, 0xb5, 0x8b, 0x22, 0x5e}}},
@@ -1416,7 +1277,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0x65, 0x03, 0xe5, 0x89, 0xa6, 0x6e, 0xb3, 0x5b, 0x8e, 0xca, 0xeb, 0xfe, 0x22, 0x56, 0x8b, 0x5d, 0x14, 0x4b, 0x4d, 0xf9, 0xbe, 0xb5, 0xf5, 0xe6, 0x5c, 0x7b, 0x8b, 0xf4, 0x13, 0x11, 0x34}}},
{{{0x07, 0xc6, 0x22, 0x15, 0xe2, 0x9c, 0x60, 0xa2, 0x19, 0xd9, 0x27, 0xae, 0x37, 0x4e, 0xa6, 0xc9, 0x80, 0xa6, 0x91, 0x8f, 0x12, 0x49, 0xe5, 0x00, 0x18, 0x47, 0xd1, 0xd7, 0x28, 0x22, 0x63, 0x39}} ,
{{0xe8, 0xe2, 0x00, 0x7e, 0xf2, 0x9e, 0x1e, 0x99, 0x39, 0x95, 0x04, 0xbd, 0x1e, 0x67, 0x7b, 0xb2, 0x26, 0xac, 0xe6, 0xaa, 0xe2, 0x46, 0xd5, 0xe4, 0xe8, 0x86, 0xbd, 0xab, 0x7c, 0x55, 0x59, 0x6f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x24, 0x64, 0x6e, 0x9b, 0x35, 0x71, 0x78, 0xce, 0x33, 0x03, 0x21, 0x33, 0x36, 0xf1, 0x73, 0x9b, 0xb9, 0x15, 0x8b, 0x2c, 0x69, 0xcf, 0x4d, 0xed, 0x4f, 0x4d, 0x57, 0x14, 0x13, 0x82, 0xa4, 0x4d}} ,
{{0x65, 0x6e, 0x0a, 0xa4, 0x59, 0x07, 0x17, 0xf2, 0x6b, 0x4a, 0x1f, 0x6e, 0xf6, 0xb5, 0xbc, 0x62, 0xe4, 0xb6, 0xda, 0xa2, 0x93, 0xbc, 0x29, 0x05, 0xd2, 0xd2, 0x73, 0x46, 0x03, 0x16, 0x40, 0x31}}},
@@ -1426,7 +1287,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x86, 0x17, 0x90, 0xe7, 0xc9, 0x1f, 0x10, 0xa5, 0x6a, 0x2d, 0x39, 0xd0, 0x3b, 0xc4, 0xa6, 0xe9, 0x59, 0x13, 0xda, 0x1a, 0xe6, 0xa0, 0xb9, 0x3c, 0x50, 0xb8, 0x40, 0x7c, 0x15, 0x36, 0x5a, 0x42}}},
{{{0xb4, 0x0b, 0x32, 0xab, 0xdc, 0x04, 0x51, 0x55, 0x21, 0x1e, 0x0b, 0x75, 0x99, 0x89, 0x73, 0x35, 0x3a, 0x91, 0x2b, 0xfe, 0xe7, 0x49, 0xea, 0x76, 0xc1, 0xf9, 0x46, 0xb9, 0x53, 0x02, 0x23, 0x04}} ,
{{0xfc, 0x5a, 0x1e, 0x1d, 0x74, 0x58, 0x95, 0xa6, 0x8f, 0x7b, 0x97, 0x3e, 0x17, 0x3b, 0x79, 0x2d, 0xa6, 0x57, 0xef, 0x45, 0x02, 0x0b, 0x4d, 0x6e, 0x9e, 0x93, 0x8d, 0x2f, 0xd9, 0x9d, 0xdb, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc0, 0xd7, 0x56, 0x97, 0x58, 0x91, 0xde, 0x09, 0x4f, 0x9f, 0xbe, 0x63, 0xb0, 0x83, 0x86, 0x43, 0x5d, 0xbc, 0xe0, 0xf3, 0xc0, 0x75, 0xbf, 0x8b, 0x8e, 0xaa, 0xf7, 0x8b, 0x64, 0x6e, 0xb0, 0x63}} ,
{{0x16, 0xae, 0x8b, 0xe0, 0x9b, 0x24, 0x68, 0x5c, 0x44, 0xc2, 0xd0, 0x08, 0xb7, 0x7b, 0x62, 0xfd, 0x7f, 0xd8, 0xd4, 0xb7, 0x50, 0xfd, 0x2c, 0x1b, 0xbf, 0x41, 0x95, 0xd9, 0x8e, 0xd8, 0x17, 0x1b}}},
@@ -1436,7 +1297,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x02, 0xfb, 0x54, 0xb8, 0x05, 0xf3, 0x81, 0x52, 0x69, 0x34, 0x46, 0x9d, 0x86, 0x76, 0x8f, 0xd7, 0xf8, 0x6a, 0x66, 0xff, 0xe6, 0xa7, 0x90, 0xf7, 0x5e, 0xcd, 0x6a, 0x9b, 0x55, 0xfc, 0x9d, 0x48}}},
{{{0xbd, 0xaa, 0x13, 0xe6, 0xcd, 0x45, 0x4a, 0xa4, 0x59, 0x0a, 0x64, 0xb1, 0x98, 0xd6, 0x34, 0x13, 0x04, 0xe6, 0x97, 0x94, 0x06, 0xcb, 0xd4, 0x4e, 0xbb, 0x96, 0xcd, 0xd1, 0x57, 0xd1, 0xe3, 0x06}} ,
{{0x7a, 0x6c, 0x45, 0x27, 0xc4, 0x93, 0x7f, 0x7d, 0x7c, 0x62, 0x50, 0x38, 0x3a, 0x6b, 0xb5, 0x88, 0xc6, 0xd9, 0xf1, 0x78, 0x19, 0xb9, 0x39, 0x93, 0x3d, 0xc9, 0xe0, 0x9c, 0x3c, 0xce, 0xf5, 0x72}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x24, 0xea, 0x23, 0x7d, 0x56, 0x2c, 0xe2, 0x59, 0x0e, 0x85, 0x60, 0x04, 0x88, 0x5a, 0x74, 0x1e, 0x4b, 0xef, 0x13, 0xda, 0x4c, 0xff, 0x83, 0x45, 0x85, 0x3f, 0x08, 0x95, 0x2c, 0x20, 0x13, 0x1f}} ,
{{0x48, 0x5f, 0x27, 0x90, 0x5c, 0x02, 0x42, 0xad, 0x78, 0x47, 0x5c, 0xb5, 0x7e, 0x08, 0x85, 0x00, 0xfa, 0x7f, 0xfd, 0xfd, 0xe7, 0x09, 0x11, 0xf2, 0x7e, 0x1b, 0x38, 0x6c, 0x35, 0x6d, 0x33, 0x66}}},
@@ -1446,7 +1307,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x4e, 0x3b, 0x69, 0xc8, 0x43, 0x75, 0x51, 0x6c, 0x79, 0x56, 0xe4, 0xcb, 0xf7, 0xa6, 0x51, 0xc2, 0x2c, 0x42, 0x0b, 0xd4, 0x82, 0x20, 0x1c, 0x01, 0x08, 0x66, 0xd7, 0xbf, 0x04, 0x56, 0xfc, 0x02}}},
{{{0x24, 0xe8, 0xb7, 0x60, 0xae, 0x47, 0x80, 0xfc, 0xe5, 0x23, 0xe7, 0xc2, 0xc9, 0x85, 0xe6, 0x98, 0xa0, 0x29, 0x4e, 0xe1, 0x84, 0x39, 0x2d, 0x95, 0x2c, 0xf3, 0x45, 0x3c, 0xff, 0xaf, 0x27, 0x4c}} ,
{{0x6b, 0xa6, 0xf5, 0x4b, 0x11, 0xbd, 0xba, 0x5b, 0x9e, 0xc4, 0xa4, 0x51, 0x1e, 0xbe, 0xd0, 0x90, 0x3a, 0x9c, 0xc2, 0x26, 0xb6, 0x1e, 0xf1, 0x95, 0x7d, 0xc8, 0x6d, 0x52, 0xe6, 0x99, 0x2c, 0x5f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x85, 0xe0, 0x24, 0x32, 0xb4, 0xd1, 0xef, 0xfc, 0x69, 0xa2, 0xbf, 0x8f, 0x72, 0x2c, 0x95, 0xf6, 0xe4, 0x6e, 0x7d, 0x90, 0xf7, 0x57, 0x81, 0xa0, 0xf7, 0xda, 0xef, 0x33, 0x07, 0xe3, 0x6b, 0x78}} ,
{{0x36, 0x27, 0x3e, 0xc6, 0x12, 0x07, 0xab, 0x4e, 0xbe, 0x69, 0x9d, 0xb3, 0xbe, 0x08, 0x7c, 0x2a, 0x47, 0x08, 0xfd, 0xd4, 0xcd, 0x0e, 0x27, 0x34, 0x5b, 0x98, 0x34, 0x2f, 0x77, 0x5f, 0x3a, 0x65}}},
@@ -1456,7 +1317,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x77, 0x5b, 0xe2, 0x37, 0xc7, 0xe1, 0x7c, 0x13, 0x8c, 0x9f, 0x7b, 0x7b, 0x2a, 0xce, 0x42, 0xa3, 0xb9, 0x2a, 0x99, 0xa8, 0xc0, 0xd8, 0x3c, 0x86, 0xb0, 0xfb, 0xe9, 0x76, 0x77, 0xf7, 0xf5, 0x56}}},
{{{0xdf, 0xb3, 0x46, 0x11, 0x6e, 0x13, 0xb7, 0x28, 0x4e, 0x56, 0xdd, 0xf1, 0xac, 0xad, 0x58, 0xc3, 0xf8, 0x88, 0x94, 0x5e, 0x06, 0x98, 0xa1, 0xe4, 0x6a, 0xfb, 0x0a, 0x49, 0x5d, 0x8a, 0xfe, 0x77}} ,
{{0x46, 0x02, 0xf5, 0xa5, 0xaf, 0xc5, 0x75, 0x6d, 0xba, 0x45, 0x35, 0x0a, 0xfe, 0xc9, 0xac, 0x22, 0x91, 0x8d, 0x21, 0x95, 0x33, 0x03, 0xc0, 0x8a, 0x16, 0xf3, 0x39, 0xe0, 0x01, 0x0f, 0x53, 0x3c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x34, 0x75, 0x37, 0x1f, 0x34, 0x4e, 0xa9, 0x1d, 0x68, 0x67, 0xf8, 0x49, 0x98, 0x96, 0xfc, 0x4c, 0x65, 0x97, 0xf7, 0x02, 0x4a, 0x52, 0x6c, 0x01, 0xbd, 0x48, 0xbb, 0x1b, 0xed, 0xa4, 0xe2, 0x53}} ,
{{0x59, 0xd5, 0x9b, 0x5a, 0xa2, 0x90, 0xd3, 0xb8, 0x37, 0x4c, 0x55, 0x82, 0x28, 0x08, 0x0f, 0x7f, 0xaa, 0x81, 0x65, 0xe0, 0x0c, 0x52, 0xc9, 0xa3, 0x32, 0x27, 0x64, 0xda, 0xfd, 0x34, 0x23, 0x5a}}},
@@ -1466,7 +1327,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x1d, 0x29, 0x7a, 0xa1, 0xec, 0x8e, 0xb5, 0xad, 0xea, 0x02, 0x68, 0x60, 0x74, 0x29, 0x1c, 0xa5, 0xcf, 0xc8, 0x3b, 0x7d, 0x8b, 0x2b, 0x7c, 0xad, 0xa4, 0x40, 0x17, 0x51, 0x59, 0x7c, 0x2e, 0x5d}}},
{{{0x0a, 0x6c, 0x4f, 0xbc, 0x3e, 0x32, 0xe7, 0x4a, 0x1a, 0x13, 0xc1, 0x49, 0x38, 0xbf, 0xf7, 0xc2, 0xd3, 0x8f, 0x6b, 0xad, 0x52, 0xf7, 0xcf, 0xbc, 0x27, 0xcb, 0x40, 0x67, 0x76, 0xcd, 0x6d, 0x56}} ,
{{0xe5, 0xb0, 0x27, 0xad, 0xbe, 0x9b, 0xf2, 0xb5, 0x63, 0xde, 0x3a, 0x23, 0x95, 0xb7, 0x0a, 0x7e, 0xf3, 0x9e, 0x45, 0x6f, 0x19, 0x39, 0x75, 0x8f, 0x39, 0x3d, 0x0f, 0xc0, 0x9f, 0xf1, 0xe9, 0x51}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x88, 0xaa, 0x14, 0x24, 0x86, 0x94, 0x11, 0x12, 0x3e, 0x1a, 0xb5, 0xcc, 0xbb, 0xe0, 0x9c, 0xd5, 0x9c, 0x6d, 0xba, 0x58, 0x72, 0x8d, 0xfb, 0x22, 0x7b, 0x9f, 0x7c, 0x94, 0x30, 0xb3, 0x51, 0x21}} ,
{{0xf6, 0x74, 0x3d, 0xf2, 0xaf, 0xd0, 0x1e, 0x03, 0x7c, 0x23, 0x6b, 0xc9, 0xfc, 0x25, 0x70, 0x90, 0xdc, 0x9a, 0xa4, 0xfb, 0x49, 0xfc, 0x3d, 0x0a, 0x35, 0x38, 0x6f, 0xe4, 0x7e, 0x50, 0x01, 0x2a}}},
@@ -1476,7 +1337,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x84, 0x7c, 0xc2, 0xa6, 0x91, 0x23, 0xce, 0xbd, 0xdc, 0xf9, 0xce, 0xd5, 0x75, 0x30, 0x22, 0xe6, 0xf9, 0x43, 0x62, 0x0d, 0xf7, 0x75, 0x9d, 0x7f, 0x8c, 0xff, 0x7d, 0xe4, 0x72, 0xac, 0x9f, 0x1c}}},
{{{0x88, 0xc1, 0x99, 0xd0, 0x3c, 0x1c, 0x5d, 0xb4, 0xef, 0x13, 0x0f, 0x90, 0xb9, 0x36, 0x2f, 0x95, 0x95, 0xc6, 0xdc, 0xde, 0x0a, 0x51, 0xe2, 0x8d, 0xf3, 0xbc, 0x51, 0xec, 0xdf, 0xb1, 0xa2, 0x5f}} ,
{{0x2e, 0x68, 0xa1, 0x23, 0x7d, 0x9b, 0x40, 0x69, 0x85, 0x7b, 0x42, 0xbf, 0x90, 0x4b, 0xd6, 0x40, 0x2f, 0xd7, 0x52, 0x52, 0xb2, 0x21, 0xde, 0x64, 0xbd, 0x88, 0xc3, 0x6d, 0xa5, 0xfa, 0x81, 0x3f}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xfb, 0xfd, 0x47, 0x7b, 0x8a, 0x66, 0x9e, 0x79, 0x2e, 0x64, 0x82, 0xef, 0xf7, 0x21, 0xec, 0xf6, 0xd8, 0x86, 0x09, 0x31, 0x7c, 0xdd, 0x03, 0x6a, 0x58, 0xa0, 0x77, 0xb7, 0x9b, 0x8c, 0x87, 0x1f}} ,
{{0x55, 0x47, 0xe4, 0xa8, 0x3d, 0x55, 0x21, 0x34, 0xab, 0x1d, 0xae, 0xe0, 0xf4, 0xea, 0xdb, 0xc5, 0xb9, 0x58, 0xbf, 0xc4, 0x2a, 0x89, 0x31, 0x1a, 0xf4, 0x2d, 0xe1, 0xca, 0x37, 0x99, 0x47, 0x59}}},
@@ -1486,7 +1347,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x29, 0xac, 0xff, 0x27, 0xe0, 0x59, 0xbe, 0x39, 0x9c, 0x0d, 0x83, 0xd7, 0x10, 0x0b, 0x15, 0xb7, 0xe1, 0xc2, 0x2c, 0x30, 0x73, 0x80, 0x3a, 0x7d, 0x5d, 0xab, 0x58, 0x6b, 0xc1, 0xf0, 0xf4, 0x22}}},
{{{0xfe, 0x7f, 0xfb, 0x35, 0x7d, 0xc6, 0x01, 0x23, 0x28, 0xc4, 0x02, 0xac, 0x1f, 0x42, 0xb4, 0x9d, 0xfc, 0x00, 0x94, 0xa5, 0xee, 0xca, 0xda, 0x97, 0x09, 0x41, 0x77, 0x87, 0x5d, 0x7b, 0x87, 0x78}} ,
{{0xf5, 0xfb, 0x90, 0x2d, 0x81, 0x19, 0x9e, 0x2f, 0x6d, 0x85, 0x88, 0x8c, 0x40, 0x5c, 0x77, 0x41, 0x4d, 0x01, 0x19, 0x76, 0x60, 0xe8, 0x4c, 0x48, 0xe4, 0x33, 0x83, 0x32, 0x6c, 0xb4, 0x41, 0x03}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xff, 0x10, 0xc2, 0x09, 0x4f, 0x6e, 0xf4, 0xd2, 0xdf, 0x7e, 0xca, 0x7b, 0x1c, 0x1d, 0xba, 0xa3, 0xb6, 0xda, 0x67, 0x33, 0xd4, 0x87, 0x36, 0x4b, 0x11, 0x20, 0x05, 0xa6, 0x29, 0xc1, 0x87, 0x17}} ,
{{0xf6, 0x96, 0xca, 0x2f, 0xda, 0x38, 0xa7, 0x1b, 0xfc, 0xca, 0x7d, 0xfe, 0x08, 0x89, 0xe2, 0x47, 0x2b, 0x6a, 0x5d, 0x4b, 0xfa, 0xa1, 0xb4, 0xde, 0xb6, 0xc2, 0x31, 0x51, 0xf5, 0xe0, 0xa4, 0x0b}}},
@@ -1496,7 +1357,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0c, 0x0e, 0xf3, 0xba, 0xf0, 0xe5, 0xba, 0xb2, 0x57, 0x77, 0xc6, 0x20, 0x9b, 0x89, 0x24, 0xbe, 0xf2, 0x9c, 0x8a, 0xba, 0x69, 0xc1, 0xf1, 0xb0, 0x4f, 0x2a, 0x05, 0x9a, 0xee, 0x10, 0x7e, 0x36}}},
{{{0x3f, 0x26, 0xe9, 0x40, 0xe9, 0x03, 0xad, 0x06, 0x69, 0x91, 0xe0, 0xd1, 0x89, 0x60, 0x84, 0x79, 0xde, 0x27, 0x6d, 0xe6, 0x76, 0xbd, 0xea, 0xe6, 0xae, 0x48, 0xc3, 0x67, 0xc0, 0x57, 0xcd, 0x2f}} ,
{{0x7f, 0xc1, 0xdc, 0xb9, 0xc7, 0xbc, 0x86, 0x3d, 0x55, 0x4b, 0x28, 0x7a, 0xfb, 0x4d, 0xc7, 0xf8, 0xbc, 0x67, 0x2a, 0x60, 0x4d, 0x8f, 0x07, 0x0b, 0x1a, 0x17, 0xbf, 0xfa, 0xac, 0xa7, 0x3d, 0x1a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x91, 0x3f, 0xed, 0x5e, 0x18, 0x78, 0x3f, 0x23, 0x2c, 0x0d, 0x8c, 0x44, 0x00, 0xe8, 0xfb, 0xe9, 0x8e, 0xd6, 0xd1, 0x36, 0x58, 0x57, 0x9e, 0xae, 0x4b, 0x5c, 0x0b, 0x07, 0xbc, 0x6b, 0x55, 0x2b}} ,
{{0x6f, 0x4d, 0x17, 0xd7, 0xe1, 0x84, 0xd9, 0x78, 0xb1, 0x90, 0xfd, 0x2e, 0xb3, 0xb5, 0x19, 0x3f, 0x1b, 0xfa, 0xc0, 0x68, 0xb3, 0xdd, 0x00, 0x2e, 0x89, 0xbd, 0x7e, 0x80, 0x32, 0x13, 0xa0, 0x7b}}},
@@ -1506,7 +1367,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x01, 0xb8, 0xd6, 0x16, 0x67, 0xa0, 0x15, 0xb9, 0xe1, 0x58, 0xa4, 0xa7, 0x31, 0x37, 0x77, 0x2f, 0x8b, 0x12, 0x9f, 0xf4, 0x3f, 0xc7, 0x36, 0x66, 0xd2, 0xa8, 0x56, 0xf7, 0x7f, 0x74, 0xc6, 0x41}}},
{{{0x5d, 0xf8, 0xb4, 0xa8, 0x30, 0xdd, 0xcc, 0x38, 0xa5, 0xd3, 0xca, 0xd8, 0xd1, 0xf8, 0xb2, 0x31, 0x91, 0xd4, 0x72, 0x05, 0x57, 0x4a, 0x3b, 0x82, 0x4a, 0xc6, 0x68, 0x20, 0xe2, 0x18, 0x41, 0x61}} ,
{{0x19, 0xd4, 0x8d, 0x47, 0x29, 0x12, 0x65, 0xb0, 0x11, 0x78, 0x47, 0xb5, 0xcb, 0xa3, 0xa5, 0xfa, 0x05, 0x85, 0x54, 0xa9, 0x33, 0x97, 0x8d, 0x2b, 0xc2, 0xfe, 0x99, 0x35, 0x28, 0xe5, 0xeb, 0x63}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xb1, 0x3f, 0x3f, 0xef, 0xd8, 0xf4, 0xfc, 0xb3, 0xa0, 0x60, 0x50, 0x06, 0x2b, 0x29, 0x52, 0x70, 0x15, 0x0b, 0x24, 0x24, 0xf8, 0x5f, 0x79, 0x18, 0xcc, 0xff, 0x89, 0x99, 0x84, 0xa1, 0xae, 0x13}} ,
{{0x44, 0x1f, 0xb8, 0xc2, 0x01, 0xc1, 0x30, 0x19, 0x55, 0x05, 0x60, 0x10, 0xa4, 0x6c, 0x2d, 0x67, 0x70, 0xe5, 0x25, 0x1b, 0xf2, 0xbf, 0xdd, 0xfb, 0x70, 0x2b, 0xa1, 0x8c, 0x9c, 0x94, 0x84, 0x08}}},
@@ -1516,7 +1377,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xdc, 0xff, 0x15, 0x61, 0x2f, 0x4a, 0x2f, 0x62, 0xf2, 0x04, 0x2f, 0xb5, 0x0c, 0xb7, 0x1e, 0x3f, 0x74, 0x1a, 0x0f, 0xd7, 0xea, 0xcd, 0xd9, 0x7d, 0xf6, 0x12, 0x0e, 0x2f, 0xdb, 0x5a, 0x3b, 0x16}}},
{{{0x1b, 0x37, 0x47, 0xe3, 0xf5, 0x9e, 0xea, 0x2c, 0x2a, 0xe7, 0x82, 0x36, 0xf4, 0x1f, 0x81, 0x47, 0x92, 0x4b, 0x69, 0x0e, 0x11, 0x8c, 0x5d, 0x53, 0x5b, 0x81, 0x27, 0x08, 0xbc, 0xa0, 0xae, 0x25}} ,
{{0x69, 0x32, 0xa1, 0x05, 0x11, 0x42, 0x00, 0xd2, 0x59, 0xac, 0x4d, 0x62, 0x8b, 0x13, 0xe2, 0x50, 0x5d, 0xa0, 0x9d, 0x9b, 0xfd, 0xbb, 0x12, 0x41, 0x75, 0x41, 0x9e, 0xcc, 0xdc, 0xc7, 0xdc, 0x5d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd9, 0xe3, 0x38, 0x06, 0x46, 0x70, 0x82, 0x5e, 0x28, 0x49, 0x79, 0xff, 0x25, 0xd2, 0x4e, 0x29, 0x8d, 0x06, 0xb0, 0x23, 0xae, 0x9b, 0x66, 0xe4, 0x7d, 0xc0, 0x70, 0x91, 0xa3, 0xfc, 0xec, 0x4e}} ,
{{0x62, 0x12, 0x37, 0x6a, 0x30, 0xf6, 0x1e, 0xfb, 0x14, 0x5c, 0x0d, 0x0e, 0xb7, 0x81, 0x6a, 0xe7, 0x08, 0x05, 0xac, 0xaa, 0x38, 0x46, 0xe2, 0x73, 0xea, 0x4b, 0x07, 0x81, 0x43, 0x7c, 0x9e, 0x5e}}},
@@ -1526,7 +1387,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe5, 0x44, 0xec, 0x06, 0x9d, 0x90, 0x79, 0x9f, 0xd3, 0xe0, 0x79, 0xaf, 0x8f, 0x10, 0xfd, 0xdd, 0x04, 0xae, 0x27, 0x97, 0x46, 0x33, 0x79, 0xea, 0xb8, 0x4e, 0xca, 0x5a, 0x59, 0x57, 0xe1, 0x0e}}},
{{{0x1a, 0xda, 0xf3, 0xa5, 0x41, 0x43, 0x28, 0xfc, 0x7e, 0xe7, 0x71, 0xea, 0xc6, 0x3b, 0x59, 0xcc, 0x2e, 0xd3, 0x40, 0xec, 0xb3, 0x13, 0x6f, 0x44, 0xcd, 0x13, 0xb2, 0x37, 0xf2, 0x6e, 0xd9, 0x1c}} ,
{{0xe3, 0xdb, 0x60, 0xcd, 0x5c, 0x4a, 0x18, 0x0f, 0xef, 0x73, 0x36, 0x71, 0x8c, 0xf6, 0x11, 0xb4, 0xd8, 0xce, 0x17, 0x5e, 0x4f, 0x26, 0x77, 0x97, 0x5f, 0xcb, 0xef, 0x91, 0xeb, 0x6a, 0x62, 0x7a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x18, 0x4a, 0xa2, 0x97, 0x08, 0x81, 0x2d, 0x83, 0xc4, 0xcc, 0xf0, 0x83, 0x7e, 0xec, 0x0d, 0x95, 0x4c, 0x5b, 0xfb, 0xfa, 0x98, 0x80, 0x4a, 0x66, 0x56, 0x0c, 0x51, 0xb3, 0xf2, 0x04, 0x5d, 0x27}} ,
{{0x3b, 0xb9, 0xb8, 0x06, 0x5a, 0x2e, 0xfe, 0xc3, 0x82, 0x37, 0x9c, 0xa3, 0x11, 0x1f, 0x9c, 0xa6, 0xda, 0x63, 0x48, 0x9b, 0xad, 0xde, 0x2d, 0xa6, 0xbc, 0x6e, 0x32, 0xda, 0x27, 0x65, 0xdd, 0x57}}},
@@ -1536,7 +1397,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa4, 0x4b, 0x62, 0x4c, 0xe6, 0xfd, 0x72, 0x07, 0xf2, 0x81, 0xfc, 0xf2, 0xbd, 0x12, 0x7c, 0x68, 0x76, 0x2a, 0xba, 0xf5, 0x65, 0xb1, 0x1f, 0x17, 0x0a, 0x38, 0xb0, 0xbf, 0xc0, 0xf8, 0xf4, 0x2a}}},
{{{0x55, 0x60, 0x55, 0x5b, 0xe4, 0x1d, 0x71, 0x4c, 0x9d, 0x5b, 0x9f, 0x70, 0xa6, 0x85, 0x9a, 0x2c, 0xa0, 0xe2, 0x32, 0x48, 0xce, 0x9e, 0x2a, 0xa5, 0x07, 0x3b, 0xc7, 0x6c, 0x86, 0x77, 0xde, 0x3c}} ,
{{0xf7, 0x18, 0x7a, 0x96, 0x7e, 0x43, 0x57, 0xa9, 0x55, 0xfc, 0x4e, 0xb6, 0x72, 0x00, 0xf2, 0xe4, 0xd7, 0x52, 0xd3, 0xd3, 0xb6, 0x85, 0xf6, 0x71, 0xc7, 0x44, 0x3f, 0x7f, 0xd7, 0xb3, 0xf2, 0x79}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x46, 0xca, 0xa7, 0x55, 0x7b, 0x79, 0xf3, 0xca, 0x5a, 0x65, 0xf6, 0xed, 0x50, 0x14, 0x7b, 0xe4, 0xc4, 0x2a, 0x65, 0x9e, 0xe2, 0xf9, 0xca, 0xa7, 0x22, 0x26, 0x53, 0xcb, 0x21, 0x5b, 0xa7, 0x31}} ,
{{0x90, 0xd7, 0xc5, 0x26, 0x08, 0xbd, 0xb0, 0x53, 0x63, 0x58, 0xc3, 0x31, 0x5e, 0x75, 0x46, 0x15, 0x91, 0xa6, 0xf8, 0x2f, 0x1a, 0x08, 0x65, 0x88, 0x2f, 0x98, 0x04, 0xf1, 0x7c, 0x6e, 0x00, 0x77}}},
@@ -1546,7 +1407,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x68, 0x0e, 0x6f, 0x97, 0xba, 0x70, 0xbb, 0xa3, 0x0e, 0xe5, 0x0b, 0x12, 0xf4, 0xa2, 0xdc, 0x47, 0xf8, 0xe6, 0xd0, 0x23, 0x6c, 0x33, 0xa8, 0x99, 0x46, 0x6e, 0x0f, 0x44, 0xba, 0x76, 0x48, 0x0f}}},
{{{0xa3, 0x2a, 0x61, 0x37, 0xe2, 0x59, 0x12, 0x0e, 0x27, 0xba, 0x64, 0x43, 0xae, 0xc0, 0x42, 0x69, 0x79, 0xa4, 0x1e, 0x29, 0x8b, 0x15, 0xeb, 0xf8, 0xaf, 0xd4, 0xa2, 0x68, 0x33, 0xb5, 0x7a, 0x24}} ,
{{0x2c, 0x19, 0x33, 0xdd, 0x1b, 0xab, 0xec, 0x01, 0xb0, 0x23, 0xf8, 0x42, 0x2b, 0x06, 0x88, 0xea, 0x3d, 0x2d, 0x00, 0x2a, 0x78, 0x45, 0x4d, 0x38, 0xed, 0x2e, 0x2e, 0x44, 0x49, 0xed, 0xcb, 0x33}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xa0, 0x68, 0xe8, 0x41, 0x8f, 0x91, 0xf8, 0x11, 0x13, 0x90, 0x2e, 0xa7, 0xab, 0x30, 0xef, 0xad, 0xa0, 0x61, 0x00, 0x88, 0xef, 0xdb, 0xce, 0x5b, 0x5c, 0xbb, 0x62, 0xc8, 0x56, 0xf9, 0x00, 0x73}} ,
{{0x3f, 0x60, 0xc1, 0x82, 0x2d, 0xa3, 0x28, 0x58, 0x24, 0x9e, 0x9f, 0xe3, 0x70, 0xcc, 0x09, 0x4e, 0x1a, 0x3f, 0x11, 0x11, 0x15, 0x07, 0x3c, 0xa4, 0x41, 0xe0, 0x65, 0xa3, 0x0a, 0x41, 0x6d, 0x11}}},
@@ -1556,7 +1417,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xb4, 0x18, 0x47, 0x79, 0xcb, 0xd4, 0x5a, 0x07, 0x14, 0x0f, 0xa0, 0xd5, 0xac, 0xd0, 0x41, 0x40, 0xab, 0x61, 0x23, 0xe5, 0x2a, 0x2a, 0x6f, 0xf7, 0xa8, 0xd4, 0x76, 0xef, 0xe7, 0x45, 0x6c}}},
{{{0xa1, 0x5e, 0x60, 0x4f, 0xfb, 0xe1, 0x70, 0x6a, 0x1f, 0x55, 0x4f, 0x09, 0xb4, 0x95, 0x33, 0x36, 0xc6, 0x81, 0x01, 0x18, 0x06, 0x25, 0x27, 0xa4, 0xb4, 0x24, 0xa4, 0x86, 0x03, 0x4c, 0xac, 0x02}} ,
{{0x77, 0x38, 0xde, 0xd7, 0x60, 0x48, 0x07, 0xf0, 0x74, 0xa8, 0xff, 0x54, 0xe5, 0x30, 0x43, 0xff, 0x77, 0xfb, 0x21, 0x07, 0xff, 0xb2, 0x07, 0x6b, 0xe4, 0xe5, 0x30, 0xfc, 0x19, 0x6c, 0xa3, 0x01}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x13, 0xc5, 0x2c, 0xac, 0xd3, 0x83, 0x82, 0x7c, 0x29, 0xf7, 0x05, 0xa5, 0x00, 0xb6, 0x1f, 0x86, 0x55, 0xf4, 0xd6, 0x2f, 0x0c, 0x99, 0xd0, 0x65, 0x9b, 0x6b, 0x46, 0x0d, 0x43, 0xf8, 0x16, 0x28}} ,
{{0x1e, 0x7f, 0xb4, 0x74, 0x7e, 0xb1, 0x89, 0x4f, 0x18, 0x5a, 0xab, 0x64, 0x06, 0xdf, 0x45, 0x87, 0xe0, 0x6a, 0xc6, 0xf0, 0x0e, 0xc9, 0x24, 0x35, 0x38, 0xea, 0x30, 0x54, 0xb4, 0xc4, 0x52, 0x54}}},
@@ -1566,7 +1427,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x32, 0xad, 0xae, 0x85, 0x58, 0x43, 0xb8, 0xb1, 0xe6, 0x3e, 0x00, 0x9c, 0x78, 0x88, 0x56, 0xdb, 0x9c, 0xfc, 0x79, 0xf6, 0xf9, 0x41, 0x5f, 0xb7, 0xbc, 0x11, 0xf9, 0x20, 0x36, 0x1c, 0x53, 0x2b}}},
{{{0x5a, 0x20, 0x5b, 0xa1, 0xa5, 0x44, 0x91, 0x24, 0x02, 0x63, 0x12, 0x64, 0xb8, 0x55, 0xf6, 0xde, 0x2c, 0xdb, 0x47, 0xb8, 0xc6, 0x0a, 0xc3, 0x00, 0x78, 0x93, 0xd8, 0xf5, 0xf5, 0x18, 0x28, 0x0a}} ,
{{0xd6, 0x1b, 0x9a, 0x6c, 0xe5, 0x46, 0xea, 0x70, 0x96, 0x8d, 0x4e, 0x2a, 0x52, 0x21, 0x26, 0x4b, 0xb1, 0xbb, 0x0f, 0x7c, 0xa9, 0x9b, 0x04, 0xbb, 0x51, 0x08, 0xf1, 0x9a, 0xa4, 0x76, 0x7c, 0x18}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xfa, 0x94, 0xf7, 0x40, 0xd0, 0xd7, 0xeb, 0xa9, 0x82, 0x36, 0xd5, 0x15, 0xb9, 0x33, 0x7a, 0xbf, 0x8a, 0xf2, 0x63, 0xaa, 0x37, 0xf5, 0x59, 0xac, 0xbd, 0xbb, 0x32, 0x36, 0xbe, 0x73, 0x99, 0x38}} ,
{{0x2c, 0xb3, 0xda, 0x7a, 0xd8, 0x3d, 0x99, 0xca, 0xd2, 0xf4, 0xda, 0x99, 0x8e, 0x4f, 0x98, 0xb7, 0xf4, 0xae, 0x3e, 0x9f, 0x8e, 0x35, 0x60, 0xa4, 0x33, 0x75, 0xa4, 0x04, 0x93, 0xb1, 0x6b, 0x4d}}},
@@ -1576,7 +1437,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x53, 0xcf, 0xc2, 0xa1, 0xad, 0x6a, 0xf3, 0xcd, 0x8f, 0xc9, 0xde, 0x1c, 0xf8, 0x6c, 0x8f, 0xf8, 0x76, 0x42, 0xe7, 0xfe, 0xb2, 0x72, 0x21, 0x0a, 0x66, 0x74, 0x8f, 0xb7, 0xeb, 0xe4, 0x6f, 0x01}}},
{{{0x22, 0x8c, 0x6b, 0xbe, 0xfc, 0x4d, 0x70, 0x62, 0x6e, 0x52, 0x77, 0x99, 0x88, 0x7e, 0x7b, 0x57, 0x7a, 0x0d, 0xfe, 0xdc, 0x72, 0x92, 0xf1, 0x68, 0x1d, 0x97, 0xd7, 0x7c, 0x8d, 0x53, 0x10, 0x37}} ,
{{0x53, 0x88, 0x77, 0x02, 0xca, 0x27, 0xa8, 0xe5, 0x45, 0xe2, 0xa8, 0x48, 0x2a, 0xab, 0x18, 0xca, 0xea, 0x2d, 0x2a, 0x54, 0x17, 0x37, 0x32, 0x09, 0xdc, 0xe0, 0x4a, 0xb7, 0x7d, 0x82, 0x10, 0x7d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x8a, 0x64, 0x1e, 0x14, 0x0a, 0x57, 0xd4, 0xda, 0x5c, 0x96, 0x9b, 0x01, 0x4c, 0x67, 0xbf, 0x8b, 0x30, 0xfe, 0x08, 0xdb, 0x0d, 0xd5, 0xa8, 0xd7, 0x09, 0x11, 0x85, 0xa2, 0xd3, 0x45, 0xfb, 0x7e}} ,
{{0xda, 0x8c, 0xc2, 0xd0, 0xac, 0x18, 0xe8, 0x52, 0x36, 0xd4, 0x21, 0xa3, 0xdd, 0x57, 0x22, 0x79, 0xb7, 0xf8, 0x71, 0x9d, 0xc6, 0x91, 0x70, 0x86, 0x56, 0xbf, 0xa1, 0x11, 0x8b, 0x19, 0xe1, 0x0f}}},
@@ -1586,7 +1447,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x62, 0x4f, 0xd0, 0x75, 0x77, 0xba, 0x76, 0x77, 0xd7, 0xb8, 0xd8, 0x92, 0x6f, 0x98, 0x34, 0x3d, 0xd6, 0x4e, 0x1c, 0x0f, 0xf0, 0x8f, 0x2e, 0xf1, 0xb3, 0xbd, 0xb1, 0xb9, 0xec, 0x99, 0xb4, 0x07}}},
{{{0x60, 0x57, 0x2e, 0x9a, 0x72, 0x1d, 0x6b, 0x6e, 0x58, 0x33, 0x24, 0x8c, 0x48, 0x39, 0x46, 0x8e, 0x89, 0x6a, 0x88, 0x51, 0x23, 0x62, 0xb5, 0x32, 0x09, 0x36, 0xe3, 0x57, 0xf5, 0x98, 0xde, 0x6f}} ,
{{0x8b, 0x2c, 0x00, 0x48, 0x4a, 0xf9, 0x5b, 0x87, 0x69, 0x52, 0xe5, 0x5b, 0xd1, 0xb1, 0xe5, 0x25, 0x25, 0xe0, 0x9c, 0xc2, 0x13, 0x44, 0xe8, 0xb9, 0x0a, 0x70, 0xad, 0xbd, 0x0f, 0x51, 0x94, 0x69}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xa2, 0xdc, 0xab, 0xa9, 0x25, 0x2d, 0xac, 0x5f, 0x03, 0x33, 0x08, 0xe7, 0x7e, 0xfe, 0x95, 0x36, 0x3c, 0x5b, 0x3a, 0xd3, 0x05, 0x82, 0x1c, 0x95, 0x2d, 0xd8, 0x77, 0x7e, 0x02, 0xd9, 0x5b, 0x70}} ,
{{0xc2, 0xfe, 0x1b, 0x0c, 0x67, 0xcd, 0xd6, 0xe0, 0x51, 0x8e, 0x2c, 0xe0, 0x79, 0x88, 0xf0, 0xcf, 0x41, 0x4a, 0xad, 0x23, 0xd4, 0x46, 0xca, 0x94, 0xa1, 0xc3, 0xeb, 0x28, 0x06, 0xfa, 0x17, 0x14}}},
@@ -1596,7 +1457,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x64, 0xe9, 0x28, 0xc7, 0xa4, 0xcf, 0x2a, 0xf9, 0x90, 0x64, 0x72, 0x2c, 0x8b, 0xeb, 0xec, 0xa0, 0xf2, 0x7d, 0x35, 0xb5, 0x90, 0x4d, 0x7f, 0x5b, 0x4a, 0x49, 0xe4, 0xb8, 0x3b, 0xc8, 0xa1, 0x2f}}},
{{{0x8b, 0xc5, 0xcc, 0x3d, 0x69, 0xa6, 0xa1, 0x18, 0x44, 0xbc, 0x4d, 0x77, 0x37, 0xc7, 0x86, 0xec, 0x0c, 0xc9, 0xd6, 0x44, 0xa9, 0x23, 0x27, 0xb9, 0x03, 0x34, 0xa7, 0x0a, 0xd5, 0xc7, 0x34, 0x37}} ,
{{0xf9, 0x7e, 0x3e, 0x66, 0xee, 0xf9, 0x99, 0x28, 0xff, 0xad, 0x11, 0xd8, 0xe2, 0x66, 0xc5, 0xcd, 0x0f, 0x0d, 0x0b, 0x6a, 0xfc, 0x7c, 0x24, 0xa8, 0x4f, 0xa8, 0x5e, 0x80, 0x45, 0x8b, 0x6c, 0x41}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xef, 0x1e, 0xec, 0xf7, 0x8d, 0x77, 0xf2, 0xea, 0xdb, 0x60, 0x03, 0x21, 0xc0, 0xff, 0x5e, 0x67, 0xc3, 0x71, 0x0b, 0x21, 0xb4, 0x41, 0xa0, 0x68, 0x38, 0xc6, 0x01, 0xa3, 0xd3, 0x51, 0x3c, 0x3c}} ,
{{0x92, 0xf8, 0xd6, 0x4b, 0xef, 0x42, 0x13, 0xb2, 0x4a, 0xc4, 0x2e, 0x72, 0x3f, 0xc9, 0x11, 0xbd, 0x74, 0x02, 0x0e, 0xf5, 0x13, 0x9d, 0x83, 0x1a, 0x1b, 0xd5, 0x54, 0xde, 0xc4, 0x1e, 0x16, 0x6c}}},
@@ -1606,7 +1467,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x7a, 0x8d, 0xa1, 0x5d, 0x70, 0x5d, 0x51, 0x27, 0xee, 0x30, 0x65, 0x56, 0x95, 0x46, 0xde, 0xbd, 0x03, 0x75, 0xb4, 0x57, 0x59, 0x89, 0xeb, 0x02, 0x9e, 0xcc, 0x89, 0x19, 0xa7, 0xcb, 0x17, 0x67}}},
{{{0x6a, 0xeb, 0xfc, 0x9a, 0x9a, 0x10, 0xce, 0xdb, 0x3a, 0x1c, 0x3c, 0x6a, 0x9d, 0xea, 0x46, 0xbc, 0x45, 0x49, 0xac, 0xe3, 0x41, 0x12, 0x7c, 0xf0, 0xf7, 0x4f, 0xf9, 0xf7, 0xff, 0x2c, 0x89, 0x04}} ,
{{0x30, 0x31, 0x54, 0x1a, 0x46, 0xca, 0xe6, 0xc6, 0xcb, 0xe2, 0xc3, 0xc1, 0x8b, 0x75, 0x81, 0xbe, 0xee, 0xf8, 0xa3, 0x11, 0x1c, 0x25, 0xa3, 0xa7, 0x35, 0x51, 0x55, 0xe2, 0x25, 0xaa, 0xe2, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xb4, 0x48, 0x10, 0x9f, 0x8a, 0x09, 0x76, 0xfa, 0xf0, 0x7a, 0xb0, 0x70, 0xf7, 0x83, 0x80, 0x52, 0x84, 0x2b, 0x26, 0xa2, 0xc4, 0x5d, 0x4f, 0xba, 0xb1, 0xc8, 0x40, 0x0d, 0x78, 0x97, 0xc4, 0x60}} ,
{{0xd4, 0xb1, 0x6c, 0x08, 0xc7, 0x40, 0x38, 0x73, 0x5f, 0x0b, 0xf3, 0x76, 0x5d, 0xb2, 0xa5, 0x2f, 0x57, 0x57, 0x07, 0xed, 0x08, 0xa2, 0x6c, 0x4f, 0x08, 0x02, 0xb5, 0x0e, 0xee, 0x44, 0xfa, 0x22}}},
@@ -1616,7 +1477,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xff, 0x76, 0x03, 0xc5, 0x4b, 0x89, 0x99, 0x70, 0x00, 0x59, 0x70, 0x9c, 0xd5, 0xd9, 0x11, 0x89, 0x5a, 0x46, 0xfe, 0xef, 0xdc, 0xd9, 0x55, 0x2b, 0x45, 0xa7, 0xb0, 0x2d, 0xfb, 0x24, 0xc2, 0x29}}},
{{{0x38, 0x06, 0xf8, 0x0b, 0xac, 0x82, 0xc4, 0x97, 0x2b, 0x90, 0xe0, 0xf7, 0xa8, 0xab, 0x6c, 0x08, 0x80, 0x66, 0x90, 0x46, 0xf7, 0x26, 0x2d, 0xf8, 0xf1, 0xc4, 0x6b, 0x4a, 0x82, 0x98, 0x8e, 0x37}} ,
{{0x8e, 0xb4, 0xee, 0xb8, 0xd4, 0x3f, 0xb2, 0x1b, 0xe0, 0x0a, 0x3d, 0x75, 0x34, 0x28, 0xa2, 0x8e, 0xc4, 0x92, 0x7b, 0xfe, 0x60, 0x6e, 0x6d, 0xb8, 0x31, 0x1d, 0x62, 0x0d, 0x78, 0x14, 0x42, 0x11}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x5e, 0xa8, 0xd8, 0x04, 0x9b, 0x73, 0xc9, 0xc9, 0xdc, 0x0d, 0x73, 0xbf, 0x0a, 0x0a, 0x73, 0xff, 0x18, 0x1f, 0x9c, 0x51, 0xaa, 0xc6, 0xf1, 0x83, 0x25, 0xfd, 0xab, 0xa3, 0x11, 0xd3, 0x01, 0x24}} ,
{{0x4d, 0xe3, 0x7e, 0x38, 0x62, 0x5e, 0x64, 0xbb, 0x2b, 0x53, 0xb5, 0x03, 0x68, 0xc4, 0xf2, 0x2b, 0x5a, 0x03, 0x32, 0x99, 0x4a, 0x41, 0x9a, 0xe1, 0x1a, 0xae, 0x8c, 0x48, 0xf3, 0x24, 0x32, 0x65}}},
@@ -1626,7 +1487,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x7c, 0x31, 0x33, 0xcc, 0xe4, 0xcf, 0x6c, 0xff, 0x80, 0x47, 0x77, 0xd1, 0xd8, 0xe9, 0x69, 0x97, 0x98, 0x7f, 0x20, 0x57, 0x1d, 0x1d, 0x4f, 0x08, 0x27, 0xc8, 0x35, 0x57, 0x40, 0xc6, 0x21, 0x0c}}},
{{{0xd2, 0x8e, 0x9b, 0xfa, 0x42, 0x8e, 0xdf, 0x8f, 0xc7, 0x86, 0xf9, 0xa4, 0xca, 0x70, 0x00, 0x9d, 0x21, 0xbf, 0xec, 0x57, 0x62, 0x30, 0x58, 0x8c, 0x0d, 0x35, 0xdb, 0x5d, 0x8b, 0x6a, 0xa0, 0x5a}} ,
{{0xc1, 0x58, 0x7c, 0x0d, 0x20, 0xdd, 0x11, 0x26, 0x5f, 0x89, 0x3b, 0x97, 0x58, 0xf8, 0x8b, 0xe3, 0xdf, 0x32, 0xe2, 0xfc, 0xd8, 0x67, 0xf2, 0xa5, 0x37, 0x1e, 0x6d, 0xec, 0x7c, 0x27, 0x20, 0x79}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xd0, 0xe9, 0xc0, 0xfa, 0x95, 0x45, 0x23, 0x96, 0xf1, 0x2c, 0x79, 0x25, 0x14, 0xce, 0x40, 0x14, 0x44, 0x2c, 0x36, 0x50, 0xd9, 0x63, 0x56, 0xb7, 0x56, 0x3b, 0x9e, 0xa7, 0xef, 0x89, 0xbb, 0x0e}} ,
{{0xce, 0x7f, 0xdc, 0x0a, 0xcc, 0x82, 0x1c, 0x0a, 0x78, 0x71, 0xe8, 0x74, 0x8d, 0x01, 0x30, 0x0f, 0xa7, 0x11, 0x4c, 0xdf, 0x38, 0xd7, 0xa7, 0x0d, 0xf8, 0x48, 0x52, 0x00, 0x80, 0x7b, 0x5f, 0x0e}}},
@@ -1636,7 +1497,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x50, 0x5b, 0xff, 0xa9, 0xb2, 0xf1, 0xf1, 0x78, 0xcf, 0x14, 0xa4, 0xa9, 0xfc, 0x09, 0x46, 0x94, 0x54, 0x65, 0x0d, 0x9c, 0x5f, 0x72, 0x21, 0xe2, 0x97, 0xa5, 0x2d, 0x81, 0xce, 0x4a, 0x5f, 0x79}}},
{{{0x3d, 0x5f, 0x5c, 0xd2, 0xbc, 0x7d, 0x77, 0x0e, 0x2a, 0x6d, 0x22, 0x45, 0x84, 0x06, 0xc4, 0xdd, 0xc6, 0xa6, 0xc6, 0xd7, 0x49, 0xad, 0x6d, 0x87, 0x91, 0x0e, 0x3a, 0x67, 0x1d, 0x2c, 0x1d, 0x56}} ,
{{0xfe, 0x7a, 0x74, 0xcf, 0xd4, 0xd2, 0xe5, 0x19, 0xde, 0xd0, 0xdb, 0x70, 0x23, 0x69, 0xe6, 0x6d, 0xec, 0xec, 0xcc, 0x09, 0x33, 0x6a, 0x77, 0xdc, 0x6b, 0x22, 0x76, 0x5d, 0x92, 0x09, 0xac, 0x2d}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x23, 0x15, 0x17, 0xeb, 0xd3, 0xdb, 0x12, 0x5e, 0x01, 0xf0, 0x91, 0xab, 0x2c, 0x41, 0xce, 0xac, 0xed, 0x1b, 0x4b, 0x2d, 0xbc, 0xdb, 0x17, 0x66, 0x89, 0x46, 0xad, 0x4b, 0x1e, 0x6f, 0x0b, 0x14}} ,
{{0x11, 0xce, 0xbf, 0xb6, 0x77, 0x2d, 0x48, 0x22, 0x18, 0x4f, 0xa3, 0x5d, 0x4a, 0xb0, 0x70, 0x12, 0x3e, 0x54, 0xd7, 0xd8, 0x0e, 0x2b, 0x27, 0xdc, 0x53, 0xff, 0xca, 0x8c, 0x59, 0xb3, 0x4e, 0x44}}},
@@ -1646,7 +1507,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x0d, 0x16, 0xac, 0x3c, 0x4a, 0x58, 0x86, 0x3a, 0x46, 0x7f, 0x6c, 0xa3, 0x52, 0x6e, 0x37, 0xe4, 0x96, 0x9c, 0xe9, 0x5c, 0x66, 0x41, 0x67, 0xe4, 0xfb, 0x79, 0x0c, 0x05, 0xf6, 0x64, 0xd5, 0x7c}}},
{{{0x28, 0xc1, 0xe1, 0x54, 0x73, 0xf2, 0xbf, 0x76, 0x74, 0x19, 0x19, 0x1b, 0xe4, 0xb9, 0xa8, 0x46, 0x65, 0x73, 0xf3, 0x77, 0x9b, 0x29, 0x74, 0x5b, 0xc6, 0x89, 0x6c, 0x2c, 0x7c, 0xf8, 0xb3, 0x0f}} ,
{{0xf7, 0xd5, 0xe9, 0x74, 0x5d, 0xb8, 0x25, 0x16, 0xb5, 0x30, 0xbc, 0x84, 0xc5, 0xf0, 0xad, 0xca, 0x12, 0x28, 0xbc, 0x9d, 0xd4, 0xfa, 0x82, 0xe6, 0xe3, 0xbf, 0xa2, 0x15, 0x2c, 0xd4, 0x34, 0x10}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x61, 0xb1, 0x46, 0xba, 0x0e, 0x31, 0xa5, 0x67, 0x6c, 0x7f, 0xd6, 0xd9, 0x27, 0x85, 0x0f, 0x79, 0x14, 0xc8, 0x6c, 0x2f, 0x5f, 0x5b, 0x9c, 0x35, 0x3d, 0x38, 0x86, 0x77, 0x65, 0x55, 0x6a, 0x7b}} ,
{{0xd3, 0xb0, 0x3a, 0x66, 0x60, 0x1b, 0x43, 0xf1, 0x26, 0x58, 0x99, 0x09, 0x8f, 0x2d, 0xa3, 0x14, 0x71, 0x85, 0xdb, 0xed, 0xf6, 0x26, 0xd5, 0x61, 0x9a, 0x73, 0xac, 0x0e, 0xea, 0xac, 0xb7, 0x0c}}},
@@ -1656,7 +1517,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe8, 0xc1, 0xa9, 0xc2, 0x7b, 0x59, 0x21, 0x33, 0xe2, 0x43, 0x73, 0x2b, 0xac, 0x2d, 0xc1, 0x89, 0x3b, 0x15, 0xe2, 0xd5, 0xc0, 0x97, 0x8a, 0xfd, 0x6f, 0x36, 0x33, 0xb7, 0xb9, 0xc3, 0x88, 0x09}}},
{{{0xd0, 0xb6, 0x56, 0x30, 0x5c, 0xae, 0xb3, 0x75, 0x44, 0xa4, 0x83, 0x51, 0x6e, 0x01, 0x65, 0xef, 0x45, 0x76, 0xe6, 0xf5, 0xa2, 0x0d, 0xd4, 0x16, 0x3b, 0x58, 0x2f, 0xf2, 0x2f, 0x36, 0x18, 0x3f}} ,
{{0xfd, 0x2f, 0xe0, 0x9b, 0x1e, 0x8c, 0xc5, 0x18, 0xa9, 0xca, 0xd4, 0x2b, 0x35, 0xb6, 0x95, 0x0a, 0x9f, 0x7e, 0xfb, 0xc4, 0xef, 0x88, 0x7b, 0x23, 0x43, 0xec, 0x2f, 0x0d, 0x0f, 0x7a, 0xfc, 0x5c}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x8d, 0xd2, 0xda, 0xc7, 0x44, 0xd6, 0x7a, 0xdb, 0x26, 0x7d, 0x1d, 0xb8, 0xe1, 0xde, 0x9d, 0x7a, 0x7d, 0x17, 0x7e, 0x1c, 0x37, 0x04, 0x8d, 0x2d, 0x7c, 0x5e, 0x18, 0x38, 0x1e, 0xaf, 0xc7, 0x1b}} ,
{{0x33, 0x48, 0x31, 0x00, 0x59, 0xf6, 0xf2, 0xca, 0x0f, 0x27, 0x1b, 0x63, 0x12, 0x7e, 0x02, 0x1d, 0x49, 0xc0, 0x5d, 0x79, 0x87, 0xef, 0x5e, 0x7a, 0x2f, 0x1f, 0x66, 0x55, 0xd8, 0x09, 0xd9, 0x61}}},
@@ -1666,7 +1527,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa1, 0x4c, 0x70, 0x4b, 0x0e, 0xa0, 0x83, 0x70, 0x13, 0xa4, 0xaf, 0xb8, 0x38, 0x19, 0x22, 0x65, 0x09, 0xb4, 0x02, 0x4f, 0x06, 0xf8, 0x17, 0xce, 0x46, 0x45, 0xda, 0x50, 0x7c, 0x8a, 0xd1, 0x4e}}},
{{{0xf7, 0xd4, 0x16, 0x6c, 0x4e, 0x95, 0x9d, 0x5d, 0x0f, 0x91, 0x2b, 0x52, 0xfe, 0x5c, 0x34, 0xe5, 0x30, 0xe6, 0xa4, 0x3b, 0xf3, 0xf3, 0x34, 0x08, 0xa9, 0x4a, 0xa0, 0xb5, 0x6e, 0xb3, 0x09, 0x0a}} ,
{{0x26, 0xd9, 0x5e, 0xa3, 0x0f, 0xeb, 0xa2, 0xf3, 0x20, 0x3b, 0x37, 0xd4, 0xe4, 0x9e, 0xce, 0x06, 0x3d, 0x53, 0xed, 0xae, 0x2b, 0xeb, 0xb6, 0x24, 0x0a, 0x11, 0xa3, 0x0f, 0xd6, 0x7f, 0xa4, 0x3a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xdb, 0x9f, 0x2c, 0xfc, 0xd6, 0xb2, 0x1e, 0x2e, 0x52, 0x7a, 0x06, 0x87, 0x2d, 0x86, 0x72, 0x2b, 0x6d, 0x90, 0x77, 0x46, 0x43, 0xb5, 0x7a, 0xf8, 0x60, 0x7d, 0x91, 0x60, 0x5b, 0x9d, 0x9e, 0x07}} ,
{{0x97, 0x87, 0xc7, 0x04, 0x1c, 0x38, 0x01, 0x39, 0x58, 0xc7, 0x85, 0xa3, 0xfc, 0x64, 0x00, 0x64, 0x25, 0xa2, 0xbf, 0x50, 0x94, 0xca, 0x26, 0x31, 0x45, 0x0a, 0x24, 0xd2, 0x51, 0x29, 0x51, 0x16}}},
@@ -1676,7 +1537,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x95, 0x21, 0x01, 0xce, 0x95, 0x5b, 0x0e, 0x57, 0xc7, 0xb9, 0x62, 0xb5, 0x28, 0xca, 0x11, 0xec, 0xb4, 0x46, 0x06, 0x73, 0x26, 0xff, 0xfb, 0x66, 0x7d, 0xee, 0x5f, 0xb2, 0x56, 0xfd, 0x2a, 0x08}}},
{{{0x92, 0x67, 0x77, 0x56, 0xa1, 0xff, 0xc4, 0xc5, 0x95, 0xf0, 0xe3, 0x3a, 0x0a, 0xca, 0x94, 0x4d, 0x9e, 0x7e, 0x3d, 0xb9, 0x6e, 0xb6, 0xb0, 0xce, 0xa4, 0x30, 0x89, 0x99, 0xe9, 0xad, 0x11, 0x59}} ,
{{0xf6, 0x48, 0x95, 0xa1, 0x6f, 0x5f, 0xb7, 0xa5, 0xbb, 0x30, 0x00, 0x1c, 0xd2, 0x8a, 0xd6, 0x25, 0x26, 0x1b, 0xb2, 0x0d, 0x37, 0x6a, 0x05, 0xf4, 0x9d, 0x3e, 0x17, 0x2a, 0x43, 0xd2, 0x3a, 0x06}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x32, 0x99, 0x93, 0xd1, 0x9a, 0x72, 0xf3, 0xa9, 0x16, 0xbd, 0xb4, 0x4c, 0xdd, 0xf9, 0xd4, 0xb2, 0x64, 0x9a, 0xd3, 0x05, 0xe4, 0xa3, 0x73, 0x1c, 0xcb, 0x7e, 0x57, 0x67, 0xff, 0x04, 0xb3, 0x10}} ,
{{0xb9, 0x4b, 0xa4, 0xad, 0xd0, 0x6d, 0x61, 0x23, 0xb4, 0xaf, 0x34, 0xa9, 0xaa, 0x65, 0xec, 0xd9, 0x69, 0xe3, 0x85, 0xcd, 0xcc, 0xe7, 0xb0, 0x9b, 0x41, 0xc1, 0x1c, 0xf9, 0xa0, 0xfa, 0xb7, 0x13}}},
@@ -1686,7 +1547,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xfd, 0x4e, 0x2a, 0xc1, 0x3a, 0xca, 0x8f, 0x00, 0xd8, 0xec, 0x74, 0x67, 0xef, 0x61, 0xe0, 0x28, 0xd0, 0x96, 0xf4, 0x48, 0xde, 0x81, 0xe3, 0xef, 0xdc, 0xaa, 0x7d, 0xf3, 0xb6, 0x55, 0xa6, 0x65}}},
{{{0xeb, 0xcb, 0xc5, 0x70, 0x91, 0x31, 0x10, 0x93, 0x0d, 0xc8, 0xd0, 0xef, 0x62, 0xe8, 0x6f, 0x82, 0xe3, 0x69, 0x3d, 0x91, 0x7f, 0x31, 0xe1, 0x26, 0x35, 0x3c, 0x4a, 0x2f, 0xab, 0xc4, 0x9a, 0x5e}} ,
{{0xab, 0x1b, 0xb5, 0xe5, 0x2b, 0xc3, 0x0e, 0x29, 0xb0, 0xd0, 0x73, 0xe6, 0x4f, 0x64, 0xf2, 0xbc, 0xe4, 0xe4, 0xe1, 0x9a, 0x52, 0x33, 0x2f, 0xbd, 0xcc, 0x03, 0xee, 0x8a, 0xfa, 0x00, 0x5f, 0x50}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf6, 0xdb, 0x0d, 0x22, 0x3d, 0xb5, 0x14, 0x75, 0x31, 0xf0, 0x81, 0xe2, 0xb9, 0x37, 0xa2, 0xa9, 0x84, 0x11, 0x9a, 0x07, 0xb5, 0x53, 0x89, 0x78, 0xa9, 0x30, 0x27, 0xa1, 0xf1, 0x4e, 0x5c, 0x2e}} ,
{{0x8b, 0x00, 0x54, 0xfb, 0x4d, 0xdc, 0xcb, 0x17, 0x35, 0x40, 0xff, 0xb7, 0x8c, 0xfe, 0x4a, 0xe4, 0x4e, 0x99, 0x4e, 0xa8, 0x74, 0x54, 0x5d, 0x5c, 0x96, 0xa3, 0x12, 0x55, 0x36, 0x31, 0x17, 0x5c}}},
@@ -1696,7 +1557,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8c, 0x44, 0x09, 0x28, 0xd5, 0x23, 0xc9, 0x8f, 0xf3, 0x84, 0x45, 0xc6, 0x9a, 0x5e, 0xff, 0xd2, 0xc7, 0x57, 0x93, 0xa3, 0xc1, 0x69, 0xdd, 0x62, 0x0f, 0xda, 0x5c, 0x30, 0x59, 0x5d, 0xe9, 0x4c}}},
{{{0x92, 0x7e, 0x50, 0x27, 0x72, 0xd7, 0x0c, 0xd6, 0x69, 0x96, 0x81, 0x35, 0x84, 0x94, 0x35, 0x8b, 0x6c, 0xaa, 0x62, 0x86, 0x6e, 0x1c, 0x15, 0xf3, 0x6c, 0xb3, 0xff, 0x65, 0x1b, 0xa2, 0x9b, 0x59}} ,
{{0xe2, 0xa9, 0x65, 0x88, 0xc4, 0x50, 0xfa, 0xbb, 0x3b, 0x6e, 0x5f, 0x44, 0x01, 0xca, 0x97, 0xd4, 0xdd, 0xf6, 0xcd, 0x3f, 0x3f, 0xe5, 0x97, 0x67, 0x2b, 0x8c, 0x66, 0x0f, 0x35, 0x9b, 0xf5, 0x07}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xf1, 0x59, 0x27, 0xd8, 0xdb, 0x5a, 0x11, 0x5e, 0x82, 0xf3, 0x38, 0xff, 0x1c, 0xed, 0xfe, 0x3f, 0x64, 0x54, 0x3f, 0x7f, 0xd1, 0x81, 0xed, 0xef, 0x65, 0xc5, 0xcb, 0xfd, 0xe1, 0x80, 0xcd, 0x11}} ,
{{0xe0, 0xdb, 0x22, 0x28, 0xe6, 0xff, 0x61, 0x9d, 0x41, 0x14, 0x2d, 0x3b, 0x26, 0x22, 0xdf, 0xf1, 0x34, 0x81, 0xe9, 0x45, 0xee, 0x0f, 0x98, 0x8b, 0xa6, 0x3f, 0xef, 0xf7, 0x43, 0x19, 0xf1, 0x43}}},
@@ -1706,7 +1567,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x60, 0xbc, 0x45, 0x1f, 0x23, 0xa2, 0x3b, 0x70, 0x76, 0xe6, 0x97, 0x99, 0x4f, 0x77, 0x54, 0x67, 0x30, 0x9a, 0xe7, 0x66, 0xd6, 0xcd, 0x2e, 0x51, 0x24, 0x2c, 0x42, 0x4a, 0x11, 0xfe, 0x6f, 0x7e}}},
{{{0x87, 0xc0, 0xb1, 0xf0, 0xa3, 0x6f, 0x0c, 0x93, 0xa9, 0x0a, 0x72, 0xef, 0x5c, 0xbe, 0x65, 0x35, 0xa7, 0x6a, 0x4e, 0x2c, 0xbf, 0x21, 0x23, 0xe8, 0x2f, 0x97, 0xc7, 0x3e, 0xc8, 0x17, 0xac, 0x1e}} ,
{{0x7b, 0xef, 0x21, 0xe5, 0x40, 0xcc, 0x1e, 0xdc, 0xd6, 0xbd, 0x97, 0x7a, 0x7c, 0x75, 0x86, 0x7a, 0x25, 0x5a, 0x6e, 0x7c, 0xe5, 0x51, 0x3c, 0x1b, 0x5b, 0x82, 0x9a, 0x07, 0x60, 0xa1, 0x19, 0x04}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x96, 0x88, 0xa6, 0xab, 0x8f, 0xe3, 0x3a, 0x49, 0xf8, 0xfe, 0x34, 0xe7, 0x6a, 0xb2, 0xfe, 0x40, 0x26, 0x74, 0x57, 0x4c, 0xf6, 0xd4, 0x99, 0xce, 0x5d, 0x7b, 0x2f, 0x67, 0xd6, 0x5a, 0xe4, 0x4e}} ,
{{0x5c, 0x82, 0xb3, 0xbd, 0x55, 0x25, 0xf6, 0x6a, 0x93, 0xa4, 0x02, 0xc6, 0x7d, 0x5c, 0xb1, 0x2b, 0x5b, 0xff, 0xfb, 0x56, 0xf8, 0x01, 0x41, 0x90, 0xc6, 0xb6, 0xac, 0x4f, 0xfe, 0xa7, 0x41, 0x70}}},
@@ -1716,7 +1577,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x17, 0x0f, 0x86, 0x52, 0xd7, 0x9d, 0xc3, 0x44, 0x51, 0x76, 0x32, 0x65, 0xb4, 0x37, 0x81, 0x99, 0x46, 0x37, 0x62, 0xed, 0xcf, 0x64, 0x9d, 0x72, 0x40, 0x7a, 0x4c, 0x0b, 0x76, 0x2a, 0xfb, 0x56}}},
{{{0x33, 0xa7, 0x90, 0x7c, 0xc3, 0x6f, 0x17, 0xa5, 0xa0, 0x67, 0x72, 0x17, 0xea, 0x7e, 0x63, 0x14, 0x83, 0xde, 0xc1, 0x71, 0x2d, 0x41, 0x32, 0x7a, 0xf3, 0xd1, 0x2b, 0xd8, 0x2a, 0xa6, 0x46, 0x36}} ,
{{0xac, 0xcc, 0x6b, 0x7c, 0xf9, 0xb8, 0x8b, 0x08, 0x5c, 0xd0, 0x7d, 0x8f, 0x73, 0xea, 0x20, 0xda, 0x86, 0xca, 0x00, 0xc7, 0xad, 0x73, 0x4d, 0xe9, 0xe8, 0xa9, 0xda, 0x1f, 0x03, 0x06, 0xdd, 0x24}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x9c, 0xb2, 0x61, 0x0a, 0x98, 0x2a, 0xa5, 0xd7, 0xee, 0xa9, 0xac, 0x65, 0xcb, 0x0a, 0x1e, 0xe2, 0xbe, 0xdc, 0x85, 0x59, 0x0f, 0x9c, 0xa6, 0x57, 0x34, 0xa5, 0x87, 0xeb, 0x7b, 0x1e, 0x0c, 0x3c}} ,
{{0x2f, 0xbd, 0x84, 0x63, 0x0d, 0xb5, 0xa0, 0xf0, 0x4b, 0x9e, 0x93, 0xc6, 0x34, 0x9a, 0x34, 0xff, 0x73, 0x19, 0x2f, 0x6e, 0x54, 0x45, 0x2c, 0x92, 0x31, 0x76, 0x34, 0xf1, 0xb2, 0x26, 0xe8, 0x74}}},
@@ -1726,7 +1587,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x87, 0xc8, 0xa0, 0x6e, 0xe1, 0xb0, 0xad, 0x6a, 0x4a, 0x34, 0x71, 0xed, 0x7c, 0xd6, 0x44, 0x03, 0x65, 0x4a, 0x5c, 0x5c, 0x04, 0xf5, 0x24, 0x3f, 0xb0, 0x16, 0x5e, 0x8c, 0xb2, 0xd2, 0xc5, 0x20}}},
{{{0x98, 0x83, 0xc2, 0x37, 0xa0, 0x41, 0xa8, 0x48, 0x5c, 0x5f, 0xbf, 0xc8, 0xfa, 0x24, 0xe0, 0x59, 0x2c, 0xbd, 0xf6, 0x81, 0x7e, 0x88, 0xe6, 0xca, 0x04, 0xd8, 0x5d, 0x60, 0xbb, 0x74, 0xa7, 0x0b}} ,
{{0x21, 0x13, 0x91, 0xbf, 0x77, 0x7a, 0x33, 0xbc, 0xe9, 0x07, 0x39, 0x0a, 0xdd, 0x7d, 0x06, 0x10, 0x9a, 0xee, 0x47, 0x73, 0x1b, 0x15, 0x5a, 0xfb, 0xcd, 0x4d, 0xd0, 0xd2, 0x3a, 0x01, 0xba, 0x54}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x48, 0xd5, 0x39, 0x4a, 0x0b, 0x20, 0x6a, 0x43, 0xa0, 0x07, 0x82, 0x5e, 0x49, 0x7c, 0xc9, 0x47, 0xf1, 0x7c, 0x37, 0xb9, 0x23, 0xef, 0x6b, 0x46, 0x45, 0x8c, 0x45, 0x76, 0xdf, 0x14, 0x6b, 0x6e}} ,
{{0x42, 0xc9, 0xca, 0x29, 0x4c, 0x76, 0x37, 0xda, 0x8a, 0x2d, 0x7c, 0x3a, 0x58, 0xf2, 0x03, 0xb4, 0xb5, 0xb9, 0x1a, 0x13, 0x2d, 0xde, 0x5f, 0x6b, 0x9d, 0xba, 0x52, 0xc9, 0x5d, 0xb3, 0xf3, 0x30}}},
@@ -1736,7 +1597,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xe4, 0xe7, 0x43, 0x4b, 0xa0, 0x3f, 0x2b, 0x06, 0xba, 0x17, 0xae, 0x3d, 0xe6, 0xce, 0xbd, 0xb8, 0xed, 0x74, 0x11, 0x35, 0xec, 0x96, 0xfe, 0x31, 0xe3, 0x0e, 0x7a, 0x4e, 0xc9, 0x1d, 0xcb, 0x20}}},
{{{0xe0, 0x67, 0xe9, 0x7b, 0xdb, 0x96, 0x5c, 0xb0, 0x32, 0xd0, 0x59, 0x31, 0x90, 0xdc, 0x92, 0x97, 0xac, 0x09, 0x38, 0x31, 0x0f, 0x7e, 0xd6, 0x5d, 0xd0, 0x06, 0xb6, 0x1f, 0xea, 0xf0, 0x5b, 0x07}} ,
{{0x81, 0x9f, 0xc7, 0xde, 0x6b, 0x41, 0x22, 0x35, 0x14, 0x67, 0x77, 0x3e, 0x90, 0x81, 0xb0, 0xd9, 0x85, 0x4c, 0xca, 0x9b, 0x3f, 0x04, 0x59, 0xd6, 0xaa, 0x17, 0xc3, 0x88, 0x34, 0x37, 0xba, 0x43}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x4c, 0xb6, 0x69, 0xc8, 0x81, 0x95, 0x94, 0x33, 0x92, 0x34, 0xe9, 0x3c, 0x84, 0x0d, 0x3d, 0x5a, 0x37, 0x9c, 0x22, 0xa0, 0xaa, 0x65, 0xce, 0xb4, 0xc2, 0x2d, 0x66, 0x67, 0x02, 0xff, 0x74, 0x10}} ,
{{0x22, 0xb0, 0xd5, 0xe6, 0xc7, 0xef, 0xb1, 0xa7, 0x13, 0xda, 0x60, 0xb4, 0x80, 0xc1, 0x42, 0x7d, 0x10, 0x70, 0x97, 0x04, 0x4d, 0xda, 0x23, 0x89, 0xc2, 0x0e, 0x68, 0xcb, 0xde, 0xe0, 0x9b, 0x29}}},
@@ -1746,7 +1607,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa0, 0x20, 0x99, 0x69, 0x0a, 0xae, 0xa3, 0x70, 0x4e, 0x64, 0x80, 0xb7, 0x85, 0x9c, 0x87, 0x54, 0x43, 0x43, 0x55, 0x80, 0x6d, 0x8d, 0x7c, 0xa9, 0x64, 0xca, 0x6c, 0x2e, 0x21, 0xd8, 0xc8, 0x6c}}},
{{{0x91, 0x4a, 0x07, 0xad, 0x08, 0x75, 0xc1, 0x4f, 0xa4, 0xb2, 0xc3, 0x6f, 0x46, 0x3e, 0xb1, 0xce, 0x52, 0xab, 0x67, 0x09, 0x54, 0x48, 0x6b, 0x6c, 0xd7, 0x1d, 0x71, 0x76, 0xcb, 0xff, 0xdd, 0x31}} ,
{{0x36, 0x88, 0xfa, 0xfd, 0xf0, 0x36, 0x6f, 0x07, 0x74, 0x88, 0x50, 0xd0, 0x95, 0x38, 0x4a, 0x48, 0x2e, 0x07, 0x64, 0x97, 0x11, 0x76, 0x01, 0x1a, 0x27, 0x4d, 0x8e, 0x25, 0x9a, 0x9b, 0x1c, 0x22}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xbe, 0x57, 0xbd, 0x0e, 0x0f, 0xac, 0x5e, 0x76, 0xa3, 0x71, 0xad, 0x2b, 0x10, 0x45, 0x02, 0xec, 0x59, 0xd5, 0x5d, 0xa9, 0x44, 0xcc, 0x25, 0x4c, 0xb3, 0x3c, 0x5b, 0x69, 0x07, 0x55, 0x26, 0x6b}} ,
{{0x30, 0x6b, 0xd4, 0xa7, 0x51, 0x29, 0xe3, 0xf9, 0x7a, 0x75, 0x2a, 0x82, 0x2f, 0xd6, 0x1d, 0x99, 0x2b, 0x80, 0xd5, 0x67, 0x1e, 0x15, 0x9d, 0xca, 0xfd, 0xeb, 0xac, 0x97, 0x35, 0x09, 0x7f, 0x3f}}},
@@ -1756,7 +1617,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xad, 0xa0, 0x41, 0xec, 0xc8, 0x4d, 0xb9, 0xd2, 0x6e, 0x96, 0x4e, 0x5b, 0xc5, 0xc2, 0xa0, 0x1b, 0xcf, 0x0c, 0xbf, 0x17, 0x66, 0x57, 0xc1, 0x17, 0x90, 0x45, 0x71, 0xc2, 0xe1, 0x24, 0xeb, 0x27}}},
{{{0x2c, 0xb9, 0x42, 0xa4, 0xaf, 0x3b, 0x42, 0x0e, 0xc2, 0x0f, 0xf2, 0xea, 0x83, 0xaf, 0x9a, 0x13, 0x17, 0xb0, 0xbd, 0x89, 0x17, 0xe3, 0x72, 0xcb, 0x0e, 0x76, 0x7e, 0x41, 0x63, 0x04, 0x88, 0x71}} ,
{{0x75, 0x78, 0x38, 0x86, 0x57, 0xdd, 0x9f, 0xee, 0x54, 0x70, 0x65, 0xbf, 0xf1, 0x2c, 0xe0, 0x39, 0x0d, 0xe3, 0x89, 0xfd, 0x8e, 0x93, 0x4f, 0x43, 0xdc, 0xd5, 0x5b, 0xde, 0xf9, 0x98, 0xe5, 0x7b}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xe7, 0x3b, 0x65, 0x11, 0xdf, 0xb2, 0xf2, 0x63, 0x94, 0x12, 0x6f, 0x5c, 0x9e, 0x77, 0xc1, 0xb6, 0xd8, 0xab, 0x58, 0x7a, 0x1d, 0x95, 0x73, 0xdd, 0xe7, 0xe3, 0x6f, 0xf2, 0x03, 0x1d, 0xdb, 0x76}} ,
{{0xae, 0x06, 0x4e, 0x2c, 0x52, 0x1b, 0xbc, 0x5a, 0x5a, 0xa5, 0xbe, 0x27, 0xbd, 0xeb, 0xe1, 0x14, 0x17, 0x68, 0x26, 0x07, 0x03, 0xd1, 0x18, 0x0b, 0xdf, 0xf1, 0x06, 0x5c, 0xa6, 0x1b, 0xb9, 0x24}}},
@@ -1766,7 +1627,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x8d, 0xbd, 0xa3, 0xc4, 0x06, 0x9b, 0x1f, 0x90, 0x58, 0x60, 0x74, 0xb2, 0x00, 0x3b, 0x3c, 0xd2, 0xda, 0x82, 0xbb, 0x10, 0x90, 0x69, 0x92, 0xa9, 0xb4, 0x30, 0x81, 0xe3, 0x7c, 0xa8, 0x89, 0x45}}},
{{{0x3f, 0xdc, 0x05, 0xcb, 0x41, 0x3c, 0xc8, 0x23, 0x04, 0x2c, 0x38, 0x99, 0xe3, 0x68, 0x55, 0xf9, 0xd3, 0x32, 0xc7, 0xbf, 0xfa, 0xd4, 0x1b, 0x5d, 0xde, 0xdc, 0x10, 0x42, 0xc0, 0x42, 0xd9, 0x75}} ,
{{0x2d, 0xab, 0x35, 0x4e, 0x87, 0xc4, 0x65, 0x97, 0x67, 0x24, 0xa4, 0x47, 0xad, 0x3f, 0x8e, 0xf3, 0xcb, 0x31, 0x17, 0x77, 0xc5, 0xe2, 0xd7, 0x8f, 0x3c, 0xc1, 0xcd, 0x56, 0x48, 0xc1, 0x6c, 0x69}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x14, 0xae, 0x5f, 0x88, 0x7b, 0xa5, 0x90, 0xdf, 0x10, 0xb2, 0x8b, 0x5e, 0x24, 0x17, 0xc3, 0xa3, 0xd4, 0x0f, 0x92, 0x61, 0x1a, 0x19, 0x5a, 0xad, 0x76, 0xbd, 0xd8, 0x1c, 0xdd, 0xe0, 0x12, 0x6d}} ,
{{0x8e, 0xbd, 0x70, 0x8f, 0x02, 0xa3, 0x24, 0x4d, 0x5a, 0x67, 0xc4, 0xda, 0xf7, 0x20, 0x0f, 0x81, 0x5b, 0x7a, 0x05, 0x24, 0x67, 0x83, 0x0b, 0x2a, 0x80, 0xe7, 0xfd, 0x74, 0x4b, 0x9e, 0x5c, 0x0d}}},
@@ -1776,7 +1637,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x97, 0x29, 0x6c, 0xc4, 0x42, 0x0b, 0xdd, 0xc0, 0x29, 0x5c, 0x9b, 0x34, 0x97, 0xd0, 0xc7, 0x79, 0x80, 0x63, 0x74, 0xe4, 0x8e, 0x37, 0xb0, 0x2b, 0x7c, 0xe8, 0x68, 0x6c, 0xc3, 0x82, 0x97, 0x57}}},
{{{0x22, 0xbe, 0x83, 0xb6, 0x4b, 0x80, 0x6b, 0x43, 0x24, 0x5e, 0xef, 0x99, 0x9b, 0xa8, 0xfc, 0x25, 0x8d, 0x3b, 0x03, 0x94, 0x2b, 0x3e, 0xe7, 0x95, 0x76, 0x9b, 0xcc, 0x15, 0xdb, 0x32, 0xe6, 0x66}} ,
{{0x84, 0xf0, 0x4a, 0x13, 0xa6, 0xd6, 0xfa, 0x93, 0x46, 0x07, 0xf6, 0x7e, 0x5c, 0x6d, 0x5e, 0xf6, 0xa6, 0xe7, 0x48, 0xf0, 0x06, 0xea, 0xff, 0x90, 0xc1, 0xcc, 0x4c, 0x19, 0x9c, 0x3c, 0x4e, 0x53}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2a, 0x50, 0xe3, 0x07, 0x15, 0x59, 0xf2, 0x8b, 0x81, 0xf2, 0xf3, 0xd3, 0x6c, 0x99, 0x8c, 0x70, 0x67, 0xec, 0xcc, 0xee, 0x9e, 0x59, 0x45, 0x59, 0x7d, 0x47, 0x75, 0x69, 0xf5, 0x24, 0x93, 0x5d}} ,
{{0x6a, 0x4f, 0x1b, 0xbe, 0x6b, 0x30, 0xcf, 0x75, 0x46, 0xe3, 0x7b, 0x9d, 0xfc, 0xcd, 0xd8, 0x5c, 0x1f, 0xb4, 0xc8, 0xe2, 0x24, 0xec, 0x1a, 0x28, 0x05, 0x32, 0x57, 0xfd, 0x3c, 0x5a, 0x98, 0x10}}},
@@ -1786,7 +1647,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x9b, 0x35, 0xe2, 0xa9, 0x3d, 0x32, 0x1e, 0xbb, 0x16, 0x28, 0x70, 0xe9, 0x45, 0x2f, 0x8f, 0x70, 0x7f, 0x08, 0x7e, 0x53, 0xc4, 0x7a, 0xbf, 0xf7, 0xe1, 0xa4, 0x6a, 0xd8, 0xac, 0x64, 0x1b, 0x11}}},
{{{0xb2, 0xeb, 0x47, 0x46, 0x18, 0x3e, 0x1f, 0x99, 0x0c, 0xcc, 0xf1, 0x2c, 0xe0, 0xe7, 0x8f, 0xe0, 0x01, 0x7e, 0x65, 0xb8, 0x0c, 0xd0, 0xfb, 0xc8, 0xb9, 0x90, 0x98, 0x33, 0x61, 0x3b, 0xd8, 0x27}} ,
{{0xa0, 0xbe, 0x72, 0x3a, 0x50, 0x4b, 0x74, 0xab, 0x01, 0xc8, 0x93, 0xc5, 0xe4, 0xc7, 0x08, 0x6c, 0xb4, 0xca, 0xee, 0xeb, 0x8e, 0xd7, 0x4e, 0x26, 0xc6, 0x1d, 0xe2, 0x71, 0xaf, 0x89, 0xa0, 0x2a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x98, 0x0b, 0xe4, 0xde, 0xdb, 0xa8, 0xfa, 0x82, 0x74, 0x06, 0x52, 0x6d, 0x08, 0x52, 0x8a, 0xff, 0x62, 0xc5, 0x6a, 0x44, 0x0f, 0x51, 0x8c, 0x1f, 0x6e, 0xb6, 0xc6, 0x2c, 0x81, 0xd3, 0x76, 0x46}} ,
{{0xf4, 0x29, 0x74, 0x2e, 0x80, 0xa7, 0x1a, 0x8f, 0xf6, 0xbd, 0xd6, 0x8e, 0xbf, 0xc1, 0x95, 0x2a, 0xeb, 0xa0, 0x7f, 0x45, 0xa0, 0x50, 0x14, 0x05, 0xb1, 0x57, 0x4c, 0x74, 0xb7, 0xe2, 0x89, 0x7d}}},
@@ -1796,7 +1657,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x29, 0xd8, 0xf2, 0xaa, 0xe9, 0x0e, 0xf7, 0x2e, 0x5f, 0x9d, 0x8a, 0x5b, 0x09, 0xed, 0xc9, 0x24, 0x22, 0xf4, 0x0f, 0x25, 0x8f, 0x1c, 0x84, 0x6e, 0x34, 0x14, 0x6c, 0xea, 0xb3, 0x86, 0x5d, 0x04}}},
{{{0x07, 0x98, 0x61, 0xe8, 0x6a, 0xd2, 0x81, 0x49, 0x25, 0xd5, 0x5b, 0x18, 0xc7, 0x35, 0x52, 0x51, 0xa4, 0x46, 0xad, 0x18, 0x0d, 0xc9, 0x5f, 0x18, 0x91, 0x3b, 0xb4, 0xc0, 0x60, 0x59, 0x8d, 0x66}} ,
{{0x03, 0x1b, 0x79, 0x53, 0x6e, 0x24, 0xae, 0x57, 0xd9, 0x58, 0x09, 0x85, 0x48, 0xa2, 0xd3, 0xb5, 0xe2, 0x4d, 0x11, 0x82, 0xe6, 0x86, 0x3c, 0xe9, 0xb1, 0x00, 0x19, 0xc2, 0x57, 0xf7, 0x66, 0x7a}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x0f, 0xe3, 0x89, 0x03, 0xd7, 0x22, 0x95, 0x9f, 0xca, 0xb4, 0x8d, 0x9e, 0x6d, 0x97, 0xff, 0x8d, 0x21, 0x59, 0x07, 0xef, 0x03, 0x2d, 0x5e, 0xf8, 0x44, 0x46, 0xe7, 0x85, 0x80, 0xc5, 0x89, 0x50}} ,
{{0x8b, 0xd8, 0x53, 0x86, 0x24, 0x86, 0x29, 0x52, 0x01, 0xfa, 0x20, 0xc3, 0x4e, 0x95, 0xcb, 0xad, 0x7b, 0x34, 0x94, 0x30, 0xb7, 0x7a, 0xfa, 0x96, 0x41, 0x60, 0x2b, 0xcb, 0x59, 0xb9, 0xca, 0x50}}},
@@ -1806,7 +1667,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x19, 0xf9, 0x35, 0xaa, 0x59, 0x1a, 0x0c, 0x5c, 0x06, 0xfc, 0x6a, 0x0b, 0x97, 0x53, 0x36, 0xfc, 0x2a, 0xa5, 0x5a, 0x9b, 0x30, 0xef, 0x23, 0xaf, 0x39, 0x5d, 0x9a, 0x6b, 0x75, 0x57, 0x48, 0x0b}}},
{{{0x26, 0xdc, 0x76, 0x3b, 0xfc, 0xf9, 0x9c, 0x3f, 0x89, 0x0b, 0x62, 0x53, 0xaf, 0x83, 0x01, 0x2e, 0xbc, 0x6a, 0xc6, 0x03, 0x0d, 0x75, 0x2a, 0x0d, 0xe6, 0x94, 0x54, 0xcf, 0xb3, 0xe5, 0x96, 0x25}} ,
{{0xfe, 0x82, 0xb1, 0x74, 0x31, 0x8a, 0xa7, 0x6f, 0x56, 0xbd, 0x8d, 0xf4, 0xe0, 0x94, 0x51, 0x59, 0xde, 0x2c, 0x5a, 0xf4, 0x84, 0x6b, 0x4a, 0x88, 0x93, 0xc0, 0x0c, 0x9a, 0xac, 0xa7, 0xa0, 0x68}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x25, 0x0d, 0xd6, 0xc7, 0x23, 0x47, 0x10, 0xad, 0xc7, 0x08, 0x5c, 0x87, 0x87, 0x93, 0x98, 0x18, 0xb8, 0xd3, 0x9c, 0xac, 0x5a, 0x3d, 0xc5, 0x75, 0xf8, 0x49, 0x32, 0x14, 0xcc, 0x51, 0x96, 0x24}} ,
{{0x65, 0x9c, 0x5d, 0xf0, 0x37, 0x04, 0xf0, 0x34, 0x69, 0x2a, 0xf0, 0xa5, 0x64, 0xca, 0xde, 0x2b, 0x5b, 0x15, 0x10, 0xd2, 0xab, 0x06, 0xdd, 0xc4, 0xb0, 0xb6, 0x5b, 0xc1, 0x17, 0xdf, 0x8f, 0x02}}},
@@ -1816,7 +1677,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x00, 0xa9, 0x4c, 0xb2, 0x12, 0xf8, 0x32, 0xa8, 0x7a, 0x00, 0x4b, 0x49, 0x32, 0xba, 0x1f, 0x5d, 0x44, 0x8e, 0x44, 0x7a, 0xdc, 0x11, 0xfb, 0x39, 0x08, 0x57, 0x87, 0xa5, 0x12, 0x42, 0x93, 0x0e}}},
{{{0x17, 0xb4, 0xae, 0x72, 0x59, 0xd0, 0xaa, 0xa8, 0x16, 0x8b, 0x63, 0x11, 0xb3, 0x43, 0x04, 0xda, 0x0c, 0xa8, 0xb7, 0x68, 0xdd, 0x4e, 0x54, 0xe7, 0xaf, 0x5d, 0x5d, 0x05, 0x76, 0x36, 0xec, 0x0d}} ,
{{0x6d, 0x7c, 0x82, 0x32, 0x38, 0x55, 0x57, 0x74, 0x5b, 0x7d, 0xc3, 0xc4, 0xfb, 0x06, 0x29, 0xf0, 0x13, 0x55, 0x54, 0xc6, 0xa7, 0xdc, 0x4c, 0x9f, 0x98, 0x49, 0x20, 0xa8, 0xc3, 0x8d, 0xfa, 0x48}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x87, 0x47, 0x9d, 0xe9, 0x25, 0xd5, 0xe3, 0x47, 0x78, 0xdf, 0x85, 0xa7, 0x85, 0x5e, 0x7a, 0x4c, 0x5f, 0x79, 0x1a, 0xf3, 0xa2, 0xb2, 0x28, 0xa0, 0x9c, 0xdd, 0x30, 0x40, 0xd4, 0x38, 0xbd, 0x28}} ,
{{0xfc, 0xbb, 0xd5, 0x78, 0x6d, 0x1d, 0xd4, 0x99, 0xb4, 0xaa, 0x44, 0x44, 0x7a, 0x1b, 0xd8, 0xfe, 0xb4, 0x99, 0xb9, 0xcc, 0xe7, 0xc4, 0xd3, 0x3a, 0x73, 0x83, 0x41, 0x5c, 0x40, 0xd7, 0x2d, 0x55}}},
@@ -1826,7 +1687,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xc0, 0x61, 0x48, 0x48, 0x17, 0xf4, 0x9e, 0x18, 0x51, 0x2d, 0xea, 0x2f, 0xf2, 0xf2, 0xe0, 0xa3, 0x14, 0xb7, 0x8b, 0x3a, 0x30, 0xf5, 0x81, 0xc1, 0x5d, 0x71, 0x39, 0x62, 0x55, 0x1f, 0x60, 0x5a}}},
{{{0xe5, 0x89, 0x8a, 0x76, 0x6c, 0xdb, 0x4d, 0x0a, 0x5b, 0x72, 0x9d, 0x59, 0x6e, 0x63, 0x63, 0x18, 0x7c, 0xe3, 0xfa, 0xe2, 0xdb, 0xa1, 0x8d, 0xf4, 0xa5, 0xd7, 0x16, 0xb2, 0xd0, 0xb3, 0x3f, 0x39}} ,
{{0xce, 0x60, 0x09, 0x6c, 0xf5, 0x76, 0x17, 0x24, 0x80, 0x3a, 0x96, 0xc7, 0x94, 0x2e, 0xf7, 0x6b, 0xef, 0xb5, 0x05, 0x96, 0xef, 0xd3, 0x7b, 0x51, 0xda, 0x05, 0x44, 0x67, 0xbc, 0x07, 0x21, 0x4e}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xe9, 0x73, 0x6f, 0x21, 0xb9, 0xde, 0x22, 0x7d, 0xeb, 0x97, 0x31, 0x10, 0xa3, 0xea, 0xe1, 0xc6, 0x37, 0xeb, 0x8f, 0x43, 0x58, 0xde, 0x41, 0x64, 0x0e, 0x3e, 0x07, 0x99, 0x3d, 0xf1, 0xdf, 0x1e}} ,
{{0xf8, 0xad, 0x43, 0xc2, 0x17, 0x06, 0xe2, 0xe4, 0xa9, 0x86, 0xcd, 0x18, 0xd7, 0x78, 0xc8, 0x74, 0x66, 0xd2, 0x09, 0x18, 0xa5, 0xf1, 0xca, 0xa6, 0x62, 0x92, 0xc1, 0xcb, 0x00, 0xeb, 0x42, 0x2e}}},
@@ -1836,7 +1697,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa3, 0x64, 0x17, 0x9b, 0x8b, 0xc8, 0x3a, 0x61, 0xe6, 0x9d, 0xc6, 0xed, 0x7b, 0x03, 0x52, 0x26, 0x9d, 0x3a, 0xb3, 0x13, 0xcc, 0x8a, 0xfd, 0x2c, 0x1a, 0x1d, 0xed, 0x13, 0xd0, 0x55, 0x57, 0x0e}}},
{{{0x1a, 0xea, 0xbf, 0xfd, 0x4a, 0x3c, 0x8e, 0xec, 0x29, 0x7e, 0x77, 0x77, 0x12, 0x99, 0xd7, 0x84, 0xf9, 0x55, 0x7f, 0xf1, 0x8b, 0xb4, 0xd2, 0x95, 0xa3, 0x8d, 0xf0, 0x8a, 0xa7, 0xeb, 0x82, 0x4b}} ,
{{0x2c, 0x28, 0xf4, 0x3a, 0xf6, 0xde, 0x0a, 0xe0, 0x41, 0x44, 0x23, 0xf8, 0x3f, 0x03, 0x64, 0x9f, 0xc3, 0x55, 0x4c, 0xc6, 0xc1, 0x94, 0x1c, 0x24, 0x5d, 0x5f, 0x92, 0x45, 0x96, 0x57, 0x37, 0x14}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xc1, 0xcd, 0x90, 0x66, 0xb9, 0x76, 0xa0, 0x5b, 0xa5, 0x85, 0x75, 0x23, 0xf9, 0x89, 0xa5, 0x82, 0xb2, 0x6f, 0xb1, 0xeb, 0xc4, 0x69, 0x6f, 0x18, 0x5a, 0xed, 0x94, 0x3d, 0x9d, 0xd9, 0x2c, 0x1a}} ,
{{0x35, 0xb0, 0xe6, 0x73, 0x06, 0xb7, 0x37, 0xe0, 0xf8, 0xb0, 0x22, 0xe8, 0xd2, 0xed, 0x0b, 0xef, 0xe6, 0xc6, 0x5a, 0x99, 0x9e, 0x1a, 0x9f, 0x04, 0x97, 0xe4, 0x4d, 0x0b, 0xbe, 0xba, 0x44, 0x40}}},
@@ -1846,7 +1707,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0xa7, 0xbb, 0xf6, 0x8e, 0xad, 0xdd, 0xf7, 0x90, 0xdd, 0x5f, 0x93, 0x89, 0xae, 0x04, 0x37, 0xe6, 0x9a, 0xb7, 0xe8, 0xc0, 0xdf, 0x16, 0x2a, 0xbf, 0xc4, 0x3a, 0x3c, 0x41, 0xd5, 0x89, 0x72, 0x5a}}},
{{{0x1f, 0x96, 0xff, 0x34, 0x2c, 0x13, 0x21, 0xcb, 0x0a, 0x89, 0x85, 0xbe, 0xb3, 0x70, 0x9e, 0x1e, 0xde, 0x97, 0xaf, 0x96, 0x30, 0xf7, 0x48, 0x89, 0x40, 0x8d, 0x07, 0xf1, 0x25, 0xf0, 0x30, 0x58}} ,
{{0x1e, 0xd4, 0x93, 0x57, 0xe2, 0x17, 0xe7, 0x9d, 0xab, 0x3c, 0x55, 0x03, 0x82, 0x2f, 0x2b, 0xdb, 0x56, 0x1e, 0x30, 0x2e, 0x24, 0x47, 0x6e, 0xe6, 0xff, 0x33, 0x24, 0x2c, 0x75, 0x51, 0xd4, 0x67}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0x2b, 0x06, 0xd9, 0xa1, 0x5d, 0xe1, 0xf4, 0xd1, 0x1e, 0x3c, 0x9a, 0xc6, 0x29, 0x2b, 0x13, 0x13, 0x78, 0xc0, 0xd8, 0x16, 0x17, 0x2d, 0x9e, 0xa9, 0xc9, 0x79, 0x57, 0xab, 0x24, 0x91, 0x92, 0x19}} ,
{{0x69, 0xfb, 0xa1, 0x9c, 0xa6, 0x75, 0x49, 0x7d, 0x60, 0x73, 0x40, 0x42, 0xc4, 0x13, 0x0a, 0x95, 0x79, 0x1e, 0x04, 0x83, 0x94, 0x99, 0x9b, 0x1e, 0x0c, 0xe8, 0x1f, 0x54, 0xef, 0xcb, 0xc0, 0x52}}},
@@ -1856,7 +1717,7 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x9d, 0x45, 0xd6, 0x80, 0xe6, 0x45, 0xaa, 0xf4, 0x15, 0xaa, 0x5c, 0x34, 0x87, 0x99, 0xa2, 0x8c, 0x26, 0x84, 0x62, 0x7d, 0xb6, 0x29, 0xc0, 0x52, 0xea, 0xf5, 0x81, 0x18, 0x0f, 0x35, 0xa9, 0x0e}}},
{{{0xe7, 0x20, 0x72, 0x7c, 0x6d, 0x94, 0x5f, 0x52, 0x44, 0x54, 0xe3, 0xf1, 0xb2, 0xb0, 0x36, 0x46, 0x0f, 0xae, 0x92, 0xe8, 0x70, 0x9d, 0x6e, 0x79, 0xb1, 0xad, 0x37, 0xa9, 0x5f, 0xc0, 0xde, 0x03}} ,
{{0x15, 0x55, 0x37, 0xc6, 0x1c, 0x27, 0x1c, 0x6d, 0x14, 0x4f, 0xca, 0xa4, 0xc4, 0x88, 0x25, 0x46, 0x39, 0xfc, 0x5a, 0xe5, 0xfe, 0x29, 0x11, 0x69, 0xf5, 0x72, 0x84, 0x4d, 0x78, 0x9f, 0x94, 0x15}}},
-{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+{{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
{{{0xec, 0xd3, 0xff, 0x57, 0x0b, 0xb0, 0xb2, 0xdc, 0xf8, 0x4f, 0xe2, 0x12, 0xd5, 0x36, 0xbe, 0x6b, 0x09, 0x43, 0x6d, 0xa3, 0x4d, 0x90, 0x2d, 0xb8, 0x74, 0xe8, 0x71, 0x45, 0x19, 0x8b, 0x0c, 0x6a}} ,
{{0xb8, 0x42, 0x1c, 0x03, 0xad, 0x2c, 0x03, 0x8e, 0xac, 0xd7, 0x98, 0x29, 0x13, 0xc6, 0x02, 0x29, 0xb5, 0xd4, 0xe7, 0xcf, 0xcc, 0x8b, 0x83, 0xec, 0x35, 0xc7, 0x9c, 0x74, 0xb7, 0xad, 0x85, 0x5f}}},
@@ -1868,407 +1729,279 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
{{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}}
};
-static inline void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
+void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
{
- fe25519_mul(&r->x, &p->x, &p->t);
- fe25519_mul(&r->y, &p->y, &p->z);
- fe25519_mul(&r->z, &p->z, &p->t);
+ fe25519_mul(&r->x, &p->x, &p->t);
+ fe25519_mul(&r->y, &p->y, &p->z);
+ fe25519_mul(&r->z, &p->z, &p->t);
}
-static inline void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
+void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
{
- fe25519_mul(&r->x, &p->x, &p->t);
- fe25519_mul(&r->y, &p->y, &p->z);
- fe25519_mul(&r->z, &p->z, &p->t);
+ fe25519_mul(&r->x, &p->x, &p->t);
+ fe25519_mul(&r->y, &p->y, &p->z);
+ fe25519_mul(&r->z, &p->z, &p->t);
}
-static inline void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
+void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
{
- p1p1_to_p2_2(r, p);
- fe25519_mul(&r->t, &p->x, &p->y);
+ p1p1_to_p2_2(r, p);
+ fe25519_mul(&r->t, &p->x, &p->y);
}
-static void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q)
+void ge25519_mixadd2(ge25519_p3 *r, const ge25519_aff *q)
{
- fe25519 a,b,t1,t2,c,d,e,f,g,h,qt;
- fe25519_mul(&qt, &q->x, &q->y);
- fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */
- fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */
- fe25519_sub(&t1, &q->y, &q->x);
- fe25519_add(&t2, &q->y, &q->x);
- fe25519_mul(&a, &a, &t1);
- fe25519_mul(&b, &b, &t2);
- fe25519_sub(&e, &b, &a); /* E = B-A */
- fe25519_add(&h, &b, &a); /* H = B+A */
- fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */
- fe25519_mul(&c, &c, &ge25519_ec2d);
- fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */
- fe25519_sub(&f, &d, &c); /* F = D-C */
- fe25519_add(&g, &d, &c); /* G = D+C */
- fe25519_mul(&r->x, &e, &f);
- fe25519_mul(&r->y, &h, &g);
- fe25519_mul(&r->z, &g, &f);
- fe25519_mul(&r->t, &e, &h);
+ fe25519 a,b,t1,t2,c,d,e,f,g,h,qt;
+ fe25519_mul(&qt, &q->x, &q->y);
+ fe25519_sub(&a, &r->y, &r->x); /* A = (Y1-X1)*(Y2-X2) */
+ fe25519_add(&b, &r->y, &r->x); /* B = (Y1+X1)*(Y2+X2) */
+ fe25519_sub(&t1, &q->y, &q->x);
+ fe25519_add(&t2, &q->y, &q->x);
+ fe25519_mul(&a, &a, &t1);
+ fe25519_mul(&b, &b, &t2);
+ fe25519_sub(&e, &b, &a); /* E = B-A */
+ fe25519_add(&h, &b, &a); /* H = B+A */
+ fe25519_mul(&c, &r->t, &qt); /* C = T1*k*T2 */
+ fe25519_mul(&c, &c, &ge25519_ec2d);
+ fe25519_add(&d, &r->z, &r->z); /* D = Z1*2 */
+ fe25519_sub(&f, &d, &c); /* F = D-C */
+ fe25519_add(&g, &d, &c); /* G = D+C */
+ fe25519_mul(&r->x, &e, &f);
+ fe25519_mul(&r->y, &h, &g);
+ fe25519_mul(&r->z, &g, &f);
+ fe25519_mul(&r->t, &e, &h);
}
-static void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q)
+void add_p1p1(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_p3 *q)
{
- fe25519 a, b, c, d, t;
-
- fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */
- fe25519_sub(&t, &q->y, &q->x);
- fe25519_mul(&a, &a, &t);
- fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */
- fe25519_add(&t, &q->x, &q->y);
- fe25519_mul(&b, &b, &t);
- fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */
- fe25519_mul(&c, &c, &ge25519_ec2d);
- fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */
- fe25519_add(&d, &d, &d);
- fe25519_sub(&r->x, &b, &a); /* E = B-A */
- fe25519_sub(&r->t, &d, &c); /* F = D-C */
- fe25519_add(&r->z, &d, &c); /* G = D+C */
- fe25519_add(&r->y, &b, &a); /* H = B+A */
+ fe25519 a, b, c, d, t;
+
+ fe25519_sub(&a, &p->y, &p->x); /* A = (Y1-X1)*(Y2-X2) */
+ fe25519_sub(&t, &q->y, &q->x);
+ fe25519_mul(&a, &a, &t);
+ fe25519_add(&b, &p->x, &p->y); /* B = (Y1+X1)*(Y2+X2) */
+ fe25519_add(&t, &q->x, &q->y);
+ fe25519_mul(&b, &b, &t);
+ fe25519_mul(&c, &p->t, &q->t); /* C = T1*k*T2 */
+ fe25519_mul(&c, &c, &ge25519_ec2d);
+ fe25519_mul(&d, &p->z, &q->z); /* D = Z1*2*Z2 */
+ fe25519_add(&d, &d, &d);
+ fe25519_sub(&r->x, &b, &a); /* E = B-A */
+ fe25519_sub(&r->t, &d, &c); /* F = D-C */
+ fe25519_add(&r->z, &d, &c); /* G = D+C */
+ fe25519_add(&r->y, &b, &a); /* H = B+A */
}
/* See http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd */
-static void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
+void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
{
- fe25519 a,b,c,d;
- fe25519_square(&a, &p->x);
- fe25519_square(&b, &p->y);
- fe25519_square(&c, &p->z);
- fe25519_add(&c, &c, &c);
- fe25519_neg(&d, &a);
+ fe25519 a,b,c,d;
+ fe25519_square(&a, &p->x);
+ fe25519_square(&b, &p->y);
+ fe25519_square(&c, &p->z);
+ fe25519_add(&c, &c, &c);
+ fe25519_neg(&d, &a);
- fe25519_add(&r->x, &p->x, &p->y);
- fe25519_square(&r->x, &r->x);
- fe25519_sub(&r->x, &r->x, &a);
- fe25519_sub(&r->x, &r->x, &b);
- fe25519_add(&r->z, &d, &b);
- fe25519_sub(&r->t, &r->z, &c);
- fe25519_sub(&r->y, &d, &b);
+ fe25519_add(&r->x, &p->x, &p->y);
+ fe25519_square(&r->x, &r->x);
+ fe25519_sub(&r->x, &r->x, &a);
+ fe25519_sub(&r->x, &r->x, &b);
+ fe25519_add(&r->z, &d, &b);
+ fe25519_sub(&r->t, &r->z, &c);
+ fe25519_sub(&r->y, &d, &b);
}
/* Constant-time version of: if(b) r = p */
-static inline void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
+void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
{
- fe25519_cmov(&r->x, &p->x, b);
- fe25519_cmov(&r->y, &p->y, b);
+ fe25519_cmov(&r->x, &p->x, b);
+ fe25519_cmov(&r->y, &p->y, b);
}
-static inline unsigned char equal(signed char b,signed char c)
+unsigned char equal(signed char b,signed char c)
{
- unsigned char ub = b;
- unsigned char uc = c;
- unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
- crypto_uint32 y = x; /* 0: yes; 1..255: no */
- y -= 1; /* 4294967295: yes; 0..254: no */
- y >>= 31; /* 1: yes; 0: no */
- return (unsigned char)y;
+ unsigned char ub = b;
+ unsigned char uc = c;
+ unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
+ crypto_uint32 y = x; /* 0: yes; 1..255: no */
+ y -= 1; /* 4294967295: yes; 0..254: no */
+ y >>= 31; /* 1: yes; 0: no */
+ return (unsigned char)y;
}
-static inline unsigned char negative(signed char b)
+unsigned char negative(signed char b)
{
- unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
- x >>= 63; /* 1: yes; 0: no */
- return (unsigned char)x;
+ unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
+ x >>= 63; /* 1: yes; 0: no */
+ return (unsigned char)x;
}
-static inline void choose_t(ge25519_aff *t, unsigned long long pos, signed char b)
+void choose_t(ge25519_aff *t, unsigned long long pos, signed char b)
{
- /* constant time */
- fe25519 v;
- *t = ge25519_base_multiples_affine[5*pos+0];
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3));
- cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4));
- fe25519_neg(&v, &t->x);
- fe25519_cmov(&t->x, &v, negative(b));
+ /* constant time */
+ fe25519 v;
+ *t = ge25519_base_multiples_affine[5*pos+0];
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+1],equal(b,1) | equal(b,-1));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+2],equal(b,2) | equal(b,-2));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+3],equal(b,3) | equal(b,-3));
+ cmov_aff(t, &ge25519_base_multiples_affine[5*pos+4],equal(b,-4));
+ fe25519_neg(&v, &t->x);
+ fe25519_cmov(&t->x, &v, negative(b));
}
-static inline void setneutral(ge25519 *r)
+void setneutral(ge25519 *r)
{
- fe25519_setzero(&r->x);
- fe25519_setone(&r->y);
- fe25519_setone(&r->z);
- fe25519_setzero(&r->t);
+ fe25519_setzero(&r->x);
+ fe25519_setone(&r->y);
+ fe25519_setone(&r->z);
+ fe25519_setzero(&r->t);
}
/* return 0 on success, -1 otherwise */
-static int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
-{
- unsigned char par;
- fe25519 t, chk, num, den, den2, den4, den6;
- fe25519_setone(&r->z);
- par = p[31] >> 7;
- fe25519_unpack(&r->y, p);
- fe25519_square(&num, &r->y); /* x = y^2 */
- fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */
- fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
- fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
-
- /* Computation of sqrt(num/den) */
- /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */
- fe25519_square(&den2, &den);
- fe25519_square(&den4, &den2);
- fe25519_mul(&den6, &den4, &den2);
- fe25519_mul(&t, &den6, &num);
- fe25519_mul(&t, &t, &den);
-
- fe25519_pow2523(&t, &t);
- /* 2. computation of r->x = t * num * den^3 */
- fe25519_mul(&t, &t, &num);
- fe25519_mul(&t, &t, &den);
- fe25519_mul(&t, &t, &den);
- fe25519_mul(&r->x, &t, &den);
-
- /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */
- fe25519_square(&chk, &r->x);
- fe25519_mul(&chk, &chk, &den);
- if (!fe25519_iseq_vartime(&chk, &num))
- fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1);
-
- /* 4. Now we have one of the two square roots, except if input was not a square */
- fe25519_square(&chk, &r->x);
- fe25519_mul(&chk, &chk, &den);
- if (!fe25519_iseq_vartime(&chk, &num))
- return -1;
-
- /* 5. Choose the desired square root according to parity: */
- if(fe25519_getparity(&r->x) != (1-par))
- fe25519_neg(&r->x, &r->x);
-
- fe25519_mul(&r->t, &r->x, &r->y);
- return 0;
-}
-
-static inline void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
-{
- fe25519 tx, ty, zi;
- fe25519_invert(&zi, &p->z);
- fe25519_mul(&tx, &p->x, &zi);
- fe25519_mul(&ty, &p->y, &zi);
- fe25519_pack(r, &ty);
- r[31] ^= fe25519_getparity(&tx) << 7;
-}
-
-#if 0
-static int ge25519_isneutral_vartime(const ge25519_p3 *p)
-{
- int ret = 1;
- if(!fe25519_iszero(&p->x)) ret = 0;
- if(!fe25519_iseq_vartime(&p->y, &p->z)) ret = 0;
- return ret;
+int ge25519_unpackneg_vartime(ge25519_p3 *r, const unsigned char p[32])
+{
+ unsigned char par;
+ fe25519 t, chk, num, den, den2, den4, den6;
+ fe25519_setone(&r->z);
+ par = p[31] >> 7;
+ fe25519_unpack(&r->y, p);
+ fe25519_square(&num, &r->y); /* x = y^2 */
+ fe25519_mul(&den, &num, &ge25519_ecd); /* den = dy^2 */
+ fe25519_sub(&num, &num, &r->z); /* x = y^2-1 */
+ fe25519_add(&den, &r->z, &den); /* den = dy^2+1 */
+
+ /* Computation of sqrt(num/den) */
+ /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */
+ fe25519_square(&den2, &den);
+ fe25519_square(&den4, &den2);
+ fe25519_mul(&den6, &den4, &den2);
+ fe25519_mul(&t, &den6, &num);
+ fe25519_mul(&t, &t, &den);
+
+ fe25519_pow2523(&t, &t);
+ /* 2. computation of r->x = t * num * den^3 */
+ fe25519_mul(&t, &t, &num);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&t, &t, &den);
+ fe25519_mul(&r->x, &t, &den);
+
+ /* 3. Check whether sqrt computation gave correct result, multiply by sqrt(-1) if not: */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ fe25519_mul(&r->x, &r->x, &ge25519_sqrtm1);
+
+ /* 4. Now we have one of the two square roots, except if input was not a square */
+ fe25519_square(&chk, &r->x);
+ fe25519_mul(&chk, &chk, &den);
+ if (!fe25519_iseq_vartime(&chk, &num))
+ return -1;
+
+ /* 5. Choose the desired square root according to parity: */
+ if(fe25519_getparity(&r->x) != (1-par))
+ fe25519_neg(&r->x, &r->x);
+
+ fe25519_mul(&r->t, &r->x, &r->y);
+ return 0;
+}
+
+void ge25519_pack(unsigned char r[32], const ge25519_p3 *p)
+{
+ fe25519 tx, ty, zi;
+ fe25519_invert(&zi, &p->z);
+ fe25519_mul(&tx, &p->x, &zi);
+ fe25519_mul(&ty, &p->y, &zi);
+ fe25519_pack(r, &ty);
+ r[31] ^= fe25519_getparity(&tx) << 7;
}
-#endif
/* computes [s1]p1 + [s2]p2 */
-static void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2)
-{
- ge25519_p1p1 tp1p1;
- ge25519_p3 pre[16];
- char *pre5 = (char *)(&(pre[5])); // eliminate type punning warning
- unsigned char b[127];
- int i;
-
- /* precomputation s2 s1 */
- setneutral(pre); /* 00 00 */
- pre[1] = *p1; /* 00 01 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */
- add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */
- pre[4] = *p2; /* 01 00 */
- add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */
- add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */
- add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */
- add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */
- dbl_p1p1(&tp1p1,(ge25519_p2 *)pre5); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */
- add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */
- add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */
- add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */
- add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */
- add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */
-
- sc25519_2interleave2(b,s1,s2);
-
- /* scalar multiplication */
- *r = pre[b[126]];
- for(i=125;i>=0;i--)
- {
- dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
- p1p1_to_p2((ge25519_p2 *) r, &tp1p1);
- dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
- if(b[i]!=0)
- {
- p1p1_to_p3(r, &tp1p1);
- add_p1p1(&tp1p1, r, &pre[b[i]]);
- }
- if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
- else p1p1_to_p3(r, &tp1p1);
- }
-}
-
-static inline void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
-{
- signed char b[85];
- int i;
- ge25519_aff t;
- sc25519_window3(b,s);
-
- choose_t((ge25519_aff *)r, 0, b[0]);
- fe25519_setone(&r->z);
- fe25519_mul(&r->t, &r->x, &r->y);
- for(i=1;i<85;i++)
- {
- choose_t(&t, (unsigned long long) i, b[i]);
- ge25519_mixadd2(r, &t);
- }
-}
-
-static inline void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
-{
- unsigned long long i;
-
- for (i = 0;i < 32;++i) playground[i] = sm[i];
- for (i = 32;i < 64;++i) playground[i] = pk[i-32];
- for (i = 64;i < smlen;++i) playground[i] = sm[i];
-
- //crypto_hash_sha512(hram,playground,smlen);
- SHA512::hash(hram,playground,(unsigned int)smlen);
-}
-
-// This is the original sign and verify code -- the versions in sign() and
-// verify() below the fold are slightly modified in terms of how they behave
-// in relation to the message, but the algorithms are the same.
-
-#if 0
-int crypto_sign_keypair(
- unsigned char *pk,
- unsigned char *sk
- )
-{
- sc25519 scsk;
- ge25519 gepk;
- unsigned char extsk[64];
- int i;
-
- randombytes(sk, 32);
- crypto_hash_sha512(extsk, sk, 32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- sc25519_from32bytes(&scsk,extsk);
-
- ge25519_scalarmult_base(&gepk, &scsk);
- ge25519_pack(pk, &gepk);
- for(i=0;i<32;i++)
- sk[32 + i] = pk[i];
- return 0;
-}
-
-static int crypto_sign(
- unsigned char *sm,unsigned long long *smlen,
- const unsigned char *m,unsigned long long mlen,
- const unsigned char *sk
- )
-{
- sc25519 sck, scs, scsk;
- ge25519 ger;
- unsigned char r[32];
- unsigned char s[32];
- unsigned char extsk[64];
- unsigned long long i;
- unsigned char hmg[crypto_hash_sha512_BYTES];
- unsigned char hram[crypto_hash_sha512_BYTES];
-
- crypto_hash_sha512(extsk, sk, 32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- *smlen = mlen+64;
- for(i=0;i<mlen;i++)
- sm[64 + i] = m[i];
- for(i=0;i<32;i++)
- sm[32 + i] = extsk[32+i];
-
- crypto_hash_sha512(hmg, sm+32, mlen+32); /* Generate k as h(extsk[32],...,extsk[63],m) */
-
- /* Computation of R */
- sc25519_from64bytes(&sck, hmg);
- ge25519_scalarmult_base(&ger, &sck);
- ge25519_pack(r, &ger);
-
- /* Computation of s */
- for(i=0;i<32;i++)
- sm[i] = r[i];
+void ge25519_double_scalarmult_vartime(ge25519_p3 *r, const ge25519_p3 *p1, const sc25519 *s1, const ge25519_p3 *p2, const sc25519 *s2)
+{
+ ge25519_p1p1 tp1p1;
+ ge25519_p3 pre[16];
+ char *pre5 = (char *)(&(pre[5])); // eliminate type punning warning
+ unsigned char b[127];
+ int i;
+
+ /* precomputation s2 s1 */
+ setneutral(pre); /* 00 00 */
+ pre[1] = *p1; /* 00 01 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)p1); p1p1_to_p3( &pre[2], &tp1p1); /* 00 10 */
+ add_p1p1(&tp1p1,&pre[1], &pre[2]); p1p1_to_p3( &pre[3], &tp1p1); /* 00 11 */
+ pre[4] = *p2; /* 01 00 */
+ add_p1p1(&tp1p1,&pre[1], &pre[4]); p1p1_to_p3( &pre[5], &tp1p1); /* 01 01 */
+ add_p1p1(&tp1p1,&pre[2], &pre[4]); p1p1_to_p3( &pre[6], &tp1p1); /* 01 10 */
+ add_p1p1(&tp1p1,&pre[3], &pre[4]); p1p1_to_p3( &pre[7], &tp1p1); /* 01 11 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)p2); p1p1_to_p3( &pre[8], &tp1p1); /* 10 00 */
+ add_p1p1(&tp1p1,&pre[1], &pre[8]); p1p1_to_p3( &pre[9], &tp1p1); /* 10 01 */
+ dbl_p1p1(&tp1p1,(ge25519_p2 *)pre5); p1p1_to_p3(&pre[10], &tp1p1); /* 10 10 */
+ add_p1p1(&tp1p1,&pre[3], &pre[8]); p1p1_to_p3(&pre[11], &tp1p1); /* 10 11 */
+ add_p1p1(&tp1p1,&pre[4], &pre[8]); p1p1_to_p3(&pre[12], &tp1p1); /* 11 00 */
+ add_p1p1(&tp1p1,&pre[1],&pre[12]); p1p1_to_p3(&pre[13], &tp1p1); /* 11 01 */
+ add_p1p1(&tp1p1,&pre[2],&pre[12]); p1p1_to_p3(&pre[14], &tp1p1); /* 11 10 */
+ add_p1p1(&tp1p1,&pre[3],&pre[12]); p1p1_to_p3(&pre[15], &tp1p1); /* 11 11 */
+
+ sc25519_2interleave2(b,s1,s2);
+
+ /* scalar multiplication */
+ *r = pre[b[126]];
+ for(i=125;i>=0;i--)
+ {
+ dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
+ p1p1_to_p2((ge25519_p2 *) r, &tp1p1);
+ dbl_p1p1(&tp1p1, (ge25519_p2 *)r);
+ if(b[i]!=0)
+ {
+ p1p1_to_p3(r, &tp1p1);
+ add_p1p1(&tp1p1, r, &pre[b[i]]);
+ }
+ if(i != 0) p1p1_to_p2((ge25519_p2 *)r, &tp1p1);
+ else p1p1_to_p3(r, &tp1p1);
+ }
+}
- get_hram(hram, sm, sk+32, sm, mlen+64);
+void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
+{
+ signed char b[85];
+ int i;
+ ge25519_aff t;
+ sc25519_window3(b,s);
+
+ choose_t((ge25519_aff *)r, 0, b[0]);
+ fe25519_setone(&r->z);
+ fe25519_mul(&r->t, &r->x, &r->y);
+ for(i=1;i<85;i++)
+ {
+ choose_t(&t, (unsigned long long) i, b[i]);
+ ge25519_mixadd2(r, &t);
+ }
+}
- sc25519_from64bytes(&scs, hram);
- sc25519_from32bytes(&scsk, extsk);
- sc25519_mul(&scs, &scs, &scsk);
-
- sc25519_add(&scs, &scs, &sck);
-
- sc25519_to32bytes(s,&scs); /* cat s */
- for(i=0;i<32;i++)
- sm[32 + i] = s[i];
-
- return 0;
-}
-
-static int crypto_sign_open(
- unsigned char *m,unsigned long long *mlen,
- const unsigned char *sm,unsigned long long smlen,
- const unsigned char *pk
- )
+void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
{
- int i, ret;
- unsigned char t2[32];
- ge25519 get1, get2;
- sc25519 schram, scs;
- unsigned char hram[crypto_hash_sha512_BYTES];
-
- *mlen = (unsigned long long) -1;
- if (smlen < 64) return -1;
-
- if (ge25519_unpackneg_vartime(&get1, pk)) return -1;
-
- get_hram(hram,sm,pk,m,smlen);
-
- sc25519_from64bytes(&schram, hram);
-
- sc25519_from32bytes(&scs, sm+32);
-
- ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
- ge25519_pack(t2, &get2);
-
- ret = crypto_verify_32(sm, t2);
-
- if (!ret)
- {
- for(i=0;i<smlen-64;i++)
- m[i] = sm[i + 64];
- *mlen = smlen-64;
- }
- else
- {
- for(i=0;i<smlen-64;i++)
- m[i] = 0;
- }
- return ret;
-}
-#endif // 0
+ unsigned long long i;
+
+ for (i = 0;i < 32;++i) playground[i] = sm[i];
+ for (i = 32;i < 64;++i) playground[i] = pk[i-32];
+ for (i = 64;i < smlen;++i) playground[i] = sm[i];
+
+ //crypto_hash_sha512(hram,playground,smlen);
+ ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
+}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
+} // anonymous namespace
+
+#ifdef ZT_USE_FAST_X64_ED25519
+extern "C" void ed25519_amd64_asm_sign(const unsigned char *sk,const unsigned char *pk,const unsigned char *m,const unsigned int mlen,unsigned char *sig);
+#endif
+
+namespace ZeroTier {
+
void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void *keybuf,unsigned int keylen)
- throw()
{
unsigned char rawkey[32];
unsigned char digest[64];
@@ -2285,114 +2018,113 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
}
void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
- throw()
-{
- sc25519 sck, scs, scsk;
- ge25519 ger;
- unsigned char r[32];
- unsigned char s[32];
- unsigned char extsk[64];
- unsigned char hmg[crypto_hash_sha512_BYTES];
- unsigned char hram[crypto_hash_sha512_BYTES];
- unsigned char *sig = (unsigned char *)signature;
- unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
-
- SHA512::hash(digest,msg,len);
-
- SHA512::hash(extsk,myPrivate.data + 32,32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
-
- for(unsigned int i=0;i<32;i++)
- sig[32 + i] = extsk[32 + i];
- for(unsigned int i=0;i<32;i++)
- sig[64 + i] = digest[i];
-
- SHA512::hash(hmg,sig + 32,64);
- //crypto_hash_sha512(hmg, sm+32, mlen+32); /* Generate k as h(extsk[32],...,extsk[63],m) */
-
- /* Computation of R */
- sc25519_from64bytes(&sck, hmg);
- ge25519_scalarmult_base(&ger, &sck);
- ge25519_pack(r, &ger);
-
- /* Computation of s */
- for(unsigned int i=0;i<32;i++)
- sig[i] = r[i];
-
- get_hram(hram,sig,myPublic.data + 32,sig,96);
-
- sc25519_from64bytes(&scs, hram);
- sc25519_from32bytes(&scsk, extsk);
- sc25519_mul(&scs, &scs, &scsk);
-
- sc25519_add(&scs, &scs, &sck);
-
- sc25519_to32bytes(s,&scs); /* cat s */
- for(unsigned int i=0;i<32;i++)
- sig[32 + i] = s[i];
+{
+#ifdef ZT_USE_FAST_X64_ED25519
+ ed25519_amd64_asm_sign(myPrivate.data + 32,myPublic.data + 32,(const unsigned char *)msg,len,(unsigned char *)signature);
+#else
+ sc25519 sck, scs, scsk;
+ ge25519 ger;
+ unsigned char r[32];
+ unsigned char s[32];
+ unsigned char extsk[64];
+ unsigned char hmg[crypto_hash_sha512_BYTES];
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char *sig = (unsigned char *)signature;
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+
+ SHA512::hash(digest,msg,len);
+
+ SHA512::hash(extsk,myPrivate.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = extsk[32 + i];
+ for(unsigned int i=0;i<32;i++)
+ sig[64 + i] = digest[i];
+
+ SHA512::hash(hmg,sig + 32,64);
+
+ /* Computation of R */
+ sc25519_from64bytes(&sck, hmg);
+ ge25519_scalarmult_base(&ger, &sck);
+ ge25519_pack(r, &ger);
+
+ /* Computation of s */
+ for(unsigned int i=0;i<32;i++)
+ sig[i] = r[i];
+
+ get_hram(hram,sig,myPublic.data + 32,sig,96);
+
+ sc25519_from64bytes(&scs, hram);
+ sc25519_from32bytes(&scsk, extsk);
+ sc25519_mul(&scs, &scs, &scsk);
+
+ sc25519_add(&scs, &scs, &sck);
+
+ sc25519_to32bytes(s,&scs); /* cat s */
+ for(unsigned int i=0;i<32;i++)
+ sig[32 + i] = s[i];
+#endif
}
bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len,const void *signature)
- throw()
{
- unsigned char t2[32];
- ge25519 get1, get2;
- sc25519 schram, scs;
- unsigned char hram[crypto_hash_sha512_BYTES];
- unsigned char m[96];
- unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
- const unsigned char *sig = (const unsigned char *)signature;
+ unsigned char t2[32];
+ ge25519 get1, get2;
+ sc25519 schram, scs;
+ unsigned char hram[crypto_hash_sha512_BYTES];
+ unsigned char m[96];
+ unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
+ const unsigned char *sig = (const unsigned char *)signature;
- // First check the message's integrity
- SHA512::hash(digest,msg,len);
- if (!Utils::secureEq(sig + 64,digest,32))
- return false;
+ // First check the message's integrity
+ SHA512::hash(digest,msg,len);
+ if (!Utils::secureEq(sig + 64,digest,32))
+ return false;
- if (ge25519_unpackneg_vartime(&get1,their.data + 32))
- return false;
+ if (ge25519_unpackneg_vartime(&get1,their.data + 32))
+ return false;
- get_hram(hram,sig,their.data + 32,m,96);
+ get_hram(hram,sig,their.data + 32,m,96);
- sc25519_from64bytes(&schram, hram);
+ sc25519_from64bytes(&schram, hram);
- sc25519_from32bytes(&scs, sig+32);
+ sc25519_from32bytes(&scs, sig+32);
- ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
- ge25519_pack(t2, &get2);
+ ge25519_double_scalarmult_vartime(&get2, &get1, &schram, &ge25519_base, &scs);
+ ge25519_pack(t2, &get2);
- return Utils::secureEq(sig,t2,32);
+ return Utils::secureEq(sig,t2,32);
}
void C25519::_calcPubDH(C25519::Pair &kp)
- throw()
{
- // First 32 bytes of pub and priv are the keys for ECDH key
- // agreement. This generates the public portion from the private.
- crypto_scalarmult_base(kp.pub.data,kp.priv.data);
+ // First 32 bytes of pub and priv are the keys for ECDH key
+ // agreement. This generates the public portion from the private.
+ crypto_scalarmult_base(kp.pub.data,kp.priv.data);
}
void C25519::_calcPubED(C25519::Pair &kp)
- throw()
-{
- unsigned char extsk[64];
- sc25519 scsk;
- ge25519 gepk;
-
- // Second 32 bytes of pub and priv are the keys for ed25519
- // signing and verification.
- SHA512::hash(extsk,kp.priv.data + 32,32);
- extsk[0] &= 248;
- extsk[31] &= 127;
- extsk[31] |= 64;
- sc25519_from32bytes(&scsk,extsk);
- ge25519_scalarmult_base(&gepk,&scsk);
- ge25519_pack(kp.pub.data + 32,&gepk);
- // In NaCl, the public key is crammed into the next 32 bytes
- // of the private key for signing since both keys are required
- // to sign. In this version we just get it from kp.pub, so we
- // leave that out of private.
+{
+ unsigned char extsk[64];
+ sc25519 scsk;
+ ge25519 gepk;
+
+ // Second 32 bytes of pub and priv are the keys for ed25519
+ // signing and verification.
+ SHA512::hash(extsk,kp.priv.data + 32,32);
+ extsk[0] &= 248;
+ extsk[31] &= 127;
+ extsk[31] |= 64;
+ sc25519_from32bytes(&scsk,extsk);
+ ge25519_scalarmult_base(&gepk,&scsk);
+ ge25519_pack(kp.pub.data + 32,&gepk);
+ // In NaCl, the public key is crammed into the next 32 bytes
+ // of the private key for signing since both keys are required
+ // to sign. In this version we just get it from kp.pub, so we
+ // leave that out of private.
}
} // namespace ZeroTier
diff --git a/node/C25519.hpp b/node/C25519.hpp
index b19d9693..2e9184a0 100644
--- a/node/C25519.hpp
+++ b/node/C25519.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,12 +14,19 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_C25519_HPP
#define ZT_C25519_HPP
-#include "Array.hpp"
#include "Utils.hpp"
namespace ZeroTier {
@@ -34,37 +41,18 @@ namespace ZeroTier {
class C25519
{
public:
- /**
- * Public key (both crypto and signing)
- */
- typedef Array<unsigned char,ZT_C25519_PUBLIC_KEY_LEN> Public; // crypto key, signing key (both 32 bytes)
-
- /**
- * Private key (both crypto and signing)
- */
- typedef Array<unsigned char,ZT_C25519_PRIVATE_KEY_LEN> Private; // crypto key, signing key (both 32 bytes)
-
- /**
- * Message signature
- */
- typedef Array<unsigned char,ZT_C25519_SIGNATURE_LEN> Signature;
-
- /**
- * Public/private key pair
- */
- typedef struct {
- Public pub;
- Private priv;
- } Pair;
+ struct Public { uint8_t data[ZT_C25519_PUBLIC_KEY_LEN]; };
+ struct Private { uint8_t data[ZT_C25519_PRIVATE_KEY_LEN]; };
+ struct Signature { uint8_t data[ZT_C25519_SIGNATURE_LEN]; };
+ struct Pair { Public pub; Private priv; };
/**
* Generate a C25519 elliptic curve key pair
*/
static inline Pair generate()
- throw()
{
Pair kp;
- Utils::getSecureRandom(kp.priv.data,(unsigned int)kp.priv.size());
+ Utils::getSecureRandom(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
_calcPubDH(kp);
_calcPubED(kp);
return kp;
@@ -85,11 +73,10 @@ public:
*/
template<typename F>
static inline Pair generateSatisfying(F cond)
- throw()
{
Pair kp;
void *const priv = (void *)kp.priv.data;
- Utils::getSecureRandom(priv,(unsigned int)kp.priv.size());
+ Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
_calcPubED(kp); // do Ed25519 key -- bytes 32-63 of pub and priv
do {
++(((uint64_t *)priv)[1]);
@@ -110,13 +97,8 @@ public:
* @param keybuf Buffer to fill
* @param keylen Number of key bytes to generate
*/
- static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen)
- throw();
- static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen)
- throw()
- {
- agree(mine.priv,their,keybuf,keylen);
- }
+ static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
+ static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen) { agree(mine.priv,their,keybuf,keylen); }
/**
* Sign a message with a sender's key pair
@@ -137,13 +119,8 @@ public:
* @param len Length of message in bytes
* @param signature Buffer to fill with signature -- MUST be 96 bytes in length
*/
- static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature)
- throw();
- static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature)
- throw()
- {
- sign(mine.priv,mine.pub,msg,len,signature);
- }
+ static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
+ static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature) { sign(mine.priv,mine.pub,msg,len,signature); }
/**
* Sign a message with a sender's key pair
@@ -155,14 +132,12 @@ public:
* @return Signature
*/
static inline Signature sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len)
- throw()
{
Signature sig;
sign(myPrivate,myPublic,msg,len,sig.data);
return sig;
}
static inline Signature sign(const Pair &mine,const void *msg,unsigned int len)
- throw()
{
Signature sig;
sign(mine.priv,mine.pub,msg,len,sig.data);
@@ -178,8 +153,7 @@ public:
* @param signature 96-byte signature
* @return True if signature is valid and the message is authentic and unmodified
*/
- static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature)
- throw();
+ static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature);
/**
* Verify a message's signature
@@ -191,7 +165,6 @@ public:
* @return True if signature is valid and the message is authentic and unmodified
*/
static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature)
- throw()
{
return verify(their,msg,len,signature.data);
}
@@ -199,13 +172,11 @@ public:
private:
// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv
// this is the ECDH key
- static void _calcPubDH(Pair &kp)
- throw();
+ static void _calcPubDH(Pair &kp);
// derive 2nd 32 bytes of kp.pub from 2nd 32 bytes of kp.priv
// this is the Ed25519 sign/verify key
- static void _calcPubED(Pair &kp)
- throw();
+ static void _calcPubED(Pair &kp);
};
} // namespace ZeroTier
diff --git a/node/Capability.cpp b/node/Capability.cpp
new file mode 100644
index 00000000..fb52be8a
--- /dev/null
+++ b/node/Capability.cpp
@@ -0,0 +1,74 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Capability.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ try {
+ // There must be at least one entry, and sanity check for bad chain max length
+ if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
+ return -1;
+
+ // Validate all entries in chain of custody
+ Buffer<(sizeof(Capability) * 2)> tmp;
+ this->serialize(tmp,true);
+ for(unsigned int c=0;c<_maxCustodyChainLength;++c) {
+ if (c == 0) {
+ if ((!_custody[c].to)||(!_custody[c].from)||(_custody[c].from != Network::controllerFor(_nwid)))
+ return -1; // the first entry must be present and from the network's controller
+ } else {
+ if (!_custody[c].to)
+ return 0; // all previous entries were valid, so we are valid
+ else if ((!_custody[c].from)||(_custody[c].from != _custody[c-1].to))
+ return -1; // otherwise if we have another entry it must be from the previous holder in the chain
+ }
+
+ const Identity id(RR->topology->getIdentity(tPtr,_custody[c].from));
+ if (id) {
+ if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
+ return -1;
+ } else {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_custody[c].from);
+ return 1;
+ }
+ }
+
+ // We reached max custody chain length and everything was valid
+ return 0;
+ } catch ( ... ) {}
+ return -1;
+}
+
+} // namespace ZeroTier
diff --git a/node/Capability.hpp b/node/Capability.hpp
new file mode 100644
index 00000000..91a46566
--- /dev/null
+++ b/node/Capability.hpp
@@ -0,0 +1,493 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_CAPABILITY_HPP
+#define ZT_CAPABILITY_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "Credential.hpp"
+#include "Address.hpp"
+#include "C25519.hpp"
+#include "Utils.hpp"
+#include "Buffer.hpp"
+#include "Identity.hpp"
+#include "../include/ZeroTierOne.h"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * A set of grouped and signed network flow rules
+ *
+ * On the sending side the sender does the following for each packet:
+ *
+ * (1) Evaluates its capabilities in ascending order of ID to determine
+ * which capability allows it to transmit this packet.
+ * (2) If it has not done so lately, it then sends this capability to the
+ * receving peer ("presents" it).
+ * (3) The sender then sends the packet.
+ *
+ * On the receiving side the receiver evaluates the capabilities presented
+ * by the sender. If any valid un-expired capability allows this packet it
+ * is accepted.
+ *
+ * Note that this is after evaluation of network scope rules and only if
+ * network scope rules do not deliver an explicit match.
+ *
+ * Capabilities support a chain of custody. This is currently unused but
+ * in the future would allow the publication of capabilities that can be
+ * handed off between nodes. Limited transferrability of capabilities is
+ * a feature of true capability based security.
+ */
+class Capability : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
+
+ Capability()
+ {
+ memset(this,0,sizeof(Capability));
+ }
+
+ /**
+ * @param id Capability ID
+ * @param nwid Network ID
+ * @param ts Timestamp (at controller)
+ * @param mccl Maximum custody chain length (1 to create non-transferrable capability)
+ * @param rules Network flow rules for this capability
+ * @param ruleCount Number of flow rules
+ */
+ Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
+ {
+ memset(this,0,sizeof(Capability));
+ _nwid = nwid;
+ _ts = ts;
+ _id = id;
+ _maxCustodyChainLength = (mccl > 0) ? ((mccl < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) ? mccl : (unsigned int)ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH) : 1;
+ _ruleCount = (ruleCount < ZT_MAX_CAPABILITY_RULES) ? ruleCount : ZT_MAX_CAPABILITY_RULES;
+ if (_ruleCount)
+ ZT_FAST_MEMCPY(_rules,rules,sizeof(ZT_VirtualNetworkRule) * _ruleCount);
+ }
+
+ /**
+ * @return Rules -- see ruleCount() for size of array
+ */
+ inline const ZT_VirtualNetworkRule *rules() const { return _rules; }
+
+ /**
+ * @return Number of rules in rules()
+ */
+ inline unsigned int ruleCount() const { return _ruleCount; }
+
+ /**
+ * @return ID and evaluation order of this capability in network
+ */
+ inline uint32_t id() const { return _id; }
+
+ /**
+ * @return Network ID for which this capability was issued
+ */
+ inline uint64_t networkId() const { return _nwid; }
+
+ /**
+ * @return Timestamp
+ */
+ inline int64_t timestamp() const { return _ts; }
+
+ /**
+ * @return Last 'to' address in chain of custody
+ */
+ inline Address issuedTo() const
+ {
+ Address i2;
+ for(unsigned int i=0;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
+ if (!_custody[i].to)
+ return i2;
+ else i2 = _custody[i].to;
+ }
+ return i2;
+ }
+
+ /**
+ * Sign this capability and add signature to its chain of custody
+ *
+ * If this returns false, this object should be considered to be
+ * in an undefined state and should be discarded. False can be returned
+ * if there is no more room for signatures (max chain length reached)
+ * or if the 'from' identity does not include a secret key to allow
+ * it to sign anything.
+ *
+ * @param from Signing identity (must have secret)
+ * @param to Recipient of this signature
+ * @return True if signature successful and chain of custody appended
+ */
+ inline bool sign(const Identity &from,const Address &to)
+ {
+ try {
+ for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
+ if (!(_custody[i].to)) {
+ Buffer<(sizeof(Capability) * 2)> tmp;
+ this->serialize(tmp,true);
+ _custody[i].to = to;
+ _custody[i].from = from.address();
+ _custody[i].signature = from.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ }
+ } catch ( ... ) {}
+ return false;
+ }
+
+ /**
+ * Verify this capability's chain of custody and signatures
+ *
+ * @param RR Runtime environment to provide for peer lookup, etc.
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
+ {
+ for(unsigned int i=0;i<ruleCount;++i) {
+ // Each rule consists of its 8-bit type followed by the size of that type's
+ // field followed by field data. The inclusion of the size will allow non-supported
+ // rules to be ignored but still parsed.
+ b.append((uint8_t)rules[i].t);
+ switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x3f)) {
+ default:
+ b.append((uint8_t)0);
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ b.append((uint8_t)14);
+ b.append((uint64_t)rules[i].v.fwd.address);
+ b.append((uint32_t)rules[i].v.fwd.flags);
+ b.append((uint16_t)rules[i].v.fwd.length); // unused for redirect
+ break;
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ b.append((uint8_t)5);
+ Address(rules[i].v.zt).appendTo(b);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ b.append((uint8_t)2);
+ b.append((uint16_t)rules[i].v.vlanId);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ b.append((uint8_t)1);
+ b.append((uint8_t)rules[i].v.vlanPcp);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ b.append((uint8_t)1);
+ b.append((uint8_t)rules[i].v.vlanDei);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ b.append((uint8_t)6);
+ b.append(rules[i].v.mac,6);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ b.append((uint8_t)5);
+ b.append(&(rules[i].v.ipv4.ip),4);
+ b.append((uint8_t)rules[i].v.ipv4.mask);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ b.append((uint8_t)17);
+ b.append(rules[i].v.ipv6.ip,16);
+ b.append((uint8_t)rules[i].v.ipv6.mask);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ b.append((uint8_t)3);
+ b.append((uint8_t)rules[i].v.ipTos.mask);
+ b.append((uint8_t)rules[i].v.ipTos.value[0]);
+ b.append((uint8_t)rules[i].v.ipTos.value[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ b.append((uint8_t)1);
+ b.append((uint8_t)rules[i].v.ipProtocol);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ b.append((uint8_t)2);
+ b.append((uint16_t)rules[i].v.etherType);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ b.append((uint8_t)3);
+ b.append((uint8_t)rules[i].v.icmp.type);
+ b.append((uint8_t)rules[i].v.icmp.code);
+ b.append((uint8_t)rules[i].v.icmp.flags);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ b.append((uint8_t)4);
+ b.append((uint16_t)rules[i].v.port[0]);
+ b.append((uint16_t)rules[i].v.port[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ b.append((uint8_t)8);
+ b.append((uint64_t)rules[i].v.characteristics);
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ b.append((uint8_t)4);
+ b.append((uint16_t)rules[i].v.frameSize[0]);
+ b.append((uint16_t)rules[i].v.frameSize[1]);
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ b.append((uint8_t)4);
+ b.append((uint32_t)rules[i].v.randomProbability);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
+ b.append((uint8_t)8);
+ b.append((uint32_t)rules[i].v.tag.id);
+ b.append((uint32_t)rules[i].v.tag.value);
+ break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ b.append((uint8_t)19);
+ b.append((uint64_t)rules[i].v.intRange.start);
+ b.append((uint64_t)(rules[i].v.intRange.start + (uint64_t)rules[i].v.intRange.end)); // more future-proof
+ b.append((uint16_t)rules[i].v.intRange.idx);
+ b.append((uint8_t)rules[i].v.intRange.format);
+ break;
+ }
+ }
+ }
+
+ template<unsigned int C>
+ static inline void deserializeRules(const Buffer<C> &b,unsigned int &p,ZT_VirtualNetworkRule *rules,unsigned int &ruleCount,const unsigned int maxRuleCount)
+ {
+ while ((ruleCount < maxRuleCount)&&(p < b.size())) {
+ rules[ruleCount].t = (uint8_t)b[p++];
+ const unsigned int fieldLen = (unsigned int)b[p++];
+ switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x3f)) {
+ default:
+ break;
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ rules[ruleCount].v.fwd.address = b.template at<uint64_t>(p);
+ rules[ruleCount].v.fwd.flags = b.template at<uint32_t>(p + 8);
+ rules[ruleCount].v.fwd.length = b.template at<uint16_t>(p + 12);
+ break;
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ rules[ruleCount].v.zt = Address(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ rules[ruleCount].v.vlanId = b.template at<uint16_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ rules[ruleCount].v.vlanPcp = (uint8_t)b[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ rules[ruleCount].v.vlanDei = (uint8_t)b[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ ZT_FAST_MEMCPY(rules[ruleCount].v.mac,b.field(p,6),6);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ ZT_FAST_MEMCPY(&(rules[ruleCount].v.ipv4.ip),b.field(p,4),4);
+ rules[ruleCount].v.ipv4.mask = (uint8_t)b[p + 4];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ ZT_FAST_MEMCPY(rules[ruleCount].v.ipv6.ip,b.field(p,16),16);
+ rules[ruleCount].v.ipv6.mask = (uint8_t)b[p + 16];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ rules[ruleCount].v.ipTos.mask = (uint8_t)b[p];
+ rules[ruleCount].v.ipTos.value[0] = (uint8_t)b[p+1];
+ rules[ruleCount].v.ipTos.value[1] = (uint8_t)b[p+2];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ rules[ruleCount].v.ipProtocol = (uint8_t)b[p];
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ rules[ruleCount].v.etherType = b.template at<uint16_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ rules[ruleCount].v.icmp.type = (uint8_t)b[p];
+ rules[ruleCount].v.icmp.code = (uint8_t)b[p+1];
+ rules[ruleCount].v.icmp.flags = (uint8_t)b[p+2];
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ rules[ruleCount].v.port[0] = b.template at<uint16_t>(p);
+ rules[ruleCount].v.port[1] = b.template at<uint16_t>(p + 2);
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
+ rules[ruleCount].v.characteristics = b.template at<uint64_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ rules[ruleCount].v.frameSize[0] = b.template at<uint16_t>(p);
+ rules[ruleCount].v.frameSize[1] = b.template at<uint16_t>(p + 2);
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ rules[ruleCount].v.randomProbability = b.template at<uint32_t>(p);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL:
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER:
+ rules[ruleCount].v.tag.id = b.template at<uint32_t>(p);
+ rules[ruleCount].v.tag.value = b.template at<uint32_t>(p + 4);
+ break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE:
+ rules[ruleCount].v.intRange.start = b.template at<uint64_t>(p);
+ rules[ruleCount].v.intRange.end = (uint32_t)(b.template at<uint64_t>(p + 8) - rules[ruleCount].v.intRange.start);
+ rules[ruleCount].v.intRange.idx = b.template at<uint16_t>(p + 16);
+ rules[ruleCount].v.intRange.format = (uint8_t)b[p + 18];
+ break;
+ }
+ p += fieldLen;
+ ++ruleCount;
+ }
+ }
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ // These are the same between Tag and Capability
+ b.append(_nwid);
+ b.append(_ts);
+ b.append(_id);
+
+ b.append((uint16_t)_ruleCount);
+ serializeRules(b,_rules,_ruleCount);
+ b.append((uint8_t)_maxCustodyChainLength);
+
+ if (!forSign) {
+ for(unsigned int i=0;;++i) {
+ if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) {
+ _custody[i].to.appendTo(b);
+ _custody[i].from.appendTo(b);
+ b.append((uint8_t)1); // 1 == Ed25519 signature
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_custody[i].signature.data,ZT_C25519_SIGNATURE_LEN);
+ } else {
+ b.append((unsigned char)0,ZT_ADDRESS_LENGTH); // zero 'to' terminates chain
+ break;
+ }
+ }
+ }
+
+ // This is the size of any additional fields, currently 0.
+ b.append((uint16_t)0);
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ memset(this,0,sizeof(Capability));
+
+ unsigned int p = startAt;
+
+ _nwid = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+
+ const unsigned int rc = b.template at<uint16_t>(p); p += 2;
+ if (rc > ZT_MAX_CAPABILITY_RULES)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+ deserializeRules(b,p,_rules,_ruleCount,rc);
+
+ _maxCustodyChainLength = (unsigned int)b[p++];
+ if ((_maxCustodyChainLength < 1)||(_maxCustodyChainLength > ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ for(unsigned int i=0;;++i) {
+ const Address to(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (!to)
+ break;
+ if ((i >= _maxCustodyChainLength)||(i >= ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH))
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+ _custody[i].to = to;
+ _custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ p += 2;
+ ZT_FAST_MEMCPY(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const Capability &c) const { return (_id < c._id); }
+
+ inline bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); }
+ inline bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); }
+
+private:
+ uint64_t _nwid;
+ int64_t _ts;
+ uint32_t _id;
+
+ unsigned int _maxCustodyChainLength;
+
+ unsigned int _ruleCount;
+ ZT_VirtualNetworkRule _rules[ZT_MAX_CAPABILITY_RULES];
+
+ struct {
+ Address to;
+ Address from;
+ C25519::Signature signature;
+ } _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp
index 55537fd9..8184ce99 100644
--- a/node/CertificateOfMembership.cpp
+++ b/node/CertificateOfMembership.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,9 +14,22 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "CertificateOfMembership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -45,6 +58,7 @@ void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t m
std::string CertificateOfMembership::toString() const
{
+ char tmp[ZT_NETWORK_COM_MAX_QUALIFIERS * 32];
std::string s;
s.append("1:"); // COM_UINT64_ED25519
@@ -57,7 +71,7 @@ std::string CertificateOfMembership::toString() const
buf[ptr++] = Utils::hton(_qualifiers[i].value);
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
}
- s.append(Utils::hex(buf,ptr * sizeof(uint64_t)));
+ s.append(Utils::hex(buf,ptr * sizeof(uint64_t),tmp));
delete [] buf;
} catch ( ... ) {
delete [] buf;
@@ -66,11 +80,11 @@ std::string CertificateOfMembership::toString() const
s.push_back(':');
- s.append(_signedBy.toString());
+ s.append(_signedBy.toString(tmp));
if (_signedBy) {
s.push_back(':');
- s.append(Utils::hex(_signature.data,(unsigned int)_signature.size()));
+ s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp));
}
return s;
@@ -80,7 +94,7 @@ void CertificateOfMembership::fromString(const char *s)
{
_qualifierCount = 0;
_signedBy.zero();
- memset(_signature.data,0,_signature.size());
+ memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
if (!*s)
return;
@@ -131,7 +145,7 @@ void CertificateOfMembership::fromString(const char *s)
colonAt = 0;
while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
if (colonAt) {
- if (Utils::unhex(s,colonAt,_signature.data,(unsigned int)_signature.size()) != _signature.size())
+ if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN)
_signedBy.zero();
} else {
_signedBy.zero();
@@ -152,6 +166,9 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
unsigned int myidx = 0;
unsigned int otheridx = 0;
+ if ((_qualifierCount == 0)||(other._qualifierCount == 0))
+ return false;
+
while (myidx < _qualifierCount) {
// Fail if we're at the end of other, since this means the field is
// missing.
@@ -182,7 +199,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
bool CertificateOfMembership::sign(const Identity &with)
{
- uint64_t *const buf = new uint64_t[_qualifierCount * 3];
+ uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
unsigned int ptr = 0;
for(unsigned int i=0;i<_qualifierCount;++i) {
buf[ptr++] = Utils::hton(_qualifiers[i].id);
@@ -193,38 +210,32 @@ bool CertificateOfMembership::sign(const Identity &with)
try {
_signature = with.sign(buf,ptr * sizeof(uint64_t));
_signedBy = with.address();
- delete [] buf;
return true;
} catch ( ... ) {
_signedBy.zero();
- delete [] buf;
return false;
}
}
-bool CertificateOfMembership::verify(const Identity &id) const
+int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) const
{
- if (!_signedBy)
- return false;
- if (id.address() != _signedBy)
- return false;
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
+ return -1;
- uint64_t *const buf = new uint64_t[_qualifierCount * 3];
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+
+ uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
unsigned int ptr = 0;
for(unsigned int i=0;i<_qualifierCount;++i) {
buf[ptr++] = Utils::hton(_qualifiers[i].id);
buf[ptr++] = Utils::hton(_qualifiers[i].value);
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
}
-
- bool valid = false;
- try {
- valid = id.verify(buf,ptr * sizeof(uint64_t),_signature);
- delete [] buf;
- } catch ( ... ) {
- delete [] buf;
- }
- return valid;
+ return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
}
} // namespace ZeroTier
diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp
index 0342bc33..b5a90007 100644
--- a/node/CertificateOfMembership.hpp
+++ b/node/CertificateOfMembership.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
@@ -27,6 +35,7 @@
#include <algorithm>
#include "Constants.hpp"
+#include "Credential.hpp"
#include "Buffer.hpp"
#include "Address.hpp"
#include "C25519.hpp"
@@ -34,22 +43,14 @@
#include "Utils.hpp"
/**
- * Default window of time for certificate agreement
- *
- * Right now we use time for 'revision' so this is the maximum time divergence
- * between two certs for them to agree. It comes out to five minutes, which
- * gives a lot of margin for error if the controller hiccups or its clock
- * drifts but causes de-authorized peers to fall off fast enough.
- */
-#define ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA (ZT_NETWORK_AUTOCONF_DELAY * 5)
-
-/**
- * Maximum number of qualifiers in a COM
+ * Maximum number of qualifiers allowed in a COM (absolute max: 65535)
*/
-#define ZT_NETWORK_COM_MAX_QUALIFIERS 16
+#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
namespace ZeroTier {
+class RuntimeEnvironment;
+
/**
* Certificate of network membership
*
@@ -76,25 +77,16 @@ namespace ZeroTier {
* This is a memcpy()'able structure and is safe (in a crash sense) to modify
* without locks.
*/
-class CertificateOfMembership
+class CertificateOfMembership : public Credential
{
public:
- /**
- * Certificate type codes, used in serialization
- *
- * Only one so far, and only one hopefully there shall be for quite some
- * time.
- */
- enum Type
- {
- COM_UINT64_ED25519 = 1 // tuples of unsigned 64's signed with Ed25519
- };
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
/**
* Reserved qualifier IDs
*
- * IDs below 65536 should be considered reserved for future global
- * assignment here.
+ * IDs below 1024 are reserved for use as standard IDs. Others are available
+ * for user-defined use.
*
* Addition of new required fields requires that code in hasRequiredFields
* be updated as well.
@@ -102,56 +94,47 @@ public:
enum ReservedId
{
/**
- * Revision number of certificate
- *
- * Certificates may differ in revision number by a designated max
- * delta. Differences wider than this cause certificates not to agree.
+ * Timestamp of certificate
*/
- COM_RESERVED_ID_REVISION = 0,
+ COM_RESERVED_ID_TIMESTAMP = 0,
/**
* Network ID for which certificate was issued
- *
- * maxDelta here is zero, since this must match.
*/
COM_RESERVED_ID_NETWORK_ID = 1,
/**
* ZeroTier address to whom certificate was issued
- *
- * maxDelta will be 0xffffffffffffffff here since it's permitted to differ
- * from peers obviously.
*/
COM_RESERVED_ID_ISSUED_TO = 2
};
/**
- * Create an empty certificate
+ * Create an empty certificate of membership
*/
- CertificateOfMembership() :
- _qualifierCount(0)
+ CertificateOfMembership()
{
- memset(_signature.data,0,_signature.size());
+ memset(this,0,sizeof(CertificateOfMembership));
}
CertificateOfMembership(const CertificateOfMembership &c)
{
- memcpy(this,&c,sizeof(CertificateOfMembership));
+ ZT_FAST_MEMCPY(this,&c,sizeof(CertificateOfMembership));
}
/**
* Create from required fields common to all networks
*
- * @param revision Revision number of certificate
+ * @param timestamp Timestamp of certificate
* @param timestampMaxDelta Maximum variation between timestamps on this net
* @param nwid Network ID
* @param issuedTo Certificate recipient
*/
- CertificateOfMembership(uint64_t revision,uint64_t revisionMaxDelta,uint64_t nwid,const Address &issuedTo)
+ CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
{
- _qualifiers[0].id = COM_RESERVED_ID_REVISION;
- _qualifiers[0].value = revision;
- _qualifiers[0].maxDelta = revisionMaxDelta;
+ _qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
+ _qualifiers[0].value = timestamp;
+ _qualifiers[0].maxDelta = timestampMaxDelta;
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
_qualifiers[1].value = nwid;
_qualifiers[1].maxDelta = 0;
@@ -159,31 +142,15 @@ public:
_qualifiers[2].value = issuedTo.toInt();
_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
_qualifierCount = 3;
- memset(_signature.data,0,_signature.size());
+ memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
}
inline CertificateOfMembership &operator=(const CertificateOfMembership &c)
{
- memcpy(this,&c,sizeof(CertificateOfMembership));
+ ZT_FAST_MEMCPY(this,&c,sizeof(CertificateOfMembership));
return *this;
}
-#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- /**
- * Create from string-serialized data
- *
- * @param s String-serialized COM
- */
- CertificateOfMembership(const char *s) { fromString(s); }
-
- /**
- * Create from string-serialized data
- *
- * @param s String-serialized COM
- */
- CertificateOfMembership(const std::string &s) { fromString(s.c_str()); }
-#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
-
/**
* Create from binary-serialized COM in buffer
*
@@ -199,48 +166,23 @@ public:
/**
* @return True if there's something here
*/
- inline operator bool() const throw() { return (_qualifierCount != 0); }
+ inline operator bool() const { return (_qualifierCount != 0); }
/**
- * Check for presence of all required fields common to all networks
- *
- * @return True if all required fields are present
+ * @return Credential ID, always 0 for COMs
*/
- inline bool hasRequiredFields() const
- {
- if (_qualifierCount < 3)
- return false;
- if (_qualifiers[0].id != COM_RESERVED_ID_REVISION)
- return false;
- if (_qualifiers[1].id != COM_RESERVED_ID_NETWORK_ID)
- return false;
- if (_qualifiers[2].id != COM_RESERVED_ID_ISSUED_TO)
- return false;
- return true;
- }
+ inline uint32_t id() const { return 0; }
/**
- * @return Maximum delta for mandatory revision field or 0 if field missing
+ * @return Timestamp for this cert and maximum delta for timestamp
*/
- inline uint64_t revisionMaxDelta() const
+ inline int64_t timestamp() const
{
for(unsigned int i=0;i<_qualifierCount;++i) {
- if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
- return _qualifiers[i].maxDelta;
- }
- return 0ULL;
- }
-
- /**
- * @return Revision number for this cert
- */
- inline uint64_t revision() const
- {
- for(unsigned int i=0;i<_qualifierCount;++i) {
- if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
+ if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
return _qualifiers[i].value;
}
- return 0ULL;
+ return 0;
}
/**
@@ -321,27 +263,28 @@ public:
bool sign(const Identity &with);
/**
- * Verify certificate against an identity
+ * Verify this COM and its signature
*
- * @param id Identity to verify against
- * @return True if certificate is signed by this identity and verification was successful
+ * @param RR Runtime environment for looking up peers
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
*/
- bool verify(const Identity &id) const;
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
/**
* @return True if signed
*/
- inline bool isSigned() const throw() { return (_signedBy); }
+ inline bool isSigned() const { return (_signedBy); }
/**
* @return Address that signed this certificate or null address if none
*/
- inline const Address &signedBy() const throw() { return _signedBy; }
+ inline const Address &signedBy() const { return _signedBy; }
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
{
- b.append((unsigned char)COM_UINT64_ED25519);
+ b.append((uint8_t)1);
b.append((uint16_t)_qualifierCount);
for(unsigned int i=0;i<_qualifierCount;++i) {
b.append(_qualifiers[i].id);
@@ -350,7 +293,7 @@ public:
}
_signedBy.appendTo(b);
if (_signedBy)
- b.append(_signature.data,(unsigned int)_signature.size());
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
}
template<unsigned int C>
@@ -361,15 +304,15 @@ public:
_qualifierCount = 0;
_signedBy.zero();
- if (b[p++] != COM_UINT64_ED25519)
- throw std::invalid_argument("invalid type");
+ if (b[p++] != 1)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
unsigned int numq = b.template at<uint16_t>(p); p += sizeof(uint16_t);
uint64_t lastId = 0;
for(unsigned int i=0;i<numq;++i) {
const uint64_t qid = b.template at<uint64_t>(p);
if (qid < lastId)
- throw std::invalid_argument("qualifiers not sorted");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
else lastId = qid;
if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
_qualifiers[_qualifierCount].id = qid;
@@ -378,7 +321,7 @@ public:
p += 24;
++_qualifierCount;
} else {
- throw std::invalid_argument("too many qualifiers");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
}
}
@@ -386,15 +329,14 @@ public:
p += ZT_ADDRESS_LENGTH;
if (_signedBy) {
- memcpy(_signature.data,b.field(p,(unsigned int)_signature.size()),_signature.size());
- p += (unsigned int)_signature.size();
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
+ p += ZT_C25519_SIGNATURE_LEN;
}
return (p - startAt);
}
inline bool operator==(const CertificateOfMembership &c) const
- throw()
{
if (_signedBy != c._signedBy)
return false;
@@ -406,9 +348,9 @@ public:
if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
return false;
}
- return (_signature == c._signature);
+ return (memcmp(_signature.data,c._signature.data,ZT_C25519_SIGNATURE_LEN) == 0);
}
- inline bool operator!=(const CertificateOfMembership &c) const throw() { return (!(*this == c)); }
+ inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
private:
struct _Qualifier
@@ -417,7 +359,7 @@ private:
uint64_t id;
uint64_t value;
uint64_t maxDelta;
- inline bool operator<(const _Qualifier &q) const throw() { return (id < q.id); } // sort order
+ inline bool operator<(const _Qualifier &q) const { return (id < q.id); } // sort order
};
Address _signedBy;
diff --git a/node/CertificateOfOwnership.cpp b/node/CertificateOfOwnership.cpp
new file mode 100644
index 00000000..8ee67865
--- /dev/null
+++ b/node/CertificateOfOwnership.cpp
@@ -0,0 +1,72 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "CertificateOfOwnership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+bool CertificateOfOwnership::_owns(const CertificateOfOwnership::Thing &t,const void *v,unsigned int l) const
+{
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ if (_thingTypes[i] == (uint8_t)t) {
+ unsigned int k = 0;
+ while (k < l) {
+ if (reinterpret_cast<const uint8_t *>(v)[k] != _thingValues[i][k])
+ break;
+ ++k;
+ }
+ if (k == l)
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace ZeroTier
diff --git a/node/CertificateOfOwnership.hpp b/node/CertificateOfOwnership.hpp
new file mode 100644
index 00000000..278ae863
--- /dev/null
+++ b/node/CertificateOfOwnership.hpp
@@ -0,0 +1,247 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
+#define ZT_CERTIFICATEOFOWNERSHIP_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "Credential.hpp"
+#include "C25519.hpp"
+#include "Address.hpp"
+#include "Identity.hpp"
+#include "Buffer.hpp"
+#include "InetAddress.hpp"
+#include "MAC.hpp"
+
+// Max things per CertificateOfOwnership
+#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS 16
+
+// Maximum size of a thing's value field in bytes
+#define ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE 16
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Certificate indicating ownership of a network identifier
+ */
+class CertificateOfOwnership : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
+
+ enum Thing
+ {
+ THING_NULL = 0,
+ THING_MAC_ADDRESS = 1,
+ THING_IPV4_ADDRESS = 2,
+ THING_IPV6_ADDRESS = 3
+ };
+
+ CertificateOfOwnership()
+ {
+ memset(this,0,sizeof(CertificateOfOwnership));
+ }
+
+ CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id) :
+ _networkId(nwid),
+ _ts(ts),
+ _flags(0),
+ _id(id),
+ _thingCount(0),
+ _issuedTo(issuedTo)
+ {
+ memset(_thingTypes,0,sizeof(_thingTypes));
+ memset(_thingValues,0,sizeof(_thingValues));
+ }
+
+ inline uint64_t networkId() const { return _networkId; }
+ inline int64_t timestamp() const { return _ts; }
+ inline uint32_t id() const { return _id; }
+ inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
+
+ inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
+ inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
+
+ inline const Address &issuedTo() const { return _issuedTo; }
+
+ inline bool owns(const InetAddress &ip) const
+ {
+ if (ip.ss_family == AF_INET)
+ return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ if (ip.ss_family == AF_INET6)
+ return this->_owns(THING_IPV6_ADDRESS,reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ return false;
+ }
+
+ inline bool owns(const MAC &mac) const
+ {
+ uint8_t tmp[6];
+ mac.copyTo(tmp,6);
+ return this->_owns(THING_MAC_ADDRESS,tmp,6);
+ }
+
+ inline void addThing(const InetAddress &ip)
+ {
+ if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+ if (ip.ss_family == AF_INET) {
+ _thingTypes[_thingCount] = THING_IPV4_ADDRESS;
+ ZT_FAST_MEMCPY(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+ ++_thingCount;
+ } else if (ip.ss_family == AF_INET6) {
+ _thingTypes[_thingCount] = THING_IPV6_ADDRESS;
+ ZT_FAST_MEMCPY(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+ ++_thingCount;
+ }
+ }
+
+ inline void addThing(const MAC &mac)
+ {
+ if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+ _thingTypes[_thingCount] = THING_MAC_ADDRESS;
+ mac.copyTo(_thingValues[_thingCount],6);
+ ++_thingCount;
+ }
+
+ /**
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param RR Runtime environment to allow identity lookup for signedBy
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append(_networkId);
+ b.append(_ts);
+ b.append(_flags);
+ b.append(_id);
+ b.append((uint16_t)_thingCount);
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ b.append((uint8_t)_thingTypes[i]);
+ b.append(_thingValues[i],ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ }
+
+ _issuedTo.appendTo(b);
+ _signedBy.appendTo(b);
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ b.append((uint16_t)0); // length of additional fields, currently 0
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ unsigned int p = startAt;
+
+ memset(this,0,sizeof(CertificateOfOwnership));
+
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _flags = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+ _thingCount = b.template at<uint16_t>(p); p += 2;
+ for(unsigned int i=0,j=_thingCount;i<j;++i) {
+ if (i < ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) {
+ _thingTypes[i] = (uint8_t)b[p++];
+ ZT_FAST_MEMCPY(_thingValues[i],b.field(p,ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE),ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE);
+ p += ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE;
+ }
+ }
+
+ _issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ p += 2;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
+
+ inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
+ inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
+
+private:
+ bool _owns(const Thing &t,const void *v,unsigned int l) const;
+
+ uint64_t _networkId;
+ int64_t _ts;
+ uint64_t _flags;
+ uint32_t _id;
+ uint16_t _thingCount;
+ uint8_t _thingTypes[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS];
+ uint8_t _thingValues[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS][ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE];
+ Address _issuedTo;
+ Address _signedBy;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Constants.hpp b/node/Constants.hpp
index b16cb9b5..e2a35dce 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_CONSTANTS_HPP
@@ -52,6 +60,8 @@
#endif
#ifdef __APPLE__
+#define likely(x) __builtin_expect((x),1)
+#define unlikely(x) __builtin_expect((x),0)
#include <TargetConditionals.h>
#ifndef __UNIX_LIKE__
#define __UNIX_LIKE__
@@ -128,6 +138,28 @@
#define RTF_MULTICAST 0x20000000
#endif
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+#ifndef likely
+#define likely(x) __builtin_expect((x),1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect((x),0)
+#endif
+#else
+#ifndef likely
+#define likely(x) (x)
+#endif
+#ifndef unlikely
+#define unlikely(x) (x)
+#endif
+#endif
+
+#ifdef __WINDOWS__
+#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
+#else
+#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
+#endif
+
/**
* Length of a ZeroTier address in bytes
*/
@@ -144,26 +176,14 @@
#define ZT_ADDRESS_RESERVED_PREFIX 0xff
/**
- * Default payload MTU for UDP packets
- *
- * In the future we might support UDP path MTU discovery, but for now we
- * set a maximum that is equal to 1500 minus 8 (for PPPoE overhead, common
- * in some markets) minus 48 (IPv6 UDP overhead).
- */
-#define ZT_UDP_DEFAULT_PAYLOAD_MTU 1444
-
-/**
* Default MTU used for Ethernet tap device
*/
-#define ZT_IF_MTU ZT_MAX_MTU
+#define ZT_DEFAULT_MTU 2800
/**
- * Maximum number of packet fragments we'll support
- *
- * The actual spec allows 16, but this is the most we'll support right
- * now. Packets with more than this many fragments are dropped.
+ * Maximum number of packet fragments we'll support (protocol max: 16)
*/
-#define ZT_MAX_PACKET_FRAGMENTS 4
+#define ZT_MAX_PACKET_FRAGMENTS 7
/**
* Size of RX queue
@@ -174,49 +194,39 @@
#define ZT_RX_QUEUE_SIZE 64
/**
- * RX queue entries older than this do not "exist"
- */
-#define ZT_RX_QUEUE_EXPIRE 4000
-
-/**
* Length of secret key in bytes -- 256-bit -- do not change
*/
#define ZT_PEER_SECRET_KEY_LENGTH 32
/**
- * How often Topology::clean() and Network::clean() and similar are called, in ms
- */
-#define ZT_HOUSEKEEPING_PERIOD 120000
-
-/**
- * Overriding granularity for timer tasks to prevent CPU-intensive thrashing on every packet
+ * Minimum delay between timer task checks to prevent thrashing
*/
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
/**
- * How long to remember peer records in RAM if they haven't been used
+ * How often Topology::clean() and Network::clean() and similar are called, in ms
*/
-#define ZT_PEER_IN_MEMORY_EXPIRATION 600000
+#define ZT_HOUSEKEEPING_PERIOD 60000
/**
* Delay between WHOIS retries in ms
*/
-#define ZT_WHOIS_RETRY_DELAY 1000
+#define ZT_WHOIS_RETRY_DELAY 500
/**
- * Maximum identity WHOIS retries (each attempt tries consulting a different peer)
+ * Transmit queue entry timeout
*/
-#define ZT_MAX_WHOIS_RETRIES 3
+#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000
/**
- * Transmit queue entry timeout
+ * Receive queue entry timeout
*/
-#define ZT_TRANSMIT_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+#define ZT_RECEIVE_QUEUE_TIMEOUT 5000
/**
- * Receive queue entry timeout
+ * Maximum latency to allow for OK(HELLO) before packet is discarded
*/
-#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+#define ZT_HELLO_MAX_ALLOWABLE_LATENCY 120000
/**
* Maximum number of ZT hops allowed (this is not IP hops/TTL)
@@ -231,11 +241,21 @@
#define ZT_MULTICAST_LIKE_EXPIRE 600000
/**
+ * Period for multicast LIKE announcements
+ */
+#define ZT_MULTICAST_ANNOUNCE_PERIOD 120000
+
+/**
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
*/
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
/**
+ * Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
+ */
+#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
+
+/**
* Timeout for outgoing multicasts
*
* This is how long we wait for explicit or implicit gather results.
@@ -243,50 +263,49 @@
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
/**
- * Default maximum number of peers to address with a single multicast (if unspecified in network config)
+ * Delay between checks of peer pings, etc., and also related housekeeping tasks
*/
-#define ZT_MULTICAST_DEFAULT_LIMIT 32
+#define ZT_PING_CHECK_INVERVAL 5000
/**
- * How frequently to send a zero-byte UDP keepalive packet
- *
- * There are NATs with timeouts as short as 20 seconds, so this turns out
- * to be needed.
+ * How frequently to send heartbeats over in-use paths
*/
-#define ZT_NAT_KEEPALIVE_DELAY 19000
+#define ZT_PATH_HEARTBEAT_PERIOD 14000
/**
- * Delay between scans of the topology active peer DB for peers that need ping
- *
- * This is also how often pings will be retried to upstream peers (relays, roots)
- * constantly until something is heard.
+ * Do not accept HELLOs over a given path more often than this
*/
-#define ZT_PING_CHECK_INVERVAL 9500
+#define ZT_PATH_HELLO_RATE_LIMIT 1000
/**
- * Delay between ordinary case pings of direct links
+ * Delay between full-fledge pings of directly connected peers
*/
-#define ZT_PEER_DIRECT_PING_DELAY 60000
+#define ZT_PEER_PING_PERIOD 60000
/**
- * Timeout for overall peer activity (measured from last receive)
+ * Paths are considered expired if they have not sent us a real packet in this long
*/
-#define ZT_PEER_ACTIVITY_TIMEOUT 500000
+#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
/**
- * Timeout for path activity
+ * How often to retry expired paths that we're still remembering
+ */
+#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
+
+/**
+ * Timeout for overall peer activity (measured from last receive)
*/
-#define ZT_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
+#define ZT_PEER_ACTIVITY_TIMEOUT 500000
/**
- * No answer timeout to trigger dead path detection
+ * General rate limit timeout for multiple packet types (HELLO, etc.)
*/
-#define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2000
+#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
/**
- * Probation threshold after which a path becomes dead
+ * General limit for max RTT for requests over the network
*/
-#define ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION 3
+#define ZT_GENERAL_RTT_LIMIT 5000
/**
* Delay between requests for updated network autoconf information
@@ -306,19 +325,9 @@
#define ZT_MIN_UNITE_INTERVAL 30000
/**
- * Delay between initial direct NAT-t packet and more aggressive techniques
- *
- * This may also be a delay before sending the first packet if we determine
- * that we should wait for the remote to initiate rendezvous first.
+ * How often should peers try memorized or statically defined paths?
*/
-#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000
-
-/**
- * How long (max) to remember network certificates of membership?
- *
- * This only applies to networks we don't belong to.
- */
-#define ZT_PEER_NETWORK_COM_EXPIRATION 3600000
+#define ZT_TRY_MEMORIZED_PATH_INTERVAL 30000
/**
* Sanity limit on maximum bridge routes
@@ -334,7 +343,7 @@
/**
* If there is no known route, spam to up to this many active bridges
*/
-#define ZT_MAX_BRIDGE_SPAM 16
+#define ZT_MAX_BRIDGE_SPAM 32
/**
* Interval between direct path pushes in milliseconds
@@ -344,7 +353,7 @@
/**
* Time horizon for push direct paths cutoff
*/
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 30000
/**
* Maximum number of direct path pushes within cutoff time
@@ -353,30 +362,62 @@
* per CUTOFF_TIME milliseconds per peer to prevent this from being
* useful for DOS amplification attacks.
*/
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5
+#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 8
/**
* Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6)
*/
-#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
+#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8
/**
- * Enable support for old Dictionary based network configs
+ * Time horizon for VERB_NETWORK_CREDENTIALS cutoff
*/
-#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
+#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
/**
- * A test pseudo-network-ID that can be joined
- *
- * Joining this network ID will result in a network with no IP addressing
- * and default parameters. No network configuration master will be consulted
- * and instead a static config will be used. This is used in built-in testnet
- * scenarios and can also be used for external testing.
+ * Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
+ */
+#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15
+
+/**
+ * WHOIS rate limit (we allow these to be pretty fast)
+ */
+#define ZT_PEER_WHOIS_RATE_LIMIT 100
+
+/**
+ * General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
+ */
+#define ZT_PEER_GENERAL_RATE_LIMIT 1000
+
+/**
+ * Don't do expensive identity validation more often than this
*
- * This is an impossible real network ID since 0xff is a reserved address
- * prefix.
+ * IPv4 and IPv6 address prefixes are hashed down to 14-bit (0-16383) integers
+ * using the first 24 bits for IPv4 or the first 48 bits for IPv6. These are
+ * then rate limited to one identity validation per this often milliseconds.
+ */
+#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64) || defined(_M_AMD64))
+// AMD64 machines can do anywhere from one every 50ms to one every 10ms. This provides plenty of margin.
+#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 2000
+#else
+#if (defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__))
+// 32-bit Intel machines usually average about one every 100ms
+#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 5000
+#else
+// This provides a safe margin for ARM, MIPS, etc. that usually average one every 250-400ms
+#define ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT 10000
+#endif
+#endif
+
+/**
+ * How long is a path or peer considered to have a trust relationship with us (for e.g. relay policy) since last trusted established packet?
*/
-#define ZT_TEST_NETWORK_ID 0xffffffffffffffffULL
+#define ZT_TRUST_EXPIRATION 600000
+
+/**
+ * Enable support for older network configurations from older (pre-1.1.6) controllers
+ */
+#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
/**
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
@@ -387,6 +428,11 @@
#define ZT_UDP_DESIRED_BUF_SIZE 131072
#endif
+/**
+ * Desired / recommended min stack size for threads (used on some platforms to reset thread stack size)
+ */
+#define ZT_THREAD_MIN_STACK_SIZE 1048576
+
/* Ethernet frame types that might be relevant to us */
#define ZT_ETHERTYPE_IPV4 0x0800
#define ZT_ETHERTYPE_ARP 0x0806
@@ -397,4 +443,13 @@
#define ZT_ETHERTYPE_IPX_B 0x8138
#define ZT_ETHERTYPE_IPV6 0x86dd
+#define ZT_EXCEPTION_OUT_OF_BOUNDS 100
+#define ZT_EXCEPTION_OUT_OF_MEMORY 101
+#define ZT_EXCEPTION_PRIVATE_KEY_REQUIRED 102
+#define ZT_EXCEPTION_INVALID_ARGUMENT 103
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE 200
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW 201
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202
+#define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203
+
#endif
diff --git a/node/Credential.hpp b/node/Credential.hpp
new file mode 100644
index 00000000..34e94162
--- /dev/null
+++ b/node/Credential.hpp
@@ -0,0 +1,65 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_CREDENTIAL_HPP
+#define ZT_CREDENTIAL_HPP
+
+#include <string>
+#include <memory>
+#include <stdexcept>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Base class for credentials
+ */
+class Credential
+{
+public:
+ /**
+ * Do not change type code IDs -- these are used in Revocation objects and elsewhere
+ */
+ enum Type
+ {
+ CREDENTIAL_TYPE_NULL = 0,
+ CREDENTIAL_TYPE_COM = 1, // CertificateOfMembership
+ CREDENTIAL_TYPE_CAPABILITY = 2,
+ CREDENTIAL_TYPE_TAG = 3,
+ CREDENTIAL_TYPE_COO = 4, // CertificateOfOwnership
+ CREDENTIAL_TYPE_REVOCATION = 6
+ };
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/DeferredPackets.cpp b/node/DeferredPackets.cpp
deleted file mode 100644
index 192b4078..00000000
--- a/node/DeferredPackets.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "Constants.hpp"
-#include "DeferredPackets.hpp"
-#include "IncomingPacket.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Node.hpp"
-
-namespace ZeroTier {
-
-DeferredPackets::DeferredPackets(const RuntimeEnvironment *renv) :
- RR(renv),
- _waiting(0),
- _die(false)
-{
-}
-
-DeferredPackets::~DeferredPackets()
-{
- _q_m.lock();
- _die = true;
- _q_m.unlock();
-
- for(;;) {
- _q_s.post();
-
- _q_m.lock();
- if (_waiting <= 0) {
- _q_m.unlock();
- break;
- } else {
- _q_m.unlock();
- }
- }
-}
-
-bool DeferredPackets::enqueue(IncomingPacket *pkt)
-{
- {
- Mutex::Lock _l(_q_m);
- if (_q.size() >= ZT_DEFFEREDPACKETS_MAX)
- return false;
- _q.push_back(*pkt);
- }
- _q_s.post();
- return true;
-}
-
-int DeferredPackets::process()
-{
- std::list<IncomingPacket> pkt;
-
- _q_m.lock();
-
- if (_die) {
- _q_m.unlock();
- return -1;
- }
-
- while (_q.empty()) {
- ++_waiting;
- _q_m.unlock();
- _q_s.wait();
- _q_m.lock();
- --_waiting;
- if (_die) {
- _q_m.unlock();
- return -1;
- }
- }
-
- // Move item from _q list to a dummy list here to avoid copying packet
- pkt.splice(pkt.end(),_q,_q.begin());
-
- _q_m.unlock();
-
- try {
- pkt.front().tryDecode(RR,true);
- } catch ( ... ) {} // drop invalids
-
- return 1;
-}
-
-} // namespace ZeroTier
diff --git a/node/DeferredPackets.hpp b/node/DeferredPackets.hpp
deleted file mode 100644
index a9855396..00000000
--- a/node/DeferredPackets.hpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_DEFERREDPACKETS_HPP
-#define ZT_DEFERREDPACKETS_HPP
-
-#include <list>
-
-#include "Constants.hpp"
-#include "SharedPtr.hpp"
-#include "Mutex.hpp"
-#include "DeferredPackets.hpp"
-#include "BinarySemaphore.hpp"
-
-/**
- * Maximum number of deferred packets
- */
-#define ZT_DEFFEREDPACKETS_MAX 256
-
-namespace ZeroTier {
-
-class IncomingPacket;
-class RuntimeEnvironment;
-
-/**
- * Deferred packets
- *
- * IncomingPacket can defer its decoding this way by enqueueing itself here.
- * When this is done, deferredDecode() is called later. This is done for
- * operations that may be expensive to allow them to potentially be handled
- * in the background or rate limited to maintain quality of service for more
- * routine operations.
- */
-class DeferredPackets
-{
-public:
- DeferredPackets(const RuntimeEnvironment *renv);
- ~DeferredPackets();
-
- /**
- * Enqueue a packet
- *
- * @param pkt Packet to process later (possibly in the background)
- * @return False if queue is full
- */
- bool enqueue(IncomingPacket *pkt);
-
- /**
- * Wait for and then process a deferred packet
- *
- * If we are shutting down (in destructor), this returns -1 and should
- * not be called again. Otherwise it returns the number of packets
- * processed.
- *
- * @return Number processed or -1 if shutting down
- */
- int process();
-
-private:
- std::list<IncomingPacket> _q;
- const RuntimeEnvironment *const RR;
- volatile int _waiting;
- volatile bool _die;
- Mutex _q_m;
- BinarySemaphore _q_s;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp
index 59fc4bbf..f89b6ffc 100644
--- a/node/Dictionary.hpp
+++ b/node/Dictionary.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_DICTIONARY_HPP
@@ -54,35 +62,29 @@ template<unsigned int C>
class Dictionary
{
public:
- Dictionary()
- {
- _d[0] = (char)0;
- }
-
- Dictionary(const char *s)
- {
- Utils::scopy(_d,sizeof(_d),s);
- }
-
+ Dictionary() { memset(_d,0,sizeof(_d)); }
+ Dictionary(const char *s) { this->load(s); }
Dictionary(const char *s,unsigned int len)
{
- if (len > (C-1))
- len = C-1;
- memcpy(_d,s,len);
- _d[len] = (char)0;
- }
-
- Dictionary(const Dictionary &d)
- {
- Utils::scopy(_d,sizeof(_d),d._d);
+ for(unsigned int i=0;i<C;++i) {
+ if ((s)&&(i < len)) {
+ if (!(_d[i] = *s))
+ s = (const char *)0;
+ else ++s;
+ } else _d[i] = (char)0;
+ }
+ _d[C - 1] = (char)0;
}
+ Dictionary(const Dictionary &d) { memcpy(_d,d._d,C); }
inline Dictionary &operator=(const Dictionary &d)
{
- Utils::scopy(_d,sizeof(_d),d._d);
+ memcpy(_d,d._d,C);
return *this;
}
+ inline operator bool() const { return (_d[0] != 0); }
+
/**
* Load a dictionary from a C-string
*
@@ -91,7 +93,15 @@ public:
*/
inline bool load(const char *s)
{
- return Utils::scopy(_d,sizeof(_d),s);
+ for(unsigned int i=0;i<C;++i) {
+ if (s) {
+ if (!(_d[i] = *s))
+ s = (const char *)0;
+ else ++s;
+ } else _d[i] = (char)0;
+ }
+ _d[C - 1] = (char)0;
+ return (!s);
}
/**
@@ -99,7 +109,7 @@ public:
*/
inline void clear()
{
- _d[0] = (char)0;
+ memset(_d,0,sizeof(_d));
}
/**
@@ -163,12 +173,12 @@ public:
j = 0;
esc = false;
++p;
- while ((*p != 0)&&(*p != '\r')&&(*p != '\n')) {
+ while ((*p != 0)&&(*p != 13)&&(*p != 10)) {
if (esc) {
esc = false;
switch(*p) {
- case 'r': dest[j++] = '\r'; break;
- case 'n': dest[j++] = '\n'; break;
+ case 'r': dest[j++] = 13; break;
+ case 'n': dest[j++] = 10; break;
case '0': dest[j++] = (char)0; break;
case 'e': dest[j++] = '='; break;
default: dest[j++] = *p; break;
@@ -194,7 +204,7 @@ public:
dest[j] = (char)0;
return j;
} else {
- while ((*p)&&(*p != '\r')&&(*p != '\n')) {
+ while ((*p)&&(*p != 13)&&(*p != 10)) {
if (++p == eof) {
dest[0] = (char)0;
return -1;
@@ -266,6 +276,21 @@ public:
}
/**
+ * Get an unsigned int64 stored as hex in the dictionary
+ *
+ * @param key Key to look up
+ * @param dfl Default value or 0 if unspecified
+ * @return Decoded hex UInt value or 'dfl' if not found
+ */
+ inline int64_t getI(const char *key,int64_t dfl = 0) const
+ {
+ char tmp[128];
+ if (this->get(key,tmp,sizeof(tmp)) >= 1)
+ return Utils::hexStrTo64(tmp);
+ return dfl;
+ }
+
+ /**
* Add a new key=value pair
*
* If the key is already present this will append another, but the first
@@ -286,7 +311,7 @@ public:
unsigned int j = i;
if (j > 0) {
- _d[j++] = '\n';
+ _d[j++] = (char)10;
if (j == C) {
_d[i] = (char)0;
return false;
@@ -313,8 +338,8 @@ public:
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
switch(*p) {
case 0:
- case '\r':
- case '\n':
+ case 13:
+ case 10:
case '\\':
case '=':
_d[j++] = '\\';
@@ -324,8 +349,8 @@ public:
}
switch(*p) {
case 0: _d[j++] = '0'; break;
- case '\r': _d[j++] = 'r'; break;
- case '\n': _d[j++] = 'n'; break;
+ case 13: _d[j++] = 'r'; break;
+ case 10: _d[j++] = 'n'; break;
case '\\': _d[j++] = '\\'; break;
case '=': _d[j++] = 'e'; break;
}
@@ -368,8 +393,21 @@ public:
inline bool add(const char *key,uint64_t value)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
- return this->add(key,tmp,-1);
+ return this->add(key,Utils::hex(value,tmp),-1);
+ }
+
+ /**
+ * Add a 64-bit integer (unsigned) as a hex value
+ */
+ inline bool add(const char *key,int64_t value)
+ {
+ char tmp[32];
+ if (value >= 0) {
+ return this->add(key,Utils::hex((uint64_t)value,tmp),-1);
+ } else {
+ tmp[0] = '-';
+ return this->add(key,Utils::hex((uint64_t)(value * -1),tmp+1),-1);
+ }
}
/**
@@ -378,8 +416,7 @@ public:
inline bool add(const char *key,const Address &a)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
- return this->add(key,tmp,-1);
+ return this->add(key,Utils::hex(a.toInt(),tmp),-1);
}
/**
@@ -404,55 +441,13 @@ public:
}
/**
- * Erase a key from this dictionary
- *
- * Use this before add() to ensure that a key is replaced if it might
- * already be present.
- *
- * @param key Key to erase
- * @return True if key was found and erased
- */
- inline bool erase(const char *key)
- {
- char d2[C];
- char *saveptr = (char *)0;
- unsigned int d2ptr = 0;
- bool found = false;
- for(char *f=Utils::stok(_d,"\r\n",&saveptr);(f);f=Utils::stok((char *)0,"\r\n",&saveptr)) {
- if (*f) {
- const char *p = f;
- const char *k = key;
- while ((*k)&&(*p)) {
- if (*k != *p)
- break;
- ++k;
- ++p;
- }
- if (*k) {
- p = f;
- while (*p)
- d2[d2ptr++] = *(p++);
- d2[d2ptr++] = '\n';
- } else {
- found = true;
- }
- }
- }
- d2[d2ptr++] = (char)0;
- memcpy(_d,d2,d2ptr);
- return found;
- }
-
- /**
- * @return Dictionary data as a 0-terminated C-string
- */
- inline const char *data() const { return _d; }
-
- /**
* @return Value of C template parameter
*/
inline unsigned int capacity() const { return C; }
+ inline const char *data() const { return _d; }
+ inline char *unsafeData() { return _d; }
+
private:
char _d[C];
};
diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp
index f06b2230..777e88dc 100644
--- a/node/Hashtable.hpp
+++ b/node/Hashtable.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,11 +14,21 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_HASHTABLE_HPP
#define ZT_HASHTABLE_HPP
+#include "Constants.hpp"
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -32,11 +42,6 @@ namespace ZeroTier {
/**
* A minimal hash table implementation for the ZeroTier core
- *
- * This is not a drop-in replacement for STL containers, and has several
- * limitations. Keys can be uint64_t or an object, and if the latter they
- * must implement a method called hashCode() that returns an unsigned long
- * value that is evenly distributed.
*/
template<typename K,typename V>
class Hashtable
@@ -100,18 +105,18 @@ public:
Hashtable *_ht;
_Bucket *_b;
};
- friend class Hashtable::Iterator;
+ //friend class Hashtable<K,V>::Iterator;
/**
- * @param bc Initial capacity in buckets (default: 128, must be nonzero)
+ * @param bc Initial capacity in buckets (default: 64, must be nonzero)
*/
- Hashtable(unsigned long bc = 128) :
+ Hashtable(unsigned long bc = 64) :
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
_bc(bc),
_s(0)
{
if (!_t)
- throw std::bad_alloc();
+ throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<bc;++i)
_t[i] = (_Bucket *)0;
}
@@ -122,7 +127,7 @@ public:
_s(ht._s)
{
if (!_t)
- throw std::bad_alloc();
+ throw ZT_EXCEPTION_OUT_OF_MEMORY;
for(unsigned long i=0;i<_bc;++i)
_t[i] = (_Bucket *)0;
for(unsigned long i=0;i<_bc;++i) {
@@ -251,6 +256,24 @@ public:
inline const V *get(const K &k) const { return const_cast<Hashtable *>(this)->get(k); }
/**
+ * @param k Key
+ * @param v Value to fill with result
+ * @return True if value was found and set (if false, v is not modified)
+ */
+ inline bool get(const K &k,V &v) const
+ {
+ _Bucket *b = _t[_hc(k) % _bc];
+ while (b) {
+ if (b->k == k) {
+ v = b->v;
+ return true;
+ }
+ b = b->next;
+ }
+ return false;
+ }
+
+ /**
* @param k Key to check
* @return True if key is present
*/
@@ -351,27 +374,22 @@ public:
/**
* @return Number of entries
*/
- inline unsigned long size() const throw() { return _s; }
+ inline unsigned long size() const { return _s; }
/**
* @return True if table is empty
*/
- inline bool empty() const throw() { return (_s == 0); }
+ inline bool empty() const { return (_s == 0); }
private:
template<typename O>
static inline unsigned long _hc(const O &obj)
{
- return obj.hashCode();
+ return (unsigned long)obj.hashCode();
}
static inline unsigned long _hc(const uint64_t i)
{
- /* NOTE: this assumes that 'i' is evenly distributed, which is the case for
- * packet IDs and network IDs -- the two use cases in ZT for uint64_t keys.
- * These values are also greater than 0xffffffff so they'll map onto a full
- * bucket count just fine no matter what happens. Normally you'd want to
- * hash an integer key index in a hash table. */
- return (unsigned long)i;
+ return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses
}
static inline unsigned long _hc(const uint32_t i)
{
diff --git a/node/Identity.cpp b/node/Identity.cpp
index 6f89a1ee..03f27083 100644
--- a/node/Identity.cpp
+++ b/node/Identity.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -45,8 +53,8 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
// ordinary Salsa20 is randomly seekable. This is good for a cipher
// but is not what we want for sequential memory-harndess.
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
- Salsa20 s20(digest,256,(char *)digest + 32);
- s20.encrypt20((char *)genmem,(char *)genmem,64);
+ Salsa20 s20(digest,(char *)digest + 32);
+ s20.crypt20((char *)genmem,(char *)genmem,64);
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
unsigned long k = i - 64;
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
@@ -57,7 +65,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
- s20.encrypt20((char *)genmem + i,(char *)genmem + i,64);
+ s20.crypt20((char *)genmem + i,(char *)genmem + i,64);
}
// Render final digest using genmem as a lookup table
@@ -67,7 +75,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
uint64_t tmp = ((uint64_t *)genmem)[idx2];
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
((uint64_t *)digest)[idx1] = tmp;
- s20.encrypt20(digest,digest,64);
+ s20.crypt20(digest,digest,64);
}
}
@@ -75,12 +83,11 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
// threshold value.
struct _Identity_generate_cond
{
- _Identity_generate_cond() throw() {}
- _Identity_generate_cond(unsigned char *sb,char *gm) throw() : digest(sb),genmem(gm) {}
+ _Identity_generate_cond() {}
+ _Identity_generate_cond(unsigned char *sb,char *gm) : digest(sb),genmem(gm) {}
inline bool operator()(const C25519::Pair &kp) const
- throw()
{
- _computeMemoryHardHash(kp.pub.data,(unsigned int)kp.pub.size(),digest,genmem);
+ _computeMemoryHardHash(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
return (digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN);
}
unsigned char *digest;
@@ -113,7 +120,7 @@ bool Identity::locallyValidate() const
unsigned char digest[64];
char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
- _computeMemoryHardHash(_publicKey.data,(unsigned int)_publicKey.size(),digest,genmem);
+ _computeMemoryHardHash(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
delete [] genmem;
unsigned char addrb[5];
@@ -128,61 +135,79 @@ bool Identity::locallyValidate() const
(digest[63] == addrb[4]));
}
-std::string Identity::toString(bool includePrivate) const
+char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
{
- std::string r;
-
- r.append(_address.toString());
- r.append(":0:"); // 0 == IDENTITY_TYPE_C25519
- r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
+ char *p = buf;
+ Utils::hex10(_address.toInt(),p);
+ p += 10;
+ *(p++) = ':';
+ *(p++) = '0';
+ *(p++) = ':';
+ Utils::hex(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,p);
+ p += ZT_C25519_PUBLIC_KEY_LEN * 2;
if ((_privateKey)&&(includePrivate)) {
- r.push_back(':');
- r.append(Utils::hex(_privateKey->data,(unsigned int)_privateKey->size()));
+ *(p++) = ':';
+ Utils::hex(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN,p);
+ p += ZT_C25519_PRIVATE_KEY_LEN * 2;
}
-
- return r;
+ *p = (char)0;
+ return buf;
}
bool Identity::fromString(const char *str)
{
- if (!str)
+ if (!str) {
+ _address.zero();
return false;
-
- char *saveptr = (char *)0;
- char tmp[1024];
- if (!Utils::scopy(tmp,sizeof(tmp),str))
+ }
+ char tmp[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+ if (!Utils::scopy(tmp,sizeof(tmp),str)) {
+ _address.zero();
return false;
+ }
delete _privateKey;
_privateKey = (C25519::Private *)0;
int fno = 0;
+ char *saveptr = (char *)0;
for(char *f=Utils::stok(tmp,":",&saveptr);(f);f=Utils::stok((char *)0,":",&saveptr)) {
switch(fno++) {
case 0:
- _address = Address(f);
- if (_address.isReserved())
+ _address = Address(Utils::hexStrToU64(f));
+ if (_address.isReserved()) {
+ _address.zero();
return false;
+ }
break;
case 1:
- if ((f[0] != '0')||(f[1]))
+ if ((f[0] != '0')||(f[1])) {
+ _address.zero();
return false;
+ }
break;
case 2:
- if (Utils::unhex(f,_publicKey.data,(unsigned int)_publicKey.size()) != _publicKey.size())
+ if (Utils::unhex(f,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
+ _address.zero();
return false;
+ }
break;
case 3:
_privateKey = new C25519::Private();
- if (Utils::unhex(f,_privateKey->data,(unsigned int)_privateKey->size()) != _privateKey->size())
+ if (Utils::unhex(f,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
+ _address.zero();
return false;
+ }
break;
default:
+ _address.zero();
return false;
}
}
- if (fno < 3)
+ if (fno < 3) {
+ _address.zero();
return false;
+ }
return true;
}
diff --git a/node/Identity.hpp b/node/Identity.hpp
index e19c4980..74716aa0 100644
--- a/node/Identity.hpp
+++ b/node/Identity.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_IDENTITY_HPP
@@ -21,16 +29,16 @@
#include <stdio.h>
#include <stdlib.h>
-#include <string>
#include "Constants.hpp"
-#include "Array.hpp"
#include "Utils.hpp"
#include "Address.hpp"
#include "C25519.hpp"
#include "Buffer.hpp"
#include "SHA512.hpp"
+#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
+
namespace ZeroTier {
/**
@@ -46,14 +54,6 @@ namespace ZeroTier {
class Identity
{
public:
- /**
- * Identity types
- */
- enum Type
- {
- IDENTITY_TYPE_C25519 = 0
- };
-
Identity() :
_privateKey((C25519::Private *)0)
{
@@ -66,20 +66,11 @@ public:
{
}
- Identity(const char *str)
- throw(std::invalid_argument) :
+ Identity(const char *str) :
_privateKey((C25519::Private *)0)
{
if (!fromString(str))
- throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
- }
-
- Identity(const std::string &str)
- throw(std::invalid_argument) :
- _privateKey((C25519::Private *)0)
- {
- if (!fromString(str))
- throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
template<unsigned int C>
@@ -91,7 +82,10 @@ public:
~Identity()
{
- delete _privateKey;
+ if (_privateKey) {
+ Utils::burn(_privateKey,sizeof(C25519::Private));
+ delete _privateKey;
+ }
}
inline Identity &operator=(const Identity &id)
@@ -126,7 +120,7 @@ public:
/**
* @return True if this identity contains a private key
*/
- inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); }
+ inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
/**
* Compute the SHA512 hash of our private key (if we have one)
@@ -150,11 +144,10 @@ public:
* @param len Length of data
*/
inline C25519::Signature sign(const void *data,unsigned int len) const
- throw(std::runtime_error)
{
if (_privateKey)
return C25519::sign(*_privateKey,_publicKey,data,len);
- throw std::runtime_error("sign() requires a private key");
+ throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
}
/**
@@ -206,14 +199,9 @@ public:
}
/**
- * @return Identity type
- */
- inline Type type() const throw() { return IDENTITY_TYPE_C25519; }
-
- /**
* @return This identity's address
*/
- inline const Address &address() const throw() { return _address; }
+ inline const Address &address() const { return _address; }
/**
* Serialize this identity (binary)
@@ -226,11 +214,11 @@ public:
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
{
_address.appendTo(b);
- b.append((unsigned char)IDENTITY_TYPE_C25519);
- b.append(_publicKey.data,(unsigned int)_publicKey.size());
+ b.append((uint8_t)0); // C25519/Ed25519 identity type
+ b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_privateKey)&&(includePrivate)) {
- b.append((unsigned char)_privateKey->size());
- b.append(_privateKey->data,(unsigned int)_privateKey->size());
+ b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
+ b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
} else b.append((unsigned char)0);
}
@@ -257,18 +245,18 @@ public:
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
p += ZT_ADDRESS_LENGTH;
- if (b[p++] != IDENTITY_TYPE_C25519)
- throw std::invalid_argument("unsupported identity type");
+ if (b[p++] != 0)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
- memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
- p += (unsigned int)_publicKey.size();
+ ZT_FAST_MEMCPY(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
+ p += ZT_C25519_PUBLIC_KEY_LEN;
unsigned int privateKeyLength = (unsigned int)b[p++];
if (privateKeyLength) {
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
- throw std::invalid_argument("invalid private key");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_privateKey = new C25519::Private();
- memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
+ ZT_FAST_MEMCPY(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
}
@@ -279,9 +267,10 @@ public:
* Serialize to a more human-friendly string
*
* @param includePrivate If true, include private key (if it exists)
+ * @param buf Buffer to store string
* @return ASCII string representation of identity
*/
- std::string toString(bool includePrivate) const;
+ char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
/**
* Deserialize a human-friendly string
@@ -293,19 +282,36 @@ public:
* @return True if deserialization appears successful
*/
bool fromString(const char *str);
- inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
+
+ /**
+ * @return C25519 public key
+ */
+ inline const C25519::Public &publicKey() const { return _publicKey; }
+
+ /**
+ * @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
+ */
+ inline const C25519::Pair privateKeyPair() const
+ {
+ C25519::Pair pair;
+ pair.pub = _publicKey;
+ if (_privateKey)
+ pair.priv = *_privateKey;
+ else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
+ return pair;
+ }
/**
* @return True if this identity contains something
*/
- inline operator bool() const throw() { return (_address); }
-
- inline bool operator==(const Identity &id) const throw() { return ((_address == id._address)&&(_publicKey == id._publicKey)); }
- inline bool operator<(const Identity &id) const throw() { return ((_address < id._address)||((_address == id._address)&&(_publicKey < id._publicKey))); }
- inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
- inline bool operator>(const Identity &id) const throw() { return (id < *this); }
- inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
- inline bool operator>=(const Identity &id) const throw() { return !(*this < id); }
+ inline operator bool() const { return (_address); }
+
+ inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
+ inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
+ inline bool operator!=(const Identity &id) const { return !(*this == id); }
+ inline bool operator>(const Identity &id) const { return (id < *this); }
+ inline bool operator<=(const Identity &id) const { return !(id < *this); }
+ inline bool operator>=(const Identity &id) const { return !(*this < id); }
private:
Address _address;
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 37af8425..ff4fc94b 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -34,13 +42,16 @@
#include "Salsa20.hpp"
#include "SHA512.hpp"
#include "World.hpp"
-#include "Cluster.hpp"
#include "Node.hpp"
-#include "DeferredPackets.hpp"
+#include "CertificateOfMembership.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
+#include "Revocation.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
-bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
+bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
{
const Address sourceAddress(source());
@@ -52,410 +63,435 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
// If this is marked as a packet via a trusted path, check source address and path ID.
// Obviously if no trusted paths are configured this always returns false and such
// packets are dropped on the floor.
- if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
+ const uint64_t tpid = trustedPathId();
+ if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
trusted = true;
- TRACE("TRUSTED PATH packet approved from %s(%s), trusted path ID %llx",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
} else {
- TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
return true;
}
} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
- // Unencrypted HELLOs require some potentially expensive verification, so
- // do this in the background if background processing is enabled.
- if ((RR->dpEnabled > 0)&&(!deferred)) {
- RR->dp->enqueue(this);
- return true; // 'handled' via deferring to background thread(s)
- } else {
- // A null pointer for peer to _doHELLO() tells it to run its own
- // special internal authentication logic. This is done for unencrypted
- // HELLOs to learn new identities, etc.
- SharedPtr<Peer> tmp;
- return _doHELLO(RR,tmp);
- }
+ // Only HELLO is allowed in the clear, but will still have a MAC
+ return _doHELLO(RR,tPtr,false);
}
- SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
+ const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,sourceAddress));
if (peer) {
if (!trusted) {
if (!dearmor(peer->key())) {
- TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),size());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
return true;
}
}
if (!uncompress()) {
- TRACE("dropped packet from %s(%s), compressed data invalid",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),Packet::VERB_NOP,"LZ4 decompression failed");
return true;
}
const Packet::Verb v = verb();
- //TRACE("<< %s from %s(%s)",Packet::verbString(v),sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
switch(v) {
//case Packet::VERB_NOP:
default: // ignore unknown verbs, but if they pass auth check they are "received"
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),v,0,Packet::VERB_NOP);
+ peer->received(tPtr,_path,hops(),packetId(),v,0,Packet::VERB_NOP,false,0);
return true;
-
- case Packet::VERB_HELLO: return _doHELLO(RR,peer);
- case Packet::VERB_ERROR: return _doERROR(RR,peer);
- case Packet::VERB_OK: return _doOK(RR,peer);
- case Packet::VERB_WHOIS: return _doWHOIS(RR,peer);
- case Packet::VERB_RENDEZVOUS: return _doRENDEZVOUS(RR,peer);
- case Packet::VERB_FRAME: return _doFRAME(RR,peer);
- case Packet::VERB_EXT_FRAME: return _doEXT_FRAME(RR,peer);
- case Packet::VERB_ECHO: return _doECHO(RR,peer);
- case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,peer);
- case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return _doNETWORK_MEMBERSHIP_CERTIFICATE(RR,peer);
- case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,peer);
- case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer);
- case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer);
- case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer);
- case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer);
- case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer);
- case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer);
- case Packet::VERB_REQUEST_PROOF_OF_WORK: return _doREQUEST_PROOF_OF_WORK(RR,peer);
+ case Packet::VERB_HELLO: return _doHELLO(RR,tPtr,true);
+ case Packet::VERB_ERROR: return _doERROR(RR,tPtr,peer);
+ case Packet::VERB_OK: return _doOK(RR,tPtr,peer);
+ case Packet::VERB_WHOIS: return _doWHOIS(RR,tPtr,peer);
+ case Packet::VERB_RENDEZVOUS: return _doRENDEZVOUS(RR,tPtr,peer);
+ case Packet::VERB_FRAME: return _doFRAME(RR,tPtr,peer);
+ case Packet::VERB_EXT_FRAME: return _doEXT_FRAME(RR,tPtr,peer);
+ case Packet::VERB_ECHO: return _doECHO(RR,tPtr,peer);
+ case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,tPtr,peer);
+ case Packet::VERB_NETWORK_CREDENTIALS: return _doNETWORK_CREDENTIALS(RR,tPtr,peer);
+ case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,tPtr,peer);
+ case Packet::VERB_NETWORK_CONFIG: return _doNETWORK_CONFIG(RR,tPtr,peer);
+ case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,tPtr,peer);
+ case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,tPtr,peer);
+ case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,tPtr,peer);
+ case Packet::VERB_USER_MESSAGE: return _doUSER_MESSAGE(RR,tPtr,peer);
+ case Packet::VERB_REMOTE_TRACE: return _doREMOTE_TRACE(RR,tPtr,peer);
}
} else {
- RR->sw->requestWhois(sourceAddress);
+ RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
return false;
}
} catch ( ... ) {
- // Exceptions are more informatively caught in _do...() handlers but
- // this outer try/catch will catch anything else odd.
- TRACE("dropped ??? from %s(%s): unexpected exception in tryDecode()",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
return true;
}
}
-bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
- const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
- const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
-
- //TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb));
-
- switch(errorCode) {
-
- case Packet::ERROR_OBJ_NOT_FOUND:
- if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->controller() == peer->address()))
- network->setNotFound();
- }
- break;
-
- case Packet::ERROR_UNSUPPORTED_OPERATION:
- if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->controller() == peer->address()))
- network->setNotFound();
- }
- break;
-
- case Packet::ERROR_IDENTITY_COLLISION:
- if (RR->topology->isRoot(peer->identity()))
- RR->node->postEvent(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
- break;
-
- case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
- /* Note: certificates are public so it's safe to push them to anyone
- * who asks. We won't communicate unless we also get a certificate
- * from the remote that agrees. */
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->hasConfig())&&(network->config().com)) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
- network->config().com.serialize(outp);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- }
- } break;
-
- case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
+ const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
+ const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
+ uint64_t networkId = 0;
+
+ /* Security note: we do not gate doERROR() with expectingReplyTo() to
+ * avoid having to log every outgoing packet ID. Instead we put the
+ * logic to determine whether we should consider an ERROR in each
+ * error handler. In most cases these are only trusted in specific
+ * circumstances. */
+
+ switch(errorCode) {
+
+ case Packet::ERROR_OBJ_NOT_FOUND:
+ // Object not found, currently only meaningful from network controllers.
+ if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
- network->setAccessDenied();
- } break;
+ network->setNotFound();
+ }
+ break;
+
+ case Packet::ERROR_UNSUPPORTED_OPERATION:
+ // This can be sent in response to any operation, though right now we only
+ // consider it meaningful from network controllers. This would indicate
+ // that the queried node does not support acting as a controller.
+ if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ if ((network)&&(network->controller() == peer->address()))
+ network->setNotFound();
+ }
+ break;
+
+ case Packet::ERROR_IDENTITY_COLLISION:
+ // FIXME: for federation this will need a payload with a signature or something.
+ if (RR->topology->isUpstream(peer->identity()))
+ RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
+ break;
+
+ case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
+ // Peers can send this in response to frames if they do not have a recent enough COM from us
+ networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ const int64_t now = RR->node->now();
+ if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
+ network->pushCredentialsNow(tPtr,peer->address(),now);
+ } break;
+
+ case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
+ // Network controller: network access denied.
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ if ((network)&&(network->controller() == peer->address()))
+ network->setAccessDenied();
+ } break;
+
+ case Packet::ERROR_UNWANTED_MULTICAST: {
+ // Members of networks can use this error to indicate that they no longer
+ // want to receive multicasts on a given channel.
+ networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if ((network)&&(network->gate(tPtr,peer))) {
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
+ RR->mc->remove(network->id(),mg,peer->address());
+ }
+ } break;
- case Packet::ERROR_UNWANTED_MULTICAST: {
- uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
- MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
- TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str());
- RR->mc->remove(nwid,mg,peer->address());
- } break;
+ default: break;
+ }
- default: break;
- }
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false,networkId);
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb);
- } catch ( ... ) {
- TRACE("dropped ERROR from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
- }
return true;
}
-bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer)
+bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated)
{
- /* Note: this is the only packet ever sent in the clear, and it's also
- * the only packet that we authenticate via a different path. Authentication
- * occurs here and is based on the validity of the identity and the
- * integrity of the packet's MAC, but it must be done after we check
- * the identity since HELLO is a mechanism for learning new identities
- * in the first place. */
+ const int64_t now = RR->node->now();
+
+ const uint64_t pid = packetId();
+ const Address fromAddress(source());
+ const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+ const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
+ const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
+ const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
+ const int64_t timestamp = at<int64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
+ Identity id;
+ unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
+
+ if (protoVersion < ZT_PROTO_VERSION_MIN) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"protocol version too old");
+ return true;
+ }
+ if (fromAddress != id.address()) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"identity/address mismatch");
+ return true;
+ }
- try {
- const uint64_t pid = packetId();
- const Address fromAddress(source());
- const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
- const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
- const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
- const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
- const uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
-
- Identity id;
- InetAddress externalSurfaceAddress;
- uint64_t worldId = ZT_WORLD_ID_NULL;
- uint64_t worldTimestamp = 0;
- {
- unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
- if (ptr < size()) // ZeroTier One < 1.0.3 did not include physical destination address info
- ptr += externalSurfaceAddress.deserialize(*this,ptr);
- if ((ptr + 16) <= size()) { // older versions also did not include World IDs or timestamps
- worldId = at<uint64_t>(ptr); ptr += 8;
- worldTimestamp = at<uint64_t>(ptr);
- }
- }
+ SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,id.address()));
+ if (peer) {
+ // We already have an identity with this address -- check for collisions
+ if (!alreadyAuthenticated) {
+ if (peer->identity() != id) {
+ // Identity is different from the one we already have -- address collision
- if (protoVersion < ZT_PROTO_VERSION_MIN) {
- TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
- if (fromAddress != id.address()) {
- TRACE("dropped HELLO from %s(%s): identity not for sending address",fromAddress.toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
+ // Check rate limits
+ if (!RR->node->rateGateIdentityVerification(now,_path->address()))
+ return true;
- if (!peer) { // peer == NULL is the normal case here
- peer = RR->topology->getPeer(id.address());
- if (peer) {
- // We already have an identity with this address -- check for collisions
-
- if (peer->identity() != id) {
- // Identity is different from the one we already have -- address collision
-
- unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
- if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
- if (dearmor(key)) { // ensure packet is authentic, otherwise drop
- TRACE("rejected HELLO from %s(%s): address already claimed",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append((uint64_t)pid);
- outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
- outp.armor(key,true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } else {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- }
+ uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
+ if (RR->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
+ if (dearmor(key)) { // ensure packet is authentic, otherwise drop
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision");
+ Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((uint8_t)Packet::VERB_HELLO);
+ outp.append((uint64_t)pid);
+ outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
+ outp.armor(key,true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
} else {
- TRACE("rejected HELLO from %s(%s): key agreement failed",id.address().toString().c_str(),_remoteAddress.toString().c_str());
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
}
-
- return true;
} else {
- // Identity is the same as the one we already have -- check packet integrity
-
- if (!dearmor(peer->key())) {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
-
- // Continue at // VALID
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid identity");
}
- } else {
- // We don't already have an identity with this address -- validate and learn it
- // Check identity proof of work
- if (!id.locallyValidate()) {
- TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
+ return true;
+ } else {
+ // Identity is the same as the one we already have -- check packet integrity
- // Check packet integrity and authentication
- SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
- if (!dearmor(newPeer->key())) {
- TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
+ if (!dearmor(peer->key())) {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
return true;
}
- peer = RR->topology->addPeer(newPeer);
// Continue at // VALID
}
+ } // else if alreadyAuthenticated then continue at // VALID
+ } else {
+ // We don't already have an identity with this address -- validate and learn it
- // VALID -- if we made it here, packet passed identity and authenticity checks!
+ // Sanity check: this basically can't happen
+ if (alreadyAuthenticated) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"illegal alreadyAuthenticated state");
+ return true;
}
- if (externalSurfaceAddress)
- RR->sa->iam(id.address(),_localAddress,_remoteAddress,externalSurfaceAddress,RR->topology->isRoot(id),RR->node->now());
-
- Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append((uint64_t)pid);
- outp.append((uint64_t)timestamp);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- if (protoVersion >= 5) {
- _remoteAddress.serialize(outp);
- } else {
- /* LEGACY COMPATIBILITY HACK:
- *
- * For a while now (since 1.0.3), ZeroTier has recognized changes in
- * its network environment empirically by examining its external network
- * address as reported by trusted peers. In versions prior to 1.1.0
- * (protocol version < 5), they did this by saving a snapshot of this
- * information (in SelfAwareness.hpp) keyed by reporting device ID and
- * address type.
- *
- * This causes problems when clustering is combined with symmetric NAT.
- * Symmetric NAT remaps ports, so different endpoints in a cluster will
- * report back different exterior addresses. Since the old code keys
- * this by device ID and not sending physical address and compares the
- * entire address including port, it constantly thinks its external
- * surface is changing and resets connections when talking to a cluster.
- *
- * In new code we key by sending physical address and device and we also
- * take the more conservative position of only interpreting changes in
- * IP address (neglecting port) as a change in network topology that
- * necessitates a reset. But we can make older clients work here by
- * nulling out the port field. Since this info is only used for empirical
- * detection of link changes, it doesn't break anything else.
- */
- InetAddress tmpa(_remoteAddress);
- tmpa.setPort(0);
- tmpa.serialize(outp);
+ // Check rate limits
+ if (!RR->node->rateGateIdentityVerification(now,_path->address())) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"rate limit exceeded");
+ return true;
}
- if ((worldId != ZT_WORLD_ID_NULL)&&(RR->topology->worldTimestamp() > worldTimestamp)&&(worldId == RR->topology->worldId())) {
- World w(RR->topology->world());
- const unsigned int sizeAt = outp.size();
- outp.addSize(2); // make room for 16-bit size field
- w.serialize(outp,false);
- outp.setAt<uint16_t>(sizeAt,(uint16_t)(outp.size() - (sizeAt + 2)));
- } else {
- outp.append((uint16_t)0); // no world update needed
+ // Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
+ SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
+ if (!dearmor(newPeer->key())) {
+ RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+ return true;
}
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ // Check that identity's address is valid as per the derivation function
+ if (!id.locallyValidate()) {
+ RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"invalid identity");
+ return true;
+ }
- peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
- peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ peer = RR->topology->addPeer(tPtr,newPeer);
+
+ // Continue at // VALID
+ }
+
+ // VALID -- if we made it here, packet passed identity and authenticity checks!
+
+ // Get external surface address if present (was not in old versions)
+ InetAddress externalSurfaceAddress;
+ if (ptr < size()) {
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
+ if ((externalSurfaceAddress)&&(hops() == 0))
+ RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
+ }
+
+ // Get primary planet world ID and world timestamp if present
+ uint64_t planetWorldId = 0;
+ uint64_t planetWorldTimestamp = 0;
+ if ((ptr + 16) <= size()) {
+ planetWorldId = at<uint64_t>(ptr); ptr += 8;
+ planetWorldTimestamp = at<uint64_t>(ptr); ptr += 8;
}
+
+ std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
+ if (ptr < size()) {
+ // Remainder of packet, if present, is encrypted
+ cryptField(peer->key(),ptr,size() - ptr);
+
+ // Get moon IDs and timestamps if present
+ if ((ptr + 2) <= size()) {
+ const unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
+ for(unsigned int i=0;i<numMoons;++i) {
+ if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
+ moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
+ ptr += 16;
+ }
+ }
+ }
+
+ // Send OK(HELLO) with an echo of the packet's timestamp and some of the same
+ // information about us: version, sent-to address, etc.
+
+ Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append((uint64_t)pid);
+ outp.append((uint64_t)timestamp);
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+
+ if (protoVersion >= 5) {
+ _path->address().serialize(outp);
+ } else {
+ /* LEGACY COMPATIBILITY HACK:
+ *
+ * For a while now (since 1.0.3), ZeroTier has recognized changes in
+ * its network environment empirically by examining its external network
+ * address as reported by trusted peers. In versions prior to 1.1.0
+ * (protocol version < 5), they did this by saving a snapshot of this
+ * information (in SelfAwareness.hpp) keyed by reporting device ID and
+ * address type.
+ *
+ * This causes problems when clustering is combined with symmetric NAT.
+ * Symmetric NAT remaps ports, so different endpoints in a cluster will
+ * report back different exterior addresses. Since the old code keys
+ * this by device ID and not sending physical address and compares the
+ * entire address including port, it constantly thinks its external
+ * surface is changing and resets connections when talking to a cluster.
+ *
+ * In new code we key by sending physical address and device and we also
+ * take the more conservative position of only interpreting changes in
+ * IP address (neglecting port) as a change in network topology that
+ * necessitates a reset. But we can make older clients work here by
+ * nulling out the port field. Since this info is only used for empirical
+ * detection of link changes, it doesn't break anything else.
+ */
+ InetAddress tmpa(_path->address());
+ tmpa.setPort(0);
+ tmpa.serialize(outp);
+ }
+
+ const unsigned int worldUpdateSizeAt = outp.size();
+ outp.addSize(2); // make room for 16-bit size field
+ if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) {
+ RR->topology->planet().serialize(outp,false);
+ }
+ if (moonIdsAndTimestamps.size() > 0) {
+ std::vector<World> moons(RR->topology->moons());
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ for(std::vector< std::pair<uint64_t,uint64_t> >::const_iterator i(moonIdsAndTimestamps.begin());i!=moonIdsAndTimestamps.end();++i) {
+ if (i->first == m->id()) {
+ if (m->timestamp() > i->second)
+ m->serialize(outp,false);
+ break;
+ }
+ }
+ }
+ }
+ outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
+
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),now);
+
+ peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
+ peer->received(tPtr,_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false,0);
+
return true;
}
-bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
- const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+ const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
+ const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+ uint64_t networkId = 0;
- //TRACE("%s(%s): OK(%s)",source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb));
+ if (!RR->node->expectingReplyTo(inRePacketId))
+ return true;
- switch(inReVerb) {
+ switch(inReVerb) {
- case Packet::VERB_HELLO: {
- const unsigned int latency = std::min((unsigned int)(RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP)),(unsigned int)0xffff);
- const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
- const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
- const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
- const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ case Packet::VERB_HELLO: {
+ const uint64_t latency = RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP);
+ if (latency > ZT_HELLO_MAX_ALLOWABLE_LATENCY)
+ return true;
- if (vProto < ZT_PROTO_VERSION_MIN) {
- TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
+ const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
+ const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
+ const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
+ const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ if (vProto < ZT_PROTO_VERSION_MIN)
+ return true;
+
+ InetAddress externalSurfaceAddress;
+ unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
- const bool trusted = RR->topology->isRoot(peer->identity());
+ // Get reported external surface address if present
+ if (ptr < size())
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
- InetAddress externalSurfaceAddress;
- unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
- if (ptr < size()) // ZeroTier One < 1.0.3 did not include this field
- ptr += externalSurfaceAddress.deserialize(*this,ptr);
- if ((trusted)&&((ptr + 2) <= size())) { // older versions also did not include this field, and right now we only use if from a root
- World worldUpdate;
- const unsigned int worldLen = at<uint16_t>(ptr); ptr += 2;
- if (worldLen > 0) {
+ // Handle planet or moon updates if present
+ if ((ptr + 2) <= size()) {
+ const unsigned int worldsLen = at<uint16_t>(ptr); ptr += 2;
+ if (RR->topology->shouldAcceptWorldUpdateFrom(peer->address())) {
+ const unsigned int endOfWorlds = ptr + worldsLen;
+ while (ptr < endOfWorlds) {
World w;
- w.deserialize(*this,ptr);
- RR->topology->worldUpdateIfValid(w);
+ ptr += w.deserialize(*this,ptr);
+ RR->topology->addWorld(tPtr,w,false);
}
+ } else {
+ ptr += worldsLen;
}
+ }
- TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((externalSurfaceAddress) ? externalSurfaceAddress.toString().c_str() : "(none)"));
-
- peer->addDirectLatencyMeasurment(latency);
- peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
+ if (!hops())
+ _path->updateLatency((unsigned int)latency);
- if (externalSurfaceAddress)
- RR->sa->iam(peer->address(),_localAddress,_remoteAddress,externalSurfaceAddress,trusted,RR->node->now());
- } break;
+ peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
- case Packet::VERB_WHOIS: {
- if (RR->topology->isRoot(peer->identity())) {
- const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
- // Right now we can skip this since OK(WHOIS) is only accepted from
- // roots. In the future it should be done if we query less trusted
- // sources.
- //if (id.locallyValidate())
- RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
- }
- } break;
-
- case Packet::VERB_NETWORK_CONFIG_REQUEST: {
- const SharedPtr<Network> nw(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID)));
- if ((nw)&&(nw->controller() == peer->address())) {
- const unsigned int nclen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
- if (nclen) {
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,nclen),nclen);
- NetworkConfig nconf;
- if (nconf.fromDictionary(dconf)) {
- nw->setConfiguration(nconf,true);
- TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str());
- }
- }
- }
- } break;
+ if ((externalSurfaceAddress)&&(hops() == 0))
+ RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
+ } break;
- //case Packet::VERB_ECHO: {
- //} break;
-
- case Packet::VERB_MULTICAST_GATHER: {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
+ case Packet::VERB_WHOIS:
+ if (RR->topology->isUpstream(peer->identity())) {
+ const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
+ RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
+ }
+ break;
+
+ case Packet::VERB_NETWORK_CONFIG_REQUEST: {
+ networkId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network)
+ network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+ } break;
+
+ case Packet::VERB_MULTICAST_GATHER: {
+ networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network) {
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
- TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,mg.toString().c_str(),size());
const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
- RR->mc->addMultiple(RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
- } break;
-
- case Packet::VERB_MULTICAST_FRAME: {
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
+ RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
+ }
+ } break;
- //TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),nwid,mg.toString().c_str(),flags);
+ case Packet::VERB_MULTICAST_FRAME: {
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_FLAGS];
+ networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_NETWORK_ID);
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_ADI));
+ const SharedPtr<Network> network(RR->node->network(networkId));
+ if (network) {
unsigned int offset = 0;
- if ((flags & 0x01) != 0) {
- // OK(MULTICAST_FRAME) includes certificate of membership update
+ if ((flags & 0x01) != 0) { // deprecated but still used by older peers
CertificateOfMembership com;
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
- peer->validateAndSetNetworkMembershipCertificate(nwid,com);
+ if (com)
+ network->addCredential(tPtr,com);
}
if ((flags & 0x02) != 0) {
@@ -463,901 +499,684 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
unsigned int count = at<uint16_t>(offset); offset += 2;
- RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
+ RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(offset,count * 5),count,totalKnown);
}
- } break;
-
- default: break;
- }
+ }
+ } break;
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb);
- } catch ( ... ) {
- TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ default: break;
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false,networkId);
+
return true;
}
-bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (payloadLength() == ZT_ADDRESS_LENGTH) {
- Identity queried(RR->topology->getIdentity(Address(payload(),ZT_ADDRESS_LENGTH)));
- if (queried) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_WHOIS);
- outp.append(packetId());
- queried.serialize(outp,false);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } else {
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->sendDistributedQuery(*this);
-#endif
- }
+ if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
+ return true;
+
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_WHOIS);
+ outp.append(packetId());
+
+ unsigned int count = 0;
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
+ while ((ptr + ZT_ADDRESS_LENGTH) <= size()) {
+ const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ ptr += ZT_ADDRESS_LENGTH;
+
+ const Identity id(RR->topology->getIdentity(tPtr,addr));
+ if (id) {
+ id.serialize(outp,false);
+ ++count;
} else {
- TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str());
+ // Request unknown WHOIS from upstream from us (if we have one)
+ RR->sw->requestWhois(tPtr,RR->node->now(),addr);
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped WHOIS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
+
+ if (count > 0) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false,0);
+
return true;
}
-bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- if (RR->topology->isUpstream(peer->identity())) {
- const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- const SharedPtr<Peer> withPeer(RR->topology->getPeer(with));
- if (withPeer) {
- const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
- const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
- if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP);
-
- InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
- TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
- if (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,atAddr))
- RR->sw->rendezvous(withPeer,_localAddress,atAddr);
- } else {
- TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ if (RR->topology->isUpstream(peer->identity())) {
+ const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(tPtr,with));
+ if (rendezvousWith) {
+ const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
+ const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
+ if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
+ const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
+ if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localSocket(),atAddr)) {
+ const uint64_t junk = RR->node->prng();
+ RR->node->putPacket(tPtr,_path->localSocket(),atAddr,&junk,4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
+ rendezvousWith->attemptToContactAt(tPtr,_path->localSocket(),atAddr,RR->node->now(),false);
}
- } else {
- RR->sw->requestWhois(with);
- TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),with.toString().c_str());
}
- } else {
- TRACE("ignored RENDEZVOUS from %s(%s): not a root server or a network relay",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
- } catch ( ... ) {
- TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false,0);
+
return true;
}
-bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)));
- if (network) {
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ bool trustEstablished = false;
+ if (network) {
+ if (network->gate(tPtr,peer)) {
+ trustEstablished = true;
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
- if (!network->isAllowed(peer)) {
- TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id());
- _sendErrorNeedCertificate(RR,peer,network->id());
- return true;
- }
-
const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
- if (!network->config().permitsEtherType(etherType)) {
- TRACE("dropped FRAME from %s(%s): ethertype %.4x not allowed on %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
- return true;
- }
-
- const unsigned int payloadLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
- RR->node->putFrame(network->id(),network->userPtr(),MAC(peer->address(),network->id()),network->mac(),etherType,0,field(ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
+ const MAC sourceMac(peer->address(),nwid);
+ const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+ const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+ if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
}
-
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP);
} else {
- TRACE("dropped FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_FRAME,true);
}
- } catch ( ... ) {
- TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ } else {
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished,nwid);
+
return true;
}
-bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID)));
- if (network) {
- if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
- const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
-
- unsigned int comLen = 0;
- if ((flags & 0x01) != 0) {
- CertificateOfMembership com;
- comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
- peer->validateAndSetNetworkMembershipCertificate(network->id(),com);
- }
-
- if (!network->isAllowed(peer)) {
- TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id());
- _sendErrorNeedCertificate(RR,peer,network->id());
- return true;
- }
-
- // Everything after flags must be adjusted based on the length
- // of the certificate, if there was one...
-
- const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
- if (!network->config().permitsEtherType(etherType)) {
- TRACE("dropped EXT_FRAME from %s(%s): ethertype %.4x not allowed on network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id());
- return true;
- }
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
+
+ unsigned int comLen = 0;
+ if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
+ CertificateOfMembership com;
+ comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
+ if (com)
+ network->addCredential(tPtr,com);
+ }
- const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
- const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
+ if (!network->gate(tPtr,peer)) {
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,true);
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
+ return true;
+ }
- if (to.isMulticast()) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: destination is multicast, must use MULTICAST_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
- }
+ if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
+ const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
+ const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
+ const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
+ const unsigned int frameLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
+ const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
- if ((!from)||(from.isMulticast())||(from == network->mac())) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
- }
+ if ((!from)||(from == network->mac())) {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
- if (from != MAC(peer->address(),network->id())) {
- if (network->config().permitsBridging(peer->address())) {
- network->learnBridgeRoute(from,peer->address());
- } else {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
- return true;
- }
- } else if (to != network->mac()) {
- if (!network->config().permitsBridging(RR->identity.address())) {
- TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
- return true;
+ switch (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to,frameData,frameLen,etherType,0)) {
+ case 1:
+ if (from != MAC(peer->address(),nwid)) {
+ if (network->config().permitsBridging(peer->address())) {
+ network->learnBridgeRoute(from,peer->address());
+ } else {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
+ } else if (to != network->mac()) {
+ if (to.isMulticast()) {
+ if (network->config().multicastLimit == 0) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
+ } else if (!network->config().permitsBridging(RR->identity.address())) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
+ }
}
- }
-
- const unsigned int payloadLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
- RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,0,field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
+ // fall through -- 2 means accept regardless of bridging checks or other restrictions
+ case 2:
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
+ break;
}
-
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP);
- } else {
- TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
}
- } catch ( ... ) {
- TRACE("dropped EXT_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
- return true;
-}
-
-bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t pid = packetId();
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_ECHO);
- outp.append((uint64_t)pid);
- if (size() > ZT_PACKET_IDX_PAYLOAD)
- outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
- return true;
-}
-
-bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t now = RR->node->now();
- // Iterate through 18-byte network,MAC,ADI tuples
- for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
- const uint64_t nwid = at<uint64_t>(ptr);
- const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
- RR->mc->add(now,nwid,group,peer->address());
+ if ((flags & 0x10) != 0) { // ACK requested
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_EXT_FRAME);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid);
+ } else {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
}
+
return true;
}
-bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- CertificateOfMembership com;
+ if (!peer->rateGateEchoRequest(RR->node->now()))
+ return true;
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
- while (ptr < size()) {
- ptr += com.deserialize(*this,ptr);
- peer->validateAndSetNetworkMembershipCertificate(com.networkId(),com);
- }
+ const uint64_t pid = packetId();
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_ECHO);
+ outp.append((uint64_t)pid);
+ if (size() > ZT_PACKET_IDX_PAYLOAD)
+ outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+
+ peer->received(tPtr,_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0);
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
return true;
}
-bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
-
- const unsigned int metaDataLength = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
- const char *metaDataBytes = (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength);
- const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
-
- //const uint64_t haveRevision = ((ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength + 8) <= size()) ? at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT + metaDataLength) : 0ULL;
-
- const unsigned int h = hops();
- const uint64_t pid = packetId();
- peer->received(_localAddress,_remoteAddress,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP);
-
- if (RR->localNetworkController) {
- NetworkConfig netconf;
- switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,RR->identity,peer->identity(),nwid,metaData,netconf)) {
-
- case NetworkController::NETCONF_QUERY_OK: {
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf;
- if (netconf.toDictionary(dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append(nwid);
- const unsigned int dlen = dconf.sizeBytes();
- outp.append((uint16_t)dlen);
- outp.append((const void *)dconf.data(),dlen);
- outp.compress();
- RR->sw->send(outp,true,0);
- }
- } break;
-
- case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } break;
-
- case NetworkController::NETCONF_QUERY_ACCESS_DENIED: {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } break;
-
- case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR:
- // TRACE("NETWORK_CONFIG_REQUEST failed: internal error: %s",netconf.get("error","(unknown)").c_str());
- break;
+ const int64_t now = RR->node->now();
- case NetworkController::NETCONF_QUERY_IGNORE:
- break;
+ uint64_t authOnNetwork[256]; // cache for approved network IDs
+ unsigned int authOnNetworkCount = 0;
+ SharedPtr<Network> network;
+ bool trustEstablished = false;
- default:
- TRACE("NETWORK_CONFIG_REQUEST failed: invalid return value from NetworkController::doNetworkConfigRequest()");
- break;
+ // Iterate through 18-byte network,MAC,ADI tuples
+ for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
+ const uint64_t nwid = at<uint64_t>(ptr);
+ bool auth = false;
+ for(unsigned int i=0;i<authOnNetworkCount;++i) {
+ if (nwid == authOnNetwork[i]) {
+ auth = true;
+ break;
+ }
+ }
+ if (!auth) {
+ if ((!network)||(network->id() != nwid))
+ network = RR->node->network(nwid);
+ const bool authOnNet = ((network)&&(network->gate(tPtr,peer)));
+ if (!authOnNet)
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ trustEstablished |= authOnNet;
+ if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
+ auth = true;
+ if (authOnNetworkCount < 256) // sanity check, packets can't really be this big
+ authOnNetwork[authOnNetworkCount++] = nwid;
}
- } else {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
- } catch ( ... ) {
- TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
- return true;
-}
-bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
- while ((ptr + 8) <= size()) {
- uint64_t nwid = at<uint64_t>(ptr);
- SharedPtr<Network> nw(RR->node->network(nwid));
- if ((nw)&&(peer->address() == nw->controller()))
- nw->requestConfiguration();
- ptr += 8;
+ if (auth) {
+ const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
+ RR->mc->add(tPtr,now,nwid,group,peer->address());
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
- return true;
-}
-
-bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
- const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
- const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
- //TRACE("<<MC %s(%s) GATHER up to %u in %.16llx/%s",source().toString().c_str(),_remoteAddress.toString().c_str(),gatherLimit,nwid,mg.toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
- if (gatherLimit) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
- outp.append(packetId());
- outp.append(nwid);
- mg.mac().appendTo(outp);
- outp.append((uint32_t)mg.adi());
- const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
- if (gatheredLocally) {
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- if ((RR->cluster)&&(gatheredLocally < gatherLimit))
- RR->cluster->sendDistributedQuery(*this);
-#endif
- }
-
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
return true;
}
-bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
- const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
-
- const SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- // Offset -- size of optional fields added to position of later fields
- unsigned int offset = 0;
-
- if ((flags & 0x01) != 0) {
- CertificateOfMembership com;
- offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
- peer->validateAndSetNetworkMembershipCertificate(nwid,com);
- }
-
- // Check membership after we've read any included COM, since
- // that cert might be what we needed.
- if (!network->isAllowed(peer)) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id());
- _sendErrorNeedCertificate(RR,peer,network->id());
- return true;
- }
-
- unsigned int gatherLimit = 0;
- if ((flags & 0x02) != 0) {
- gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
- offset += 4;
- }
-
- MAC from;
- if ((flags & 0x04) != 0) {
- from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
- offset += 6;
- } else {
- from.fromAddress(peer->address(),nwid);
- }
-
- const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
- const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
- const unsigned int payloadLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
-
- //TRACE("<<MC FRAME %.16llx/%s from %s@%s flags %.2x length %u",nwid,to.toString().c_str(),from.toString().c_str(),peer->address().toString().c_str(),flags,payloadLen);
+ if (!peer->rateGateCredentialsReceived(RR->node->now()))
+ return true;
- if ((payloadLen > 0)&&(payloadLen <= ZT_IF_MTU)) {
- if (!to.mac().isMulticast()) {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: destination is unicast, must use FRAME or EXT_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
+ CertificateOfMembership com;
+ Capability cap;
+ Tag tag;
+ Revocation revocation;
+ CertificateOfOwnership coo;
+ bool trustEstablished = false;
+ SharedPtr<Network> network;
+
+ unsigned int p = ZT_PACKET_IDX_PAYLOAD;
+ while ((p < size())&&((*this)[p] != 0)) {
+ p += com.deserialize(*this,p);
+ if (com) {
+ network = RR->node->network(com.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,com)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
- if ((!from)||(from.isMulticast())||(from == network->mac())) {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str());
- return true;
+ } else RR->mc->addCredential(tPtr,com,false);
+ }
+ }
+ ++p; // skip trailing 0 after COMs if present
+
+ if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
+ const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCapabilities;++i) {
+ p += cap.deserialize(*this,p);
+ if ((!network)||(network->id() != cap.networkId()))
+ network = RR->node->network(cap.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,cap)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
+ }
+ }
- if (from != MAC(peer->address(),network->id())) {
- if (network->config().permitsBridging(peer->address())) {
- network->learnBridgeRoute(from,peer->address());
- } else {
- TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
- return true;
- }
+ if (p >= size()) return true;
+
+ const unsigned int numTags = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numTags;++i) {
+ p += tag.deserialize(*this,p);
+ if ((!network)||(network->id() != tag.networkId()))
+ network = RR->node->network(tag.networkId());
+ if (network) {
+ switch (network->addCredential(tPtr,tag)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
-
- RR->node->putFrame(network->id(),network->userPtr(),from,to.mac(),etherType,0,field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,payloadLen),payloadLen);
}
+ }
- if (gatherLimit) {
- Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
- outp.append(packetId());
- outp.append(nwid);
- to.mac().appendTo(outp);
- outp.append((uint32_t)to.adi());
- outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
- if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ if (p >= size()) return true;
+
+ const unsigned int numRevocations = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numRevocations;++i) {
+ p += revocation.deserialize(*this,p);
+ if ((!network)||(network->id() != revocation.networkId()))
+ network = RR->node->network(revocation.networkId());
+ if (network) {
+ switch(network->addCredential(tPtr,peer->address(),revocation)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
- } // else ignore -- not a member of this network
+ }
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ if (p >= size()) return true;
+
+ const unsigned int numCoos = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numCoos;++i) {
+ p += coo.deserialize(*this,p);
+ if ((!network)||(network->id() != coo.networkId()))
+ network = RR->node->network(coo.networkId());
+ if (network) {
+ switch(network->addCredential(tPtr,coo)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
+ }
+ }
+ }
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
+
return true;
}
-bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const uint64_t now = RR->node->now();
-
- // First, subject this to a rate limit
- if (!peer->shouldRespondToDirectPathPush(now)) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
-
- // Second, limit addresses by scope and type
- uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
- memset(countPerScope,0,sizeof(countPerScope));
-
- unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
-
- while (count--) { // if ptr overflows Buffer will throw
- // TODO: some flags are not yet implemented
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
+ const unsigned int hopCount = hops();
+ const uint64_t requestPacketId = packetId();
+
+ if (RR->localNetworkController) {
+ const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= size()) ? at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN) : 0;
+ const char *metaDataBytes = (metaDataLength != 0) ? (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength) : (const char *)0;
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
+ RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData);
+ } else {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
+ outp.append(nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
- unsigned int flags = (*this)[ptr++];
- unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
- ptr += extLen; // unused right now
- unsigned int addrType = (*this)[ptr++];
- unsigned int addrLen = (*this)[ptr++];
+ peer->received(tPtr,_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false,nwid);
- switch(addrType) {
- case 4: {
- InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
+ return true;
+}
- bool redundant = false;
- if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
- peer->setClusterOptimalPathForAddressFamily(a);
- } else {
- redundant = peer->hasActivePathTo(now,a);
- }
+bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
+ if (network) {
+ const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
+ if (configUpdateId) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_ECHO);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)network->id());
+ outp.append((uint64_t)configUpdateId);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
+ }
- if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) {
- if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- peer->sendHELLO(InetAddress(),a,now);
- } else {
- TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
- }
- }
- } break;
- case 6: {
- InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false,(network) ? network->id() : 0);
- bool redundant = false;
- if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
- peer->setClusterOptimalPathForAddressFamily(a);
- } else {
- redundant = peer->hasActivePathTo(now,a);
- }
+ return true;
+}
- if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) {
- if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- peer->sendHELLO(InetAddress(),a,now);
- } else {
- TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
- }
- }
- } break;
+bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
+ const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
+
+ const SharedPtr<Network> network(RR->node->network(nwid));
+
+ if ((flags & 0x01) != 0) {
+ try {
+ CertificateOfMembership com;
+ com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
+ if (com) {
+ if (network)
+ network->addCredential(tPtr,com);
+ else RR->mc->addCredential(tPtr,com,false);
}
- ptr += addrLen;
- }
+ } catch ( ... ) {} // discard invalid COMs
+ }
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ const bool trustEstablished = ((network)&&(network->gate(tPtr,peer)));
+ if (!trustEstablished)
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
+ outp.append(packetId());
+ outp.append(nwid);
+ mg.mac().appendTo(outp);
+ outp.append((uint32_t)mg.adi());
+ const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
+ if (gatheredLocally > 0) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+ }
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished,nwid);
+
return true;
}
-bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- const Address originatorAddress(field(ZT_PACKET_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- SharedPtr<Peer> originator(RR->topology->getPeer(originatorAddress));
- if (!originator) {
- RR->sw->requestWhois(originatorAddress);
- return false;
+ const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
+ const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
+
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
+ // Offset -- size of optional fields added to position of later fields
+ unsigned int offset = 0;
+
+ if ((flags & 0x01) != 0) {
+ // This is deprecated but may still be sent by old peers
+ CertificateOfMembership com;
+ offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
+ if (com)
+ network->addCredential(tPtr,com);
}
- const unsigned int flags = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 5);
- const uint64_t timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 7);
- const uint64_t testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 15);
-
- // Tracks total length of variable length fields, initialized to originator credential length below
- unsigned int vlf;
-
- // Originator credentials
- const unsigned int originatorCredentialLength = vlf = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 23);
- uint64_t originatorCredentialNetworkId = 0;
- if (originatorCredentialLength >= 1) {
- switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) {
- case 0x01: { // 64-bit network ID, originator must be controller
- if (originatorCredentialLength >= 9)
- originatorCredentialNetworkId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 26);
- } break;
- default: break;
- }
+ if (!network->gate(tPtr,peer)) {
+ RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,true);
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
+ return true;
}
- // Add length of "additional fields," which are currently unused
- vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 25 + vlf);
+ unsigned int gatherLimit = 0;
+ if ((flags & 0x02) != 0) {
+ gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
+ offset += 4;
+ }
- // Verify signature -- only tests signed by their originators are allowed
- const unsigned int signatureLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 27 + vlf);
- if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str());
+ MAC from;
+ if ((flags & 0x04) != 0) {
+ from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
+ offset += 6;
+ } else {
+ from.fromAddress(peer->address(),nwid);
+ }
+
+ const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
+ const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
+ const unsigned int frameLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
+
+ if (network->config().multicastLimit == 0) {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"multicast disabled");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
return true;
}
- vlf += signatureLength;
-
- // Save this length so we can copy the immutable parts of this test
- // into the one we send along to next hops.
- const unsigned int lengthOfSignedPortionAndSignature = 29 + vlf;
-
- // Get previous hop's credential, if any
- const unsigned int previousHopCredentialLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 29 + vlf);
- CertificateOfMembership previousHopCom;
- if (previousHopCredentialLength >= 1) {
- switch((*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]) {
- case 0x01: { // network certificate of membership for previous hop
- const unsigned int phcl = previousHopCom.deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 32 + vlf);
- if (phcl != (previousHopCredentialLength - 1)) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): previous hop COM invalid (%u != %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),phcl,(previousHopCredentialLength - 1));
- return true;
- }
- } break;
- default: break;
+
+ if ((frameLen > 0)&&(frameLen <= ZT_MAX_MTU)) {
+ if (!to.mac().isMulticast()) {
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
}
- }
- vlf += previousHopCredentialLength;
-
- // Check credentials (signature already verified)
- NetworkConfig originatorCredentialNetworkConfig;
- if (originatorCredentialNetworkId) {
- if (Network::controllerFor(originatorCredentialNetworkId) == originatorAddress) {
- SharedPtr<Network> nw(RR->node->network(originatorCredentialNetworkId));
- if ((nw)&&(nw->hasConfig())) {
- originatorCredentialNetworkConfig = nw->config();
- if ( ( (originatorCredentialNetworkConfig.isPublic()) || (peer->address() == originatorAddress) || ((originatorCredentialNetworkConfig.com)&&(previousHopCom)&&(originatorCredentialNetworkConfig.com.agreesWith(previousHopCom))) ) ) {
- TRACE("CIRCUIT_TEST %.16llx received from hop %s(%s) and originator %s with valid network ID credential %.16llx (verified from originator and next hop)",testId,source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and previous hop %s did not supply a valid COM",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId,peer->address().toString().c_str());
- return true;
- }
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we are not a member",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
- return true;
- }
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID as credential, is not controller for %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
+ if ((!from)||(from.isMulticast())||(from == network->mac())) {
+ RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
return true;
}
- } else {
- TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str());
- return true;
- }
- const uint64_t now = RR->node->now();
-
- unsigned int breadth = 0;
- Address nextHop[256]; // breadth is a uin8_t, so this is the max
- InetAddress nextHopBestPathAddress[256];
- unsigned int remainingHopsPtr = ZT_PACKET_IDX_PAYLOAD + 33 + vlf;
- if ((ZT_PACKET_IDX_PAYLOAD + 31 + vlf) < size()) {
- // unsigned int nextHopFlags = (*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]
- breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf];
- for(unsigned int h=0;h<breadth;++h) {
- nextHop[h].setTo(field(remainingHopsPtr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- remainingHopsPtr += ZT_ADDRESS_LENGTH;
- SharedPtr<Peer> nhp(RR->topology->getPeer(nextHop[h]));
- if (nhp) {
- Path *const rp = nhp->getBestPath(now);
- if (rp)
- nextHopBestPathAddress[h] = rp->address();
+ const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
+
+ if ((flags & 0x08)&&(network->config().isMulticastReplicator(RR->identity.address())))
+ RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
+
+ if (from != MAC(peer->address(),nwid)) {
+ if (network->config().permitsBridging(peer->address())) {
+ network->learnBridgeRoute(from,peer->address());
+ } else {
+ RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"bridging not allowed (remote)");
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+ return true;
}
}
- }
- // Report back to originator, depending on flags and whether we are last hop
- if ( ((flags & 0x01) != 0) || ((breadth == 0)&&((flags & 0x02) != 0)) ) {
- Packet outp(originatorAddress,RR->identity.address(),Packet::VERB_CIRCUIT_TEST_REPORT);
- outp.append((uint64_t)timestamp);
- outp.append((uint64_t)testId);
- outp.append((uint64_t)0); // field reserved for future use
- outp.append((uint8_t)ZT_VENDOR_ZEROTIER);
- outp.append((uint8_t)ZT_PROTO_VERSION);
- outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.append((uint16_t)ZT_PLATFORM_UNSPECIFIED);
- outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED);
- outp.append((uint16_t)0); // error code, currently unused
- outp.append((uint64_t)0); // flags, currently unused
- outp.append((uint64_t)packetId());
- peer->address().appendTo(outp);
- outp.append((uint8_t)hops());
- _localAddress.serialize(outp);
- _remoteAddress.serialize(outp);
- outp.append((uint16_t)0); // no additional fields
- outp.append((uint8_t)breadth);
- for(unsigned int h=0;h<breadth;++h) {
- nextHop[h].appendTo(outp);
- nextHopBestPathAddress[h].serialize(outp); // appends 0 if null InetAddress
- }
- RR->sw->send(outp,true,0);
+ if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0)
+ RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
}
- // If there are next hops, forward the test along through the graph
- if (breadth > 0) {
- Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST);
- outp.append(field(ZT_PACKET_IDX_PAYLOAD,lengthOfSignedPortionAndSignature),lengthOfSignedPortionAndSignature);
- const unsigned int previousHopCredentialPos = outp.size();
- outp.append((uint16_t)0); // no previous hop credentials: default
- if ((originatorCredentialNetworkConfig)&&(!originatorCredentialNetworkConfig.isPublic())&&(originatorCredentialNetworkConfig.com)) {
- outp.append((uint8_t)0x01); // COM
- originatorCredentialNetworkConfig.com.serialize(outp);
- outp.setAt<uint16_t>(previousHopCredentialPos,(uint16_t)(outp.size() - (previousHopCredentialPos + 2)));
- }
- if (remainingHopsPtr < size())
- outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr);
-
- for(unsigned int h=0;h<breadth;++h) {
- if (RR->identity.address() != nextHop[h]) { // next hops that loop back to the current hop are not valid
- outp.newInitializationVector();
- outp.setDestination(nextHop[h]);
- RR->sw->send(outp,true,originatorCredentialNetworkId);
- }
+ if (gatherLimit) {
+ Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
+ outp.append(packetId());
+ outp.append(nwid);
+ to.mac().appendTo(outp);
+ outp.append((uint32_t)to.adi());
+ outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
+ if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
- peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+ } else {
+ _sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
}
+
return true;
}
-bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- try {
- ZT_CircuitTestReport report;
- memset(&report,0,sizeof(report));
-
- report.current = peer->address().toInt();
- report.upstream = Address(field(ZT_PACKET_IDX_PAYLOAD + 52,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
- report.testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 8);
- report.timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
- report.remoteTimestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 16);
- report.sourcePacketId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 44);
- report.flags = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 36);
- report.sourcePacketHopCount = (*this)[ZT_PACKET_IDX_PAYLOAD + 57]; // end of fixed length headers: 58
- report.errorCode = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 34);
- report.vendor = (enum ZT_Vendor)((*this)[ZT_PACKET_IDX_PAYLOAD + 24]);
- report.protocolVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 25];
- report.majorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 26];
- report.minorVersion = (*this)[ZT_PACKET_IDX_PAYLOAD + 27];
- report.revision = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 28);
- report.platform = (enum ZT_Platform)at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 30);
- report.architecture = (enum ZT_Architecture)at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 32);
-
- const unsigned int receivedOnLocalAddressLen = reinterpret_cast<InetAddress *>(&(report.receivedOnLocalAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58);
- const unsigned int receivedFromRemoteAddressLen = reinterpret_cast<InetAddress *>(&(report.receivedFromRemoteAddress))->deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen);
-
- unsigned int nhptr = ZT_PACKET_IDX_PAYLOAD + 58 + receivedOnLocalAddressLen + receivedFromRemoteAddressLen;
- nhptr += at<uint16_t>(nhptr) + 2; // add "additional field" length, which right now will be zero
-
- report.nextHopCount = (*this)[nhptr++];
- if (report.nextHopCount > ZT_CIRCUIT_TEST_MAX_HOP_BREADTH) // sanity check, shouldn't be possible
- report.nextHopCount = ZT_CIRCUIT_TEST_MAX_HOP_BREADTH;
- for(unsigned int h=0;h<report.nextHopCount;++h) {
- report.nextHops[h].address = Address(field(nhptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt(); nhptr += ZT_ADDRESS_LENGTH;
- nhptr += reinterpret_cast<InetAddress *>(&(report.nextHops[h].physicalAddress))->deserialize(*this,nhptr);
- }
+ const int64_t now = RR->node->now();
- RR->node->postCircuitTestReport(&report);
- } catch ( ... ) {
- TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
+ // First, subject this to a rate limit
+ if (!peer->rateGatePushDirectPaths(now)) {
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+ return true;
}
- return true;
-}
-bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
-{
- try {
- // If this were allowed from anyone, it would itself be a DOS vector. Right
- // now we only allow it from roots and controllers of networks you have joined.
- bool allowed = RR->topology->isRoot(peer->identity());
- if (!allowed) {
- std::vector< SharedPtr<Network> > allNetworks(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator n(allNetworks.begin());n!=allNetworks.end();++n) {
- if (peer->address() == (*n)->controller()) {
- allowed = true;
- break;
+ // Second, limit addresses by scope and type
+ uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
+ memset(countPerScope,0,sizeof(countPerScope));
+
+ unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
+
+ while (count--) { // if ptr overflows Buffer will throw
+ // TODO: some flags are not yet implemented
+
+ unsigned int flags = (*this)[ptr++];
+ unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
+ ptr += extLen; // unused right now
+ unsigned int addrType = (*this)[ptr++];
+ unsigned int addrLen = (*this)[ptr++];
+
+ switch(addrType) {
+ case 4: {
+ const InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
+ if (
+ ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+ (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
+ (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
+ {
+ if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
+ peer->clusterRedirect(tPtr,_path,a,now);
+ } else if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ peer->attemptToContactAt(tPtr,InetAddress(),a,now,false);
+ }
}
- }
- }
-
- if (allowed) {
- const uint64_t pid = packetId();
- const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1];
- const unsigned int challengeLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 2);
- if (challengeLength > ZT_PROTO_MAX_PACKET_LENGTH)
- return true; // sanity check, drop invalid size
- const unsigned char *challenge = field(ZT_PACKET_IDX_PAYLOAD + 4,challengeLength);
-
- switch((*this)[ZT_PACKET_IDX_PAYLOAD]) {
-
- // Salsa20/12+SHA512 hashcash
- case 0x01: {
- if (difficulty <= 14) {
- unsigned char result[16];
- computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result);
- TRACE("PROOF_OF_WORK computed for %s: difficulty==%u, challengeLength==%u, result: %.16llx%.16llx",peer->address().toString().c_str(),difficulty,challengeLength,Utils::ntoh(*(reinterpret_cast<const uint64_t *>(result))),Utils::ntoh(*(reinterpret_cast<const uint64_t *>(result + 8))));
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
- outp.append(pid);
- outp.append((uint16_t)sizeof(result));
- outp.append(result,sizeof(result));
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
- } else {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ } break;
+ case 6: {
+ const InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
+ if (
+ ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+ (!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
+ (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
+ {
+ if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0) {
+ peer->clusterRedirect(tPtr,_path,a,now);
+ } else if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ peer->attemptToContactAt(tPtr,InetAddress(),a,now,false);
}
- } break;
-
- default:
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
- break;
- }
-
- peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_REQUEST_PROOF_OF_WORK,0,Packet::VERB_NOP);
- } else {
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ }
+ } break;
}
- } catch ( ... ) {
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+ ptr += addrLen;
}
+
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+
return true;
}
-void IncomingPacket::computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16])
+bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
- char candidatebuf[ZT_PROTO_MAX_PACKET_LENGTH + 256];
- unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
- const uint64_t s20iv = 0; // zero IV for Salsa20
- char *const candidate = (char *)(( ((uintptr_t)&(candidatebuf[0])) | 0xf ) + 1); // align to 16-byte boundary to ensure that uint64_t type punning of initial nonce is okay
- Salsa20 s20;
- unsigned int d;
- unsigned char *p;
-
- Utils::getSecureRandom(candidate,16);
- memcpy(candidate + 16,challenge,challengeLength);
-
- if (difficulty > 512)
- difficulty = 512; // sanity check
-
-try_salsa2012sha512_again:
- ++*(reinterpret_cast<volatile uint64_t *>(candidate));
-
- SHA512::hash(shabuf,candidate,16 + challengeLength);
- s20.init(shabuf,256,&s20iv);
- memset(salsabuf,0,sizeof(salsabuf));
- s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf));
- SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
-
- d = difficulty;
- p = shabuf;
- while (d >= 8) {
- if (*(p++))
- goto try_salsa2012sha512_again;
- d -= 8;
- }
- if (d > 0) {
- if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
- goto try_salsa2012sha512_again;
+ if (likely(size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) {
+ ZT_UserMessage um;
+ um.origin = peer->address().toInt();
+ um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
+ um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8);
+ um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8);
+ RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
}
- memcpy(result,candidate,16);
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,false,0);
+
+ return true;
}
-bool IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16])
+bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
{
- unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
- char candidate[ZT_PROTO_MAX_PACKET_LENGTH + 256];
- unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
- const uint64_t s20iv = 0; // zero IV for Salsa20
- Salsa20 s20;
- unsigned int d;
- unsigned char *p;
-
- if (difficulty > 512)
- difficulty = 512; // sanity check
-
- memcpy(candidate,proposedResult,16);
- memcpy(candidate + 16,challenge,challengeLength);
-
- SHA512::hash(shabuf,candidate,16 + challengeLength);
- s20.init(shabuf,256,&s20iv);
- memset(salsabuf,0,sizeof(salsabuf));
- s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf));
- SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
-
- d = difficulty;
- p = shabuf;
- while (d >= 8) {
- if (*(p++))
- return false;
- d -= 8;
- }
- if (d > 0) {
- if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
- return false;
+ ZT_RemoteTrace rt;
+ const char *ptr = reinterpret_cast<const char *>(data()) + ZT_PACKET_IDX_PAYLOAD;
+ const char *const eof = reinterpret_cast<const char *>(data()) + size();
+ rt.origin = peer->address().toInt();
+ rt.data = const_cast<char *>(ptr); // start of first string
+ while (ptr < eof) {
+ if (!*ptr) { // end of string
+ rt.len = (unsigned int)(ptr - rt.data);
+ if ((rt.len > 0)&&(rt.len <= ZT_MAX_REMOTE_TRACE_SIZE)) {
+ RR->node->postEvent(tPtr,ZT_EVENT_REMOTE_TRACE,&rt);
+ }
+ rt.data = const_cast<char *>(++ptr); // start of next string, if any
+ } else {
+ ++ptr;
+ }
}
+ peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,false,0);
+
return true;
}
-void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid)
+void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid)
{
- Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)verb());
- outp.append(packetId());
- outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
- outp.append(nwid);
- outp.armor(peer->key(),true);
- RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+ const int64_t now = RR->node->now();
+ if (peer->rateGateOutgoingComRequest(now)) {
+ Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((uint8_t)verb());
+ outp.append(packetId());
+ outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
+ outp.append(nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,tPtr,outp.data(),outp.size(),now);
+ }
}
} // namespace ZeroTier
diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp
index cd0b7dcf..88f4f066 100644
--- a/node/IncomingPacket.hpp
+++ b/node/IncomingPacket.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_INCOMINGPACKET_HPP
@@ -22,7 +30,7 @@
#include <stdexcept>
#include "Packet.hpp"
-#include "InetAddress.hpp"
+#include "Path.hpp"
#include "Utils.hpp"
#include "MulticastGroup.hpp"
#include "Peer.hpp"
@@ -56,16 +64,8 @@ class IncomingPacket : public Packet
public:
IncomingPacket() :
Packet(),
- _receiveTime(0),
- _localAddress(),
- _remoteAddress()
- {
- }
-
- IncomingPacket(const IncomingPacket &p)
+ _receiveTime(0)
{
- // All fields including InetAddress are memcpy'able
- memcpy(this,&p,sizeof(IncomingPacket));
}
/**
@@ -73,42 +73,31 @@ public:
*
* @param data Packet data
* @param len Packet length
- * @param localAddress Local interface address
- * @param remoteAddress Address from which packet came
+ * @param path Path over which packet arrived
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
- IncomingPacket(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now) :
+ IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now) :
Packet(data,len),
_receiveTime(now),
- _localAddress(localAddress),
- _remoteAddress(remoteAddress)
+ _path(path)
{
}
- inline IncomingPacket &operator=(const IncomingPacket &p)
- {
- // All fields including InetAddress are memcpy'able
- memcpy(this,&p,sizeof(IncomingPacket));
- return *this;
- }
-
/**
* Init packet-in-decode in place
*
* @param data Packet data
* @param len Packet length
- * @param localAddress Local interface address
- * @param remoteAddress Address from which packet came
+ * @param path Path over which packet arrived
* @param now Current time
* @throws std::out_of_range Range error processing packet
*/
- inline void init(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now)
+ inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,int64_t now)
{
copyFrom(data,len);
_receiveTime = now;
- _localAddress = localAddress;
- _remoteAddress = remoteAddress;
+ _path = path;
}
/**
@@ -118,76 +107,44 @@ public:
* about whether the packet was valid. A rejection is 'complete.'
*
* Once true is returned, this must not be called again. The packet's state
- * may no longer be valid. The only exception is deferred decoding. In this
- * case true is returned to indicate to the normal decode path that it is
- * finished with the packet. The packet will have added itself to the
- * deferred queue and will expect tryDecode() to be called one more time
- * with deferred set to true.
- *
- * Deferred decoding is performed by DeferredPackets.cpp and should not be
- * done elsewhere. Under deferred decoding packets only get one shot and
- * so the return value of tryDecode() is ignored.
+ * may no longer be valid.
*
* @param RR Runtime environment
- * @param deferred If true, this is a deferred decode and the return is ignored
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @return True if decoding and processing is complete, false if caller should try again
*/
- bool tryDecode(const RuntimeEnvironment *RR,bool deferred);
+ bool tryDecode(const RuntimeEnvironment *RR,void *tPtr);
/**
* @return Time of packet receipt / start of decode
*/
- inline uint64_t receiveTime() const throw() { return _receiveTime; }
-
- /**
- * Compute the Salsa20/12+SHA512 proof of work function
- *
- * @param difficulty Difficulty in bits (max: 64)
- * @param challenge Challenge string
- * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
- * @param result Buffer to fill with 16-byte result
- */
- static void computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]);
-
- /**
- * Verify the result of Salsa20/12+SHA512 proof of work
- *
- * @param difficulty Difficulty in bits (max: 64)
- * @param challenge Challenge bytes
- * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
- * @param proposedResult Result supplied by client
- * @return True if result is valid
- */
- static bool testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]);
+ inline uint64_t receiveTime() const { return _receiveTime; }
private:
// These are called internally to handle packet contents once it has
// been authenticated, decrypted, decompressed, and classified.
- bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot
- bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-
- // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
- void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
+ bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
+ bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+ bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+
+ void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid);
uint64_t _receiveTime;
- InetAddress _localAddress;
- InetAddress _remoteAddress;
+ SharedPtr<Path> _path;
};
} // namespace ZeroTier
diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp
index 3f6b9be6..36b4e434 100644
--- a/node/InetAddress.cpp
+++ b/node/InetAddress.cpp
@@ -1,11 +1,11 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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.
+ * (at your oion) 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -32,7 +40,6 @@ const InetAddress InetAddress::LO4((const void *)("\x7f\x00\x00\x01"),4,0);
const InetAddress InetAddress::LO6((const void *)("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"),16,0);
InetAddress::IpScope InetAddress::ipScope() const
- throw()
{
switch(ss_family) {
@@ -55,7 +62,7 @@ InetAddress::IpScope InetAddress::ipScope() const
case 0x37: return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD)
case 0x38: return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service)
case 0x64:
- if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_SHARED; // 100.64.0.0/10
+ if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE; // 100.64.0.0/10
break;
case 0x7f: return IP_SCOPE_LOOPBACK; // 127.0.0.0/8
case 0xa9:
@@ -103,126 +110,114 @@ InetAddress::IpScope InetAddress::ipScope() const
return IP_SCOPE_NONE;
}
-void InetAddress::set(const std::string &ip,unsigned int port)
- throw()
-{
- memset(this,0,sizeof(InetAddress));
- if (ip.find(':') != std::string::npos) {
- struct sockaddr_in6 *sin6 = reinterpret_cast<struct sockaddr_in6 *>(this);
- ss_family = AF_INET6;
- sin6->sin6_port = Utils::hton((uint16_t)port);
- if (inet_pton(AF_INET6,ip.c_str(),(void *)&(sin6->sin6_addr.s6_addr)) <= 0)
- memset(this,0,sizeof(InetAddress));
- } else {
- struct sockaddr_in *sin = reinterpret_cast<struct sockaddr_in *>(this);
- ss_family = AF_INET;
- sin->sin_port = Utils::hton((uint16_t)port);
- if (inet_pton(AF_INET,ip.c_str(),(void *)&(sin->sin_addr.s_addr)) <= 0)
- memset(this,0,sizeof(InetAddress));
- }
-}
-
void InetAddress::set(const void *ipBytes,unsigned int ipLen,unsigned int port)
- throw()
{
memset(this,0,sizeof(InetAddress));
if (ipLen == 4) {
uint32_t ipb[1];
- memcpy(ipb,ipBytes,4);
+ ZT_FAST_MEMCPY(ipb,ipBytes,4);
ss_family = AF_INET;
reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr = ipb[0];
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
} else if (ipLen == 16) {
ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes,16);
reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
}
}
-std::string InetAddress::toString() const
+char *InetAddress::toString(char buf[64]) const
{
- char buf[128];
- switch(ss_family) {
- case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d/%d",
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[3],
- (int)Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port))
- );
- return std::string(buf);
- case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d",
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[3]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[4]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[5]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[6]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[7]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[8]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[9]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[10]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[11]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[12]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[13]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[14]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[15]),
- (int)Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port))
- );
- return std::string(buf);
+ char *p = toIpString(buf);
+ if (*p) {
+ while (*p) ++p;
+ *(p++) = '/';
+ Utils::decimal(port(),p);
}
- return std::string();
+ return buf;
}
-std::string InetAddress::toIpString() const
+char *InetAddress::toIpString(char buf[64]) const
{
- char buf[128];
switch(ss_family) {
- case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d",
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
- (int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[3]
- );
- return std::string(buf);
- case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[3]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[4]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[5]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[6]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[7]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[8]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[9]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[10]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[11]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[12]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[13]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[14]),
- (int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[15])
- );
- return std::string(buf);
+ case AF_INET: {
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr));
+ char *p = buf;
+ for(int i=0;;++i) {
+ Utils::decimal((unsigned long)a[i],p);
+ if (i != 3) {
+ while (*p) ++p;
+ *(p++) = '.';
+ } else break;
+ }
+ } break;
+
+ case AF_INET6: {
+ uint16_t a[8];
+ ZT_FAST_MEMCPY(a,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ char *p = buf;
+ for(int i=0;i<8;++i) {
+ Utils::hex(Utils::ntoh(a[i]),p);
+ p[4] = (i == 7) ? (char)0 : ':';
+ p += 5;
+ }
+ } break;
+
+ default:
+ buf[0] = (char)0;
+ break;
}
- return std::string();
+ return buf;
}
-void InetAddress::fromString(const std::string &ipSlashPort)
+bool InetAddress::fromString(const char *ipSlashPort)
{
- const std::size_t slashAt = ipSlashPort.find('/');
- if (slashAt == std::string::npos) {
- set(ipSlashPort,0);
+ char buf[64];
+
+ memset(this,0,sizeof(InetAddress));
+
+ if (!*ipSlashPort)
+ return true;
+ if (!Utils::scopy(buf,sizeof(buf),ipSlashPort))
+ return false;
+
+ char *portAt = buf;
+ while ((*portAt)&&(*portAt != '/'))
+ ++portAt;
+ unsigned int port = 0;
+ if (*portAt) {
+ *(portAt++) = (char)0;
+ port = Utils::strToUInt(portAt) & 0xffff;
+ }
+
+ if (strchr(buf,':')) {
+ uint16_t a[8];
+ unsigned int b = 0;
+ char *saveptr = (char *)0;
+ for(char *s=Utils::stok(buf,":",&saveptr);((s)&&(b<8));s=Utils::stok((char *)0,":",&saveptr))
+ a[b++] = Utils::hton((uint16_t)(Utils::hexStrToUInt(s) & 0xffff));
+
+ struct sockaddr_in6 *const in6 = reinterpret_cast<struct sockaddr_in6 *>(this);
+ in6->sin6_family = AF_INET6;
+ ZT_FAST_MEMCPY(in6->sin6_addr.s6_addr,a,16);
+ in6->sin6_port = Utils::hton((uint16_t)port);
+
+ return true;
+ } else if (strchr(buf,'.')) {
+ uint8_t a[4];
+ unsigned int b = 0;
+ char *saveptr = (char *)0;
+ for(char *s=Utils::stok(buf,".",&saveptr);((s)&&(b<4));s=Utils::stok((char *)0,".",&saveptr))
+ a[b++] = (uint8_t)(Utils::strToUInt(s) & 0xff);
+
+ struct sockaddr_in *const in = reinterpret_cast<struct sockaddr_in *>(this);
+ in->sin_family = AF_INET;
+ ZT_FAST_MEMCPY(&(in->sin_addr.s_addr),a,4);
+ in->sin_port = Utils::hton((uint16_t)port);
+
+ return true;
} else {
- long p = strtol(ipSlashPort.substr(slashAt+1).c_str(),(char **)0,10);
- if ((p > 0)&&(p <= 0xffff))
- set(ipSlashPort.substr(0,slashAt),(unsigned int)p);
- else set(ipSlashPort.substr(0,slashAt),0);
+ return false;
}
}
@@ -236,9 +231,14 @@ InetAddress InetAddress::netmask() const
case AF_INET6: {
uint64_t nm[2];
const unsigned int bits = netmaskBits();
- nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
- nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
+ if(bits) {
+ nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
+ nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
+ } else {
+ nm[0] = 0;
+ nm[1] = 0;
+ }
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
@@ -264,15 +264,37 @@ InetAddress InetAddress::network() const
case AF_INET6: {
uint64_t nm[2];
const unsigned int bits = netmaskBits();
- memcpy(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+ ZT_FAST_MEMCPY(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
}
+bool InetAddress::isEqualPrefix(const InetAddress &addr) const
+{
+ if (addr.ss_family == ss_family) {
+ switch(ss_family) {
+ case AF_INET6: {
+ const InetAddress mask(netmask());
+ InetAddress addr_mask(addr.netmask());
+ const uint8_t *n = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr_mask)->sin6_addr.s6_addr);
+ const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&mask)->sin6_addr.s6_addr);
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr);
+ const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(unsigned int i=0;i<16;++i) {
+ if ((a[i] & m[i]) != (b[i] & n[i]))
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool InetAddress::containsAddress(const InetAddress &addr) const
{
if (addr.ss_family == ss_family) {
@@ -300,7 +322,6 @@ bool InetAddress::containsAddress(const InetAddress &addr) const
}
bool InetAddress::isNetwork() const
- throw()
{
switch(ss_family) {
case AF_INET: {
@@ -333,7 +354,6 @@ bool InetAddress::isNetwork() const
}
bool InetAddress::operator==(const InetAddress &a) const
- throw()
{
if (ss_family == a.ss_family) {
switch(ss_family) {
@@ -357,7 +377,6 @@ bool InetAddress::operator==(const InetAddress &a) const
}
bool InetAddress::operator<(const InetAddress &a) const
- throw()
{
if (ss_family < a.ss_family)
return true;
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index e03deb71..f69b3cc2 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_INETADDRESS_HPP
@@ -23,8 +31,6 @@
#include <string.h>
#include <stdint.h>
-#include <string>
-
#include "Constants.hpp"
#include "../include/ZeroTierOne.h"
#include "Utils.hpp"
@@ -73,110 +79,106 @@ struct InetAddress : public sockaddr_storage
IP_SCOPE_PSEUDOPRIVATE = 3, // 28.x.x.x, etc. -- unofficially unrouted IPv4 blocks often "bogarted"
IP_SCOPE_GLOBAL = 4, // globally routable IP address (all others)
IP_SCOPE_LINK_LOCAL = 5, // 169.254.x.x, IPv6 LL
- IP_SCOPE_SHARED = 6, // 100.64.0.0/10, shared space for e.g. carrier-grade NAT
+ IP_SCOPE_SHARED = 6, // currently unused, formerly used for carrier-grade NAT ranges
IP_SCOPE_PRIVATE = 7 // 10.x.x.x, 192.168.x.x, etc.
};
- InetAddress() throw() { memset(this,0,sizeof(InetAddress)); }
- InetAddress(const InetAddress &a) throw() { memcpy(this,&a,sizeof(InetAddress)); }
- InetAddress(const InetAddress *a) throw() { memcpy(this,a,sizeof(InetAddress)); }
- InetAddress(const struct sockaddr_storage &ss) throw() { *this = ss; }
- InetAddress(const struct sockaddr_storage *ss) throw() { *this = ss; }
- InetAddress(const struct sockaddr &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr *sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in *sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in6 &sa) throw() { *this = sa; }
- InetAddress(const struct sockaddr_in6 *sa) throw() { *this = sa; }
- InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) throw() { this->set(ipBytes,ipLen,port); }
- InetAddress(const uint32_t ipv4,unsigned int port) throw() { this->set(&ipv4,4,port); }
- InetAddress(const std::string &ip,unsigned int port) throw() { this->set(ip,port); }
- InetAddress(const std::string &ipSlashPort) throw() { this->fromString(ipSlashPort); }
- InetAddress(const char *ipSlashPort) throw() { this->fromString(std::string(ipSlashPort)); }
+ // Can be used with the unordered maps and sets in c++11. We don't use C++11 in the core
+ // but this is safe to put here.
+ struct Hasher
+ {
+ inline std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); }
+ };
+
+ InetAddress() { memset(this,0,sizeof(InetAddress)); }
+ InetAddress(const InetAddress &a) { ZT_FAST_MEMCPY(this,&a,sizeof(InetAddress)); }
+ InetAddress(const InetAddress *a) { ZT_FAST_MEMCPY(this,a,sizeof(InetAddress)); }
+ InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
+ InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
+ InetAddress(const struct sockaddr &sa) { *this = sa; }
+ InetAddress(const struct sockaddr *sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in &sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in *sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
+ InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
+ InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); }
+ InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
+ InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
inline InetAddress &operator=(const InetAddress &a)
- throw()
{
if (&a != this)
- memcpy(this,&a,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,&a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const InetAddress *a)
- throw()
{
if (a != this)
- memcpy(this,a,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage &ss)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&ss) != this)
- memcpy(this,&ss,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,&ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage *ss)
- throw()
{
if (reinterpret_cast<const InetAddress *>(ss) != this)
- memcpy(this,ss,sizeof(InetAddress));
+ ZT_FAST_MEMCPY(this,ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr &sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa.sa_family) {
case AF_INET:
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,&sa,sizeof(struct sockaddr_in6));
break;
}
}
@@ -184,16 +186,15 @@ struct InetAddress : public sockaddr_storage
}
inline InetAddress &operator=(const struct sockaddr *sa)
- throw()
{
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa->sa_family) {
case AF_INET:
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ ZT_FAST_MEMCPY(this,sa,sizeof(struct sockaddr_in6));
break;
}
}
@@ -203,17 +204,7 @@ struct InetAddress : public sockaddr_storage
/**
* @return IP scope classification (e.g. loopback, link-local, private, global)
*/
- IpScope ipScope() const
- throw();
-
- /**
- * Set from a string-format IP and a port
- *
- * @param ip IP address in V4 or V6 ASCII notation
- * @param port Port or 0 for none
- */
- void set(const std::string &ip,unsigned int port)
- throw();
+ IpScope ipScope() const;
/**
* Set from a raw IP and port number
@@ -222,8 +213,7 @@ struct InetAddress : public sockaddr_storage
* @param ipLen Length of IP address: 4 or 16
* @param port Port number or 0 for none
*/
- void set(const void *ipBytes,unsigned int ipLen,unsigned int port)
- throw();
+ void set(const void *ipBytes,unsigned int ipLen,unsigned int port);
/**
* Set the port component
@@ -264,23 +254,23 @@ struct InetAddress : public sockaddr_storage
/**
* @return ASCII IP/port format representation
*/
- std::string toString() const;
+ char *toString(char buf[64]) const;
/**
* @return IP portion only, in ASCII string format
*/
- std::string toIpString() const;
+ char *toIpString(char buf[64]) const;
/**
- * @param ipSlashPort ASCII IP/port format notation
+ * @param ipSlashPort IP/port (port is optional, will be 0 if not included)
+ * @return True if address appeared to be valid
*/
- void fromString(const std::string &ipSlashPort);
+ bool fromString(const char *ipSlashPort);
/**
* @return Port or 0 if no port component defined
*/
inline unsigned int port() const
- throw()
{
switch(ss_family) {
case AF_INET: return Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(this)->sin_port));
@@ -298,7 +288,20 @@ struct InetAddress : public sockaddr_storage
*
* @return Netmask bits
*/
- inline unsigned int netmaskBits() const throw() { return port(); }
+ inline unsigned int netmaskBits() const { return port(); }
+
+ /**
+ * @return True if netmask bits is valid for the address type
+ */
+ inline bool netmaskBitsValid() const
+ {
+ const unsigned int n = port();
+ switch(ss_family) {
+ case AF_INET: return (n <= 32);
+ case AF_INET6: return (n <= 128);
+ }
+ return false;
+ }
/**
* Alias for port()
@@ -308,7 +311,7 @@ struct InetAddress : public sockaddr_storage
*
* @return Gateway metric
*/
- inline unsigned int metric() const throw() { return port(); }
+ inline unsigned int metric() const { return port(); }
/**
* Construct a full netmask as an InetAddress
@@ -335,6 +338,14 @@ struct InetAddress : public sockaddr_storage
InetAddress network() const;
/**
+ * Test whether this IPv6 prefix matches the prefix of a given IPv6 address
+ *
+ * @param addr Address to check
+ * @return True if this IPv6 prefix matches the prefix of a given IPv6 address
+ */
+ bool isEqualPrefix(const InetAddress &addr) const;
+
+ /**
* Test whether this IP/netmask contains this address
*
* @param addr Address to check
@@ -345,18 +356,17 @@ struct InetAddress : public sockaddr_storage
/**
* @return True if this is an IPv4 address
*/
- inline bool isV4() const throw() { return (ss_family == AF_INET); }
+ inline bool isV4() const { return (ss_family == AF_INET); }
/**
* @return True if this is an IPv6 address
*/
- inline bool isV6() const throw() { return (ss_family == AF_INET6); }
+ inline bool isV6() const { return (ss_family == AF_INET6); }
/**
* @return pointer to raw address bytes or NULL if not available
*/
inline const void *rawIpData() const
- throw()
{
switch(ss_family) {
case AF_INET: return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
@@ -366,6 +376,25 @@ struct InetAddress : public sockaddr_storage
}
/**
+ * @return InetAddress containing only the IP portion of this address and a zero port, or NULL if not IPv4 or IPv6
+ */
+ inline InetAddress ipOnly() const
+ {
+ InetAddress r;
+ switch(ss_family) {
+ case AF_INET:
+ r.ss_family = AF_INET;
+ reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr;
+ break;
+ case AF_INET6:
+ r.ss_family = AF_INET6;
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ break;
+ }
+ return r;
+ }
+
+ /**
* Performs an IP-only comparison or, if that is impossible, a memcmp()
*
* @param a InetAddress to compare again
@@ -384,9 +413,48 @@ struct InetAddress : public sockaddr_storage
}
/**
+ * Performs an IP-only comparison or, if that is impossible, a memcmp()
+ *
+ * This version compares only the first 64 bits of IPv6 addresses.
+ *
+ * @param a InetAddress to compare again
+ * @return True if only IP portions are equal (false for non-IP or null addresses)
+ */
+ inline bool ipsEqual2(const InetAddress &a) const
+ {
+ if (ss_family == a.ss_family) {
+ if (ss_family == AF_INET)
+ return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
+ if (ss_family == AF_INET6)
+ return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,8) == 0);
+ return (memcmp(this,&a,sizeof(InetAddress)) == 0);
+ }
+ return false;
+ }
+
+ inline unsigned long hashCode() const
+ {
+ if (ss_family == AF_INET) {
+ return ((unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr + (unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_port);
+ } else if (ss_family == AF_INET6) {
+ unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(long i=0;i<16;++i)
+ reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
+ return tmp;
+ } else {
+ unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(this);
+ for(long i=0;i<(long)sizeof(InetAddress);++i)
+ reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
+ return tmp;
+ }
+ }
+
+ /**
* Set to null/zero
*/
- inline void zero() throw() { memset(this,0,sizeof(InetAddress)); }
+ inline void zero() { memset(this,0,sizeof(InetAddress)); }
/**
* Check whether this is a network/route rather than an IP assignment
@@ -396,13 +464,36 @@ struct InetAddress : public sockaddr_storage
*
* @return True if everything after netmask bits is zero
*/
- bool isNetwork() const
- throw();
+ bool isNetwork() const;
+
+ /**
+ * @return 14-bit (0-16383) hash of this IP's first 24 or 48 bits (for V4 or V6) for rate limiting code, or 0 if non-IP
+ */
+ inline unsigned long rateGateHash() const
+ {
+ unsigned long h = 0;
+ switch(ss_family) {
+ case AF_INET:
+ h = (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) & 0xffffff00) >> 8;
+ h ^= (h >> 14);
+ break;
+ case AF_INET6: {
+ const uint8_t *ip = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ h = ((unsigned long)ip[0]); h <<= 1;
+ h += ((unsigned long)ip[1]); h <<= 1;
+ h += ((unsigned long)ip[2]); h <<= 1;
+ h += ((unsigned long)ip[3]); h <<= 1;
+ h += ((unsigned long)ip[4]); h <<= 1;
+ h += ((unsigned long)ip[5]);
+ } break;
+ }
+ return (h & 0x3fff);
+ }
/**
* @return True if address family is non-zero
*/
- inline operator bool() const throw() { return (ss_family != 0); }
+ inline operator bool() const { return (ss_family != 0); }
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
@@ -446,26 +537,26 @@ struct InetAddress : public sockaddr_storage
return (unsigned int)(b.template at<uint16_t>(p) + 3); // other addresses begin with 16-bit non-inclusive length
case 0x04:
ss_family = AF_INET;
- memcpy(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
+ ZT_FAST_MEMCPY(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
break;
case 0x06:
ss_family = AF_INET6;
- memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
+ ZT_FAST_MEMCPY(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
break;
default:
- throw std::invalid_argument("invalid serialized InetAddress");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING;
}
return (p - startAt);
}
- bool operator==(const InetAddress &a) const throw();
- bool operator<(const InetAddress &a) const throw();
- inline bool operator!=(const InetAddress &a) const throw() { return !(*this == a); }
- inline bool operator>(const InetAddress &a) const throw() { return (a < *this); }
- inline bool operator<=(const InetAddress &a) const throw() { return !(a < *this); }
- inline bool operator>=(const InetAddress &a) const throw() { return !(*this < a); }
+ bool operator==(const InetAddress &a) const;
+ bool operator<(const InetAddress &a) const;
+ inline bool operator!=(const InetAddress &a) const { return !(*this == a); }
+ inline bool operator>(const InetAddress &a) const { return (a < *this); }
+ inline bool operator<=(const InetAddress &a) const { return !(a < *this); }
+ inline bool operator>=(const InetAddress &a) const { return !(*this < a); }
/**
* @param mac MAC address seed
diff --git a/node/MAC.hpp b/node/MAC.hpp
index 95623f12..c08323a4 100644
--- a/node/MAC.hpp
+++ b/node/MAC.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MAC_HPP
@@ -36,30 +44,24 @@ namespace ZeroTier {
class MAC
{
public:
- MAC() throw() : _m(0ULL) {}
- MAC(const MAC &m) throw() : _m(m._m) {}
+ MAC() : _m(0ULL) {}
+ MAC(const MAC &m) : _m(m._m) {}
- MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) throw() :
+ MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) :
_m( ((((uint64_t)a) & 0xffULL) << 40) |
((((uint64_t)b) & 0xffULL) << 32) |
((((uint64_t)c) & 0xffULL) << 24) |
((((uint64_t)d) & 0xffULL) << 16) |
((((uint64_t)e) & 0xffULL) << 8) |
(((uint64_t)f) & 0xffULL) ) {}
-
- MAC(const char *s) throw() { fromString(s); }
- MAC(const std::string &s) throw() { fromString(s.c_str()); }
-
- MAC(const void *bits,unsigned int len) throw() { setTo(bits,len); }
-
- MAC(const Address &ztaddr,uint64_t nwid) throw() { fromAddress(ztaddr,nwid); }
-
- MAC(const uint64_t m) throw() : _m(m & 0xffffffffffffULL) {}
+ MAC(const void *bits,unsigned int len) { setTo(bits,len); }
+ MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); }
+ MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {}
/**
* @return MAC in 64-bit integer
*/
- inline uint64_t toInt() const throw() { return _m; }
+ inline uint64_t toInt() const { return _m; }
/**
* Set MAC to zero
@@ -69,14 +71,13 @@ public:
/**
* @return True if MAC is non-zero
*/
- inline operator bool() const throw() { return (_m != 0ULL); }
+ inline operator bool() const { return (_m != 0ULL); }
/**
* @param bits Raw MAC in big-endian byte order
* @param len Length, must be >= 6 or result is zero
*/
inline void setTo(const void *bits,unsigned int len)
- throw()
{
if (len < 6) {
_m = 0ULL;
@@ -96,7 +97,6 @@ public:
* @param len Length of buffer, must be >= 6 or nothing is copied
*/
inline void copyTo(void *buf,unsigned int len) const
- throw()
{
if (len < 6)
return;
@@ -116,7 +116,6 @@ public:
*/
template<unsigned int C>
inline void appendTo(Buffer<C> &b) const
- throw(std::out_of_range)
{
unsigned char *p = (unsigned char *)b.appendField(6);
*(p++) = (unsigned char)((_m >> 40) & 0xff);
@@ -130,48 +129,17 @@ public:
/**
* @return True if this is broadcast (all 0xff)
*/
- inline bool isBroadcast() const throw() { return (_m == 0xffffffffffffULL); }
+ inline bool isBroadcast() const { return (_m == 0xffffffffffffULL); }
/**
* @return True if this is a multicast MAC
*/
- inline bool isMulticast() const throw() { return ((_m & 0x010000000000ULL) != 0ULL); }
+ inline bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); }
/**
* @param True if this is a locally-administered MAC
*/
- inline bool isLocallyAdministered() const throw() { return ((_m & 0x020000000000ULL) != 0ULL); }
-
- /**
- * @param s Hex MAC, with or without : delimiters
- */
- inline void fromString(const char *s)
- {
- char tmp[8];
- for(int i=0;i<6;++i)
- tmp[i] = (char)0;
- Utils::unhex(s,tmp,6);
- setTo(tmp,6);
- }
-
- /**
- * @return MAC address in standard :-delimited hex format
- */
- inline std::string toString() const
- {
- char tmp[24];
- toString(tmp,sizeof(tmp));
- return std::string(tmp);
- }
-
- /**
- * @param buf Buffer to contain human-readable MAC
- * @param len Length of buffer
- */
- inline void toString(char *buf,unsigned int len) const
- {
- Utils::snprintf(buf,len,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)(*this)[0],(int)(*this)[1],(int)(*this)[2],(int)(*this)[3],(int)(*this)[4],(int)(*this)[5]);
- }
+ inline bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); }
/**
* Set this MAC to a MAC derived from an address and a network ID
@@ -180,7 +148,6 @@ public:
* @param nwid 64-bit network ID
*/
inline void fromAddress(const Address &ztaddr,uint64_t nwid)
- throw()
{
uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40;
m |= ztaddr.toInt(); // a is 40 bits
@@ -200,7 +167,6 @@ public:
* @param nwid Network ID
*/
inline Address toAddress(uint64_t nwid) const
- throw()
{
uint64_t a = _m & 0xffffffffffULL; // least significant 40 bits of MAC are formed from address
a ^= ((nwid >> 8) & 0xff) << 32; // ... XORed with bits 8-48 of the nwid in little-endian byte order, so unmask it
@@ -216,7 +182,6 @@ public:
* @return First octet of MAC for this network
*/
static inline unsigned char firstOctetForNetwork(uint64_t nwid)
- throw()
{
unsigned char a = ((unsigned char)(nwid & 0xfe) | 0x02); // locally administered, not multicast, from LSB of network ID
return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux
@@ -226,34 +191,55 @@ public:
* @param i Value from 0 to 5 (inclusive)
* @return Byte at said position (address interpreted in big-endian order)
*/
- inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
+ inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); }
/**
* @return 6, which is the number of bytes in a MAC, for container compliance
*/
- inline unsigned int size() const throw() { return 6; }
+ inline unsigned int size() const { return 6; }
- inline unsigned long hashCode() const throw() { return (unsigned long)_m; }
+ inline unsigned long hashCode() const { return (unsigned long)_m; }
+
+ inline char *toString(char buf[18]) const
+ {
+ buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf];
+ buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf];
+ buf[2] = ':';
+ buf[3] = Utils::HEXCHARS[(_m >> 36) & 0xf];
+ buf[4] = Utils::HEXCHARS[(_m >> 32) & 0xf];
+ buf[5] = ':';
+ buf[6] = Utils::HEXCHARS[(_m >> 28) & 0xf];
+ buf[7] = Utils::HEXCHARS[(_m >> 24) & 0xf];
+ buf[8] = ':';
+ buf[9] = Utils::HEXCHARS[(_m >> 20) & 0xf];
+ buf[10] = Utils::HEXCHARS[(_m >> 16) & 0xf];
+ buf[11] = ':';
+ buf[12] = Utils::HEXCHARS[(_m >> 12) & 0xf];
+ buf[13] = Utils::HEXCHARS[(_m >> 8) & 0xf];
+ buf[14] = ':';
+ buf[15] = Utils::HEXCHARS[(_m >> 4) & 0xf];
+ buf[16] = Utils::HEXCHARS[_m & 0xf];
+ buf[17] = (char)0;
+ return buf;
+ }
inline MAC &operator=(const MAC &m)
- throw()
{
_m = m._m;
return *this;
}
inline MAC &operator=(const uint64_t m)
- throw()
{
_m = m;
return *this;
}
- inline bool operator==(const MAC &m) const throw() { return (_m == m._m); }
- inline bool operator!=(const MAC &m) const throw() { return (_m != m._m); }
- inline bool operator<(const MAC &m) const throw() { return (_m < m._m); }
- inline bool operator<=(const MAC &m) const throw() { return (_m <= m._m); }
- inline bool operator>(const MAC &m) const throw() { return (_m > m._m); }
- inline bool operator>=(const MAC &m) const throw() { return (_m >= m._m); }
+ inline bool operator==(const MAC &m) const { return (_m == m._m); }
+ inline bool operator!=(const MAC &m) const { return (_m != m._m); }
+ inline bool operator<(const MAC &m) const { return (_m < m._m); }
+ inline bool operator<=(const MAC &m) const { return (_m <= m._m); }
+ inline bool operator>(const MAC &m) const { return (_m > m._m); }
+ inline bool operator>=(const MAC &m) const { return (_m >= m._m); }
private:
uint64_t _m;
diff --git a/node/Membership.cpp b/node/Membership.cpp
new file mode 100644
index 00000000..affe7a71
--- /dev/null
+++ b/node/Membership.cpp
@@ -0,0 +1,237 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include <algorithm>
+
+#include "Membership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Peer.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Packet.hpp"
+#include "Node.hpp"
+#include "Trace.hpp"
+
+#define ZT_CREDENTIAL_PUSH_EVERY (ZT_NETWORK_AUTOCONF_DELAY / 3)
+
+namespace ZeroTier {
+
+Membership::Membership() :
+ _lastUpdatedMulticast(0),
+ _lastPushedCom(0),
+ _comRevocationThreshold(0),
+ _revocations(4),
+ _remoteTags(4),
+ _remoteCaps(4),
+ _remoteCoos(4)
+{
+ resetPushState();
+}
+
+void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
+{
+ bool sendCom = ( (nconf.com) && ( ((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) );
+
+ const Capability *sendCap;
+ if (localCapabilityIndex >= 0) {
+ sendCap = &(nconf.capabilities[localCapabilityIndex]);
+ if ( ((now - _localCredLastPushed.cap[localCapabilityIndex]) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) )
+ _localCredLastPushed.cap[localCapabilityIndex] = now;
+ else sendCap = (const Capability *)0;
+ } else sendCap = (const Capability *)0;
+
+ const Tag *sendTags[ZT_MAX_NETWORK_TAGS];
+ unsigned int sendTagCount = 0;
+ for(unsigned int t=0;t<nconf.tagCount;++t) {
+ if ( ((now - _localCredLastPushed.tag[t]) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
+ _localCredLastPushed.tag[t] = now;
+ sendTags[sendTagCount++] = &(nconf.tags[t]);
+ }
+ }
+
+ const CertificateOfOwnership *sendCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
+ unsigned int sendCooCount = 0;
+ for(unsigned int c=0;c<nconf.certificateOfOwnershipCount;++c) {
+ if ( ((now - _localCredLastPushed.coo[c]) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
+ _localCredLastPushed.coo[c] = now;
+ sendCoos[sendCooCount++] = &(nconf.certificatesOfOwnership[c]);
+ }
+ }
+
+ unsigned int tagPtr = 0;
+ unsigned int cooPtr = 0;
+ while ((tagPtr < sendTagCount)||(cooPtr < sendCooCount)||(sendCom)||(sendCap)) {
+ Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+
+ if (sendCom) {
+ sendCom = false;
+ nconf.com.serialize(outp);
+ _lastPushedCom = now;
+ }
+ outp.append((uint8_t)0x00);
+
+ if (sendCap) {
+ outp.append((uint16_t)1);
+ sendCap->serialize(outp);
+ sendCap = (const Capability *)0;
+ } else outp.append((uint16_t)0);
+
+ const unsigned int tagCountAt = outp.size();
+ outp.addSize(2);
+ unsigned int thisPacketTagCount = 0;
+ while ((tagPtr < sendTagCount)&&((outp.size() + sizeof(Tag) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+ sendTags[tagPtr++]->serialize(outp);
+ ++thisPacketTagCount;
+ }
+ outp.setAt(tagCountAt,(uint16_t)thisPacketTagCount);
+
+ // No revocations, these propagate differently
+ outp.append((uint16_t)0);
+
+ const unsigned int cooCountAt = outp.size();
+ outp.addSize(2);
+ unsigned int thisPacketCooCount = 0;
+ while ((cooPtr < sendCooCount)&&((outp.size() + sizeof(CertificateOfOwnership) + 16) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+ sendCoos[cooPtr++]->serialize(outp);
+ ++thisPacketCooCount;
+ }
+ outp.setAt(cooCountAt,(uint16_t)thisPacketCooCount);
+
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+}
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
+{
+ const int64_t newts = com.timestamp();
+ if (newts <= _comRevocationThreshold) {
+ RR->t->credentialRejected(tPtr,com,"revoked");
+ return ADD_REJECTED;
+ }
+
+ const int64_t oldts = _com.timestamp();
+ if (newts < oldts) {
+ RR->t->credentialRejected(tPtr,com,"old");
+ return ADD_REJECTED;
+ }
+ if ((newts == oldts)&&(_com == com))
+ return ADD_ACCEPTED_REDUNDANT;
+
+ switch(com.verify(RR,tPtr)) {
+ default:
+ RR->t->credentialRejected(tPtr,com,"invalid");
+ return ADD_REJECTED;
+ case 0:
+ _com = com;
+ return ADD_ACCEPTED_NEW;
+ case 1:
+ return ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+// Template out addCredential() for many cred types to avoid copypasta
+template<typename C>
+static Membership::AddCredentialResult _addCredImpl(Hashtable<uint32_t,C> &remoteCreds,const Hashtable<uint64_t,int64_t> &revocations,const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const C &cred)
+{
+ C *rc = remoteCreds.get(cred.id());
+ if (rc) {
+ if (rc->timestamp() > cred.timestamp()) {
+ RR->t->credentialRejected(tPtr,cred,"old");
+ return Membership::ADD_REJECTED;
+ }
+ if (*rc == cred)
+ return Membership::ADD_ACCEPTED_REDUNDANT;
+ }
+
+ const int64_t *const rt = revocations.get(Membership::credentialKey(C::credentialType(),cred.id()));
+ if ((rt)&&(*rt >= cred.timestamp())) {
+ RR->t->credentialRejected(tPtr,cred,"revoked");
+ return Membership::ADD_REJECTED;
+ }
+
+ switch(cred.verify(RR,tPtr)) {
+ default:
+ RR->t->credentialRejected(tPtr,cred,"invalid");
+ return Membership::ADD_REJECTED;
+ case 0:
+ if (!rc)
+ rc = &(remoteCreds[cred.id()]);
+ *rc = cred;
+ return Membership::ADD_ACCEPTED_NEW;
+ case 1:
+ return Membership::ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag) { return _addCredImpl<Tag>(_remoteTags,_revocations,RR,tPtr,nconf,tag); }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap) { return _addCredImpl<Capability>(_remoteCaps,_revocations,RR,tPtr,nconf,cap); }
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo) { return _addCredImpl<CertificateOfOwnership>(_remoteCoos,_revocations,RR,tPtr,nconf,coo); }
+
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
+{
+ int64_t *rt;
+ switch(rev.verify(RR,tPtr)) {
+ default:
+ RR->t->credentialRejected(tPtr,rev,"invalid");
+ return ADD_REJECTED;
+ case 0: {
+ const Credential::Type ct = rev.type();
+ switch(ct) {
+ case Credential::CREDENTIAL_TYPE_COM:
+ if (rev.threshold() > _comRevocationThreshold) {
+ _comRevocationThreshold = rev.threshold();
+ return ADD_ACCEPTED_NEW;
+ }
+ return ADD_ACCEPTED_REDUNDANT;
+ case Credential::CREDENTIAL_TYPE_CAPABILITY:
+ case Credential::CREDENTIAL_TYPE_TAG:
+ case Credential::CREDENTIAL_TYPE_COO:
+ rt = &(_revocations[credentialKey(ct,rev.credentialId())]);
+ if (*rt < rev.threshold()) {
+ *rt = rev.threshold();
+ _comRevocationThreshold = rev.threshold();
+ return ADD_ACCEPTED_NEW;
+ }
+ return ADD_ACCEPTED_REDUNDANT;
+ default:
+ RR->t->credentialRejected(tPtr,rev,"invalid");
+ return ADD_REJECTED;
+ }
+ }
+ case 1:
+ return ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+void Membership::clean(const int64_t now,const NetworkConfig &nconf)
+{
+ _cleanCredImpl<Tag>(nconf,_remoteTags);
+ _cleanCredImpl<Capability>(nconf,_remoteCaps);
+ _cleanCredImpl<CertificateOfOwnership>(nconf,_remoteCoos);
+}
+
+} // namespace ZeroTier
diff --git a/node/Membership.hpp b/node/Membership.hpp
new file mode 100644
index 00000000..ad0bb73e
--- /dev/null
+++ b/node/Membership.hpp
@@ -0,0 +1,285 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_MEMBERSHIP_HPP
+#define ZT_MEMBERSHIP_HPP
+
+#include <stdint.h>
+
+#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
+#include "Credential.hpp"
+#include "Hashtable.hpp"
+#include "CertificateOfMembership.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
+#include "Revocation.hpp"
+#include "NetworkConfig.hpp"
+
+#define ZT_MEMBERSHIP_CRED_ID_UNUSED 0xffffffffffffffffULL
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class Network;
+
+/**
+ * A container for certificates of membership and other network credentials
+ *
+ * This is essentially a relational join between Peer and Network.
+ *
+ * This class is not thread safe. It must be locked externally.
+ */
+class Membership
+{
+public:
+ enum AddCredentialResult
+ {
+ ADD_REJECTED,
+ ADD_ACCEPTED_NEW,
+ ADD_ACCEPTED_REDUNDANT,
+ ADD_DEFERRED_FOR_WHOIS
+ };
+
+ Membership();
+
+ /**
+ * Send COM and other credentials to this peer if needed
+ *
+ * This checks last pushed times for our COM and for other credentials and
+ * sends VERB_NETWORK_CREDENTIALS if the recipient might need them.
+ *
+ * @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
+ * @param peerAddress Address of member peer (the one that this Membership describes)
+ * @param nconf My network config
+ * @param localCapabilityIndex Index of local capability to include (in nconf.capabilities[]) or -1 if none
+ * @param force If true, send objects regardless of last push time
+ */
+ void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const int64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
+
+ /**
+ * Check whether we should push MULTICAST_LIKEs to this peer, and update last sent time if true
+ *
+ * @param now Current time
+ * @return True if we should update multicasts
+ */
+ inline bool multicastLikeGate(const int64_t now)
+ {
+ if ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD) {
+ _lastUpdatedMulticast = now;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check whether the peer represented by this Membership should be allowed on this network at all
+ *
+ * @param nconf Our network config
+ * @return True if this peer is allowed on this network at all
+ */
+ inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const
+ {
+ if (nconf.isPublic()) return true;
+ if (_com.timestamp() <= _comRevocationThreshold) return false;
+ return nconf.com.agreesWith(_com);
+ }
+
+ inline bool recentlyAssociated(const int64_t now) const
+ {
+ return ((_com)&&((now - _com.timestamp()) < ZT_PEER_ACTIVITY_TIMEOUT));
+ }
+
+ /**
+ * Check whether the peer represented by this Membership owns a given resource
+ *
+ * @tparam Type of resource: InetAddress or MAC
+ * @param nconf Our network config
+ * @param r Resource to check
+ * @return True if this peer has a certificate of ownership for the given resource
+ */
+ template<typename T>
+ inline bool hasCertificateOfOwnershipFor(const NetworkConfig &nconf,const T &r) const
+ {
+ uint32_t *k = (uint32_t *)0;
+ CertificateOfOwnership *v = (CertificateOfOwnership *)0;
+ Hashtable< uint32_t,CertificateOfOwnership >::Iterator i(*(const_cast< Hashtable< uint32_t,CertificateOfOwnership> *>(&_remoteCoos)));
+ while (i.next(k,v)) {
+ if (_isCredentialTimestampValid(nconf,*v)&&(v->owns(r)))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get a remote member's tag (if we have it)
+ *
+ * @param nconf Network configuration
+ * @param id Tag ID
+ * @return Pointer to tag or NULL if not found
+ */
+ inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const
+ {
+ const Tag *const t = _remoteTags.get(id);
+ return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0);
+ }
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
+
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev);
+
+ /**
+ * Clean internal databases of stale entries
+ *
+ * @param now Current time
+ * @param nconf Current network configuration
+ */
+ void clean(const int64_t now,const NetworkConfig &nconf);
+
+ /**
+ * Reset last pushed time for local credentials
+ *
+ * This is done when we update our network configuration and our credentials have changed
+ */
+ inline void resetPushState()
+ {
+ _lastPushedCom = 0;
+ memset(&_localCredLastPushed,0,sizeof(_localCredLastPushed));
+ }
+
+ /**
+ * Generates a key for the internal use in indexing credentials by type and credential ID
+ */
+ static uint64_t credentialKey(const Credential::Type &t,const uint32_t i) { return (((uint64_t)t << 32) | (uint64_t)i); }
+
+private:
+ template<typename C>
+ inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const
+ {
+ const int64_t ts = remoteCredential.timestamp();
+ if (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) {
+ const int64_t *threshold = _revocations.get(credentialKey(C::credentialType(),remoteCredential.id()));
+ return ((!threshold)||(ts > *threshold));
+ }
+ return false;
+ }
+
+ template<typename C>
+ void _cleanCredImpl(const NetworkConfig &nconf,Hashtable<uint32_t,C> &remoteCreds)
+ {
+ uint32_t *k = (uint32_t *)0;
+ C *v = (C *)0;
+ typename Hashtable<uint32_t,C>::Iterator i(remoteCreds);
+ while (i.next(k,v)) {
+ if (!_isCredentialTimestampValid(nconf,*v))
+ remoteCreds.erase(*k);
+ }
+ }
+
+ // Last time we pushed MULTICAST_LIKE(s)
+ int64_t _lastUpdatedMulticast;
+
+ // Last time we pushed our COM to this peer
+ int64_t _lastPushedCom;
+
+ // Revocation threshold for COM or 0 if none
+ int64_t _comRevocationThreshold;
+
+ // Remote member's latest network COM
+ CertificateOfMembership _com;
+
+ // Revocations by credentialKey()
+ Hashtable< uint64_t,int64_t > _revocations;
+
+ // Remote credentials that we have received from this member (and that are valid)
+ Hashtable< uint32_t,Tag > _remoteTags;
+ Hashtable< uint32_t,Capability > _remoteCaps;
+ Hashtable< uint32_t,CertificateOfOwnership > _remoteCoos;
+
+ // Time we last pushed our local credentials to this member
+ struct {
+ uint64_t tag[ZT_MAX_NETWORK_TAGS];
+ uint64_t cap[ZT_MAX_NETWORK_CAPABILITIES];
+ uint64_t coo[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
+ } _localCredLastPushed;
+
+public:
+ class CapabilityIterator
+ {
+ public:
+ CapabilityIterator(Membership &m,const NetworkConfig &nconf) :
+ _hti(m._remoteCaps),
+ _k((uint32_t *)0),
+ _c((Capability *)0),
+ _m(m),
+ _nconf(nconf)
+ {
+ }
+
+ inline Capability *next()
+ {
+ while (_hti.next(_k,_c)) {
+ if (_m._isCredentialTimestampValid(_nconf,*_c))
+ return _c;
+ }
+ return (Capability *)0;
+ }
+
+ private:
+ Hashtable< uint32_t,Capability >::Iterator _hti;
+ uint32_t *_k;
+ Capability *_c;
+ Membership &_m;
+ const NetworkConfig &_nconf;
+ };
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp
index dbf38998..0f4a621e 100644
--- a/node/MulticastGroup.hpp
+++ b/node/MulticastGroup.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MULTICASTGROUP_HPP
@@ -21,8 +29,6 @@
#include <stdint.h>
-#include <string>
-
#include "MAC.hpp"
#include "InetAddress.hpp"
@@ -46,30 +52,18 @@ namespace ZeroTier {
class MulticastGroup
{
public:
- MulticastGroup()
- throw() :
+ MulticastGroup() :
_mac(),
_adi(0)
{
}
- MulticastGroup(const MAC &m,uint32_t a)
- throw() :
+ MulticastGroup(const MAC &m,uint32_t a) :
_mac(m),
_adi(a)
{
}
- MulticastGroup(const char *s)
- {
- fromString(s);
- }
-
- MulticastGroup(const std::string &s)
- {
- fromString(s.c_str());
- }
-
/**
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
*
@@ -77,7 +71,6 @@ public:
* @return Multicat group for ARP/NDP
*/
static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip)
- throw()
{
if (ip.isV4()) {
// IPv4 wants broadcast MACs, so we shove the V4 address itself into
@@ -97,46 +90,20 @@ public:
}
/**
- * @return Human readable string representing this group (MAC/ADI in hex)
- */
- inline std::string toString() const
- {
- char buf[64];
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%.8lx",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned long)_adi);
- return std::string(buf);
- }
-
- /**
- * Parse a human-readable multicast group
- *
- * @param s Multicast group in hex MAC/ADI format
- */
- inline void fromString(const char *s)
- {
- char hex[17];
- unsigned int hexlen = 0;
- while ((*s)&&(*s != '/')&&(hexlen < (sizeof(hex) - 1)))
- hex[hexlen++] = *s;
- hex[hexlen] = (char)0;
- _mac.fromString(hex);
- _adi = (*s == '/') ? (uint32_t)Utils::hexStrToULong(s + 1) : (uint32_t)0;
- }
-
- /**
* @return Multicast address
*/
- inline const MAC &mac() const throw() { return _mac; }
+ inline const MAC &mac() const { return _mac; }
/**
* @return Additional distinguishing information
*/
- inline uint32_t adi() const throw() { return _adi; }
+ inline uint32_t adi() const { return _adi; }
- inline unsigned long hashCode() const throw() { return (_mac.hashCode() ^ (unsigned long)_adi); }
+ inline unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); }
- inline bool operator==(const MulticastGroup &g) const throw() { return ((_mac == g._mac)&&(_adi == g._adi)); }
- inline bool operator!=(const MulticastGroup &g) const throw() { return ((_mac != g._mac)||(_adi != g._adi)); }
- inline bool operator<(const MulticastGroup &g) const throw()
+ inline bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); }
+ inline bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); }
+ inline bool operator<(const MulticastGroup &g) const
{
if (_mac < g._mac)
return true;
@@ -144,9 +111,9 @@ public:
return (_adi < g._adi);
return false;
}
- inline bool operator>(const MulticastGroup &g) const throw() { return (g < *this); }
- inline bool operator<=(const MulticastGroup &g) const throw() { return !(g < *this); }
- inline bool operator>=(const MulticastGroup &g) const throw() { return !(*this < g); }
+ inline bool operator>(const MulticastGroup &g) const { return (g < *this); }
+ inline bool operator<=(const MulticastGroup &g) const { return !(g < *this); }
+ inline bool operator>=(const MulticastGroup &g) const { return !(*this < g); }
private:
MAC _mac;
diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp
index e1d4567a..753e4ee0 100644
--- a/node/Multicaster.cpp
+++ b/node/Multicaster.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,13 +14,20 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <algorithm>
#include "Constants.hpp"
#include "RuntimeEnvironment.hpp"
-#include "SharedPtr.hpp"
#include "Multicaster.hpp"
#include "Topology.hpp"
#include "Switch.hpp"
@@ -29,13 +36,14 @@
#include "C25519.hpp"
#include "CertificateOfMembership.hpp"
#include "Node.hpp"
+#include "Network.hpp"
namespace ZeroTier {
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
RR(renv),
- _groups(1024),
- _groups_m()
+ _groups(256),
+ _gatherAuth(256)
{
}
@@ -43,14 +51,14 @@ Multicaster::~Multicaster()
{
}
-void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
+void Multicaster::addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
{
const unsigned char *p = (const unsigned char *)addresses;
const unsigned char *e = p + (5 * count);
Mutex::Lock _l(_groups_m);
MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
while (p != e) {
- _add(now,nwid,mg,gs,Address(p,5));
+ _add(tPtr,now,nwid,mg,gs,Address(p,5));
p += 5;
}
}
@@ -103,7 +111,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const
// Members are returned in random order so that repeated gather queries
// will return different subsets of a large multicast group.
k = 0;
- while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) {
+ while ((added < limit)&&(k < s->members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_PROTO_MAX_PACKET_LENGTH)) {
rptr = (unsigned int)RR->node->prng();
restart_member_scan:
@@ -131,8 +139,6 @@ restart_member_scan:
appendTo.setAt(totalAt,(uint32_t)totalKnown);
appendTo.setAt(addedAt,(uint16_t)added);
- //TRACE("..MC Multicaster::gather() attached %u of %u peers for %.16llx/%s (2)",n,(unsigned int)(gs->second.members.size() - skipped),nwid,mg.toString().c_str());
-
return added;
}
@@ -152,23 +158,67 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
}
void Multicaster::send(
- const CertificateOfMembership *com,
- unsigned int limit,
- uint64_t now,
- uint64_t nwid,
- const std::vector<Address> &alwaysSendTo,
+ void *tPtr,
+ int64_t now,
+ const SharedPtr<Network> &network,
+ const Address &origin,
const MulticastGroup &mg,
const MAC &src,
unsigned int etherType,
const void *data,
unsigned int len)
{
- unsigned long idxbuf[8194];
+ unsigned long idxbuf[4096];
unsigned long *indexes = idxbuf;
+ // If we're in hub-and-spoke designated multicast replication mode, see if we
+ // have a multicast replicator active. If so, pick the best and send it
+ // there. If we are a multicast replicator or if none are alive, fall back
+ // to sender replication. Note that bridges do not do this since this would
+ // break bridge route learning. This is sort of an edge case limitation of
+ // the current protocol and could be fixed, but fixing it would add more
+ // complexity than the fix is probably worth. Bridges are generally high
+ // bandwidth nodes.
+ if (!network->config().isActiveBridge(RR->identity.address())) {
+ Address multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int multicastReplicatorCount = network->config().multicastReplicators(multicastReplicators);
+ if (multicastReplicatorCount) {
+ if (std::find(multicastReplicators,multicastReplicators + multicastReplicatorCount,RR->identity.address()) == (multicastReplicators + multicastReplicatorCount)) {
+ SharedPtr<Peer> bestMulticastReplicator;
+ SharedPtr<Path> bestMulticastReplicatorPath;
+ unsigned int bestMulticastReplicatorLatency = 0xffff;
+ for(unsigned int i=0;i<multicastReplicatorCount;++i) {
+ const SharedPtr<Peer> p(RR->topology->getPeerNoCache(multicastReplicators[i]));
+ if ((p)&&(p->isAlive(now))) {
+ const SharedPtr<Path> pp(p->getBestPath(now,false));
+ if ((pp)&&(pp->latency() < bestMulticastReplicatorLatency)) {
+ bestMulticastReplicatorLatency = pp->latency();
+ bestMulticastReplicatorPath = pp;
+ bestMulticastReplicator = p;
+ }
+ }
+ }
+ if (bestMulticastReplicator) {
+ Packet outp(bestMulticastReplicator->address(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
+ outp.append((uint64_t)network->id());
+ outp.append((uint8_t)0x0c); // includes source MAC | please replicate
+ ((src) ? src : MAC(RR->identity.address(),network->id())).appendTo(outp);
+ mg.mac().appendTo(outp);
+ outp.append((uint32_t)mg.adi());
+ outp.append((uint16_t)etherType);
+ outp.append(data,len);
+ if (!network->config().disableCompression()) outp.compress();
+ outp.armor(bestMulticastReplicator->key(),true);
+ bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
+ return;
+ }
+ }
+ }
+ }
+
try {
Mutex::Lock _l(_groups_m);
- MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
+ MulticastGroupStatus &gs = _groups[Multicaster::Key(network->id(),mg)];
if (!gs.members.empty()) {
// Allocate a memory buffer if group is monstrous
@@ -186,6 +236,10 @@ void Multicaster::send(
}
}
+ Address activeBridges[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int activeBridgeCount = network->config().activeBridges(activeBridges);
+ const unsigned int limit = network->config().multicastLimit;
+
if (gs.members.size() >= limit) {
// Skip queue if we already have enough members to complete the send operation
OutboundMulticast out;
@@ -193,8 +247,8 @@ void Multicaster::send(
out.init(
RR,
now,
- nwid,
- com,
+ network->id(),
+ network->config().disableCompression(),
limit,
1, // we'll still gather a little from peers to keep multicast list fresh
src,
@@ -205,9 +259,9 @@ void Multicaster::send(
unsigned int count = 0;
- for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
- if (*ast != RR->identity.address()) {
- out.sendOnly(RR,*ast); // optimization: don't use dedup log if it's a one-pass send
+ for(unsigned int i=0;i<activeBridgeCount;++i) {
+ if ((activeBridges[i] != RR->identity.address())&&(activeBridges[i] != origin)) {
+ out.sendOnly(RR,tPtr,activeBridges[i]); // optimization: don't use dedup log if it's a one-pass send
if (++count >= limit)
break;
}
@@ -215,46 +269,68 @@ void Multicaster::send(
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
- Address ma(gs.members[indexes[idx++]].address);
- if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
- out.sendOnly(RR,ma); // optimization: don't use dedup log if it's a one-pass send
+ const Address ma(gs.members[indexes[idx++]].address);
+ if ((std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount))&&(ma != origin)) {
+ out.sendOnly(RR,tPtr,ma); // optimization: don't use dedup log if it's a one-pass send
++count;
}
}
} else {
- unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
+ const unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
gs.lastExplicitGather = now;
- SharedPtr<Peer> explicitGatherPeers[2];
- explicitGatherPeers[0] = RR->topology->getBestRoot();
- const Address nwidc(Network::controllerFor(nwid));
- if (nwidc != RR->identity.address())
- explicitGatherPeers[1] = RR->topology->getPeer(nwidc);
- for(unsigned int k=0;k<2;++k) {
- const SharedPtr<Peer> &p = explicitGatherPeers[k];
- if (!p)
- continue;
- //TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
-
- const CertificateOfMembership *com = (CertificateOfMembership *)0;
- {
- SharedPtr<Network> nw(RR->node->network(nwid));
- if ((nw)&&(nw->hasConfig())&&(nw->config().com)&&(nw->config().isPrivate())&&(p->needsOurNetworkMembershipCertificate(nwid,now,true)))
- com = &(nw->config().com);
+
+ Address explicitGatherPeers[16];
+ unsigned int numExplicitGatherPeers = 0;
+
+ SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
+ if (bestRoot)
+ explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
+
+ explicitGatherPeers[numExplicitGatherPeers++] = network->controller();
+
+ Address ac[ZT_MAX_NETWORK_SPECIALISTS];
+ const unsigned int accnt = network->config().alwaysContactAddresses(ac);
+ unsigned int shuffled[ZT_MAX_NETWORK_SPECIALISTS];
+ for(unsigned int i=0;i<accnt;++i)
+ shuffled[i] = i;
+ for(unsigned int i=0,k=accnt>>1;i<k;++i) {
+ const uint64_t x = RR->node->prng();
+ const unsigned int x1 = shuffled[(unsigned int)x % accnt];
+ const unsigned int x2 = shuffled[(unsigned int)(x >> 32) % accnt];
+ const unsigned int tmp = shuffled[x1];
+ shuffled[x1] = shuffled[x2];
+ shuffled[x2] = tmp;
+ }
+ for(unsigned int i=0;i<accnt;++i) {
+ explicitGatherPeers[numExplicitGatherPeers++] = ac[shuffled[i]];
+ if (numExplicitGatherPeers == 16)
+ break;
+ }
+
+ std::vector<Address> anchors(network->config().anchors());
+ for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
+ if (*a != RR->identity.address()) {
+ explicitGatherPeers[numExplicitGatherPeers++] = *a;
+ if (numExplicitGatherPeers == 16)
+ break;
}
+ }
- Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
- outp.append(nwid);
- outp.append((uint8_t)(com ? 0x01 : 0x00));
+ for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
+ const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
+ Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
+ outp.append(network->id());
+ outp.append((uint8_t)((com) ? 0x01 : 0x00));
mg.mac().appendTo(outp);
outp.append((uint32_t)mg.adi());
outp.append((uint32_t)gatherLimit);
if (com)
com->serialize(outp);
- RR->sw->send(outp,true,0);
+ RR->node->expectReplyTo(outp.packetId());
+ RR->sw->send(tPtr,outp,true);
}
- gatherLimit = 0;
}
gs.txQueue.push_back(OutboundMulticast());
@@ -263,8 +339,8 @@ void Multicaster::send(
out.init(
RR,
now,
- nwid,
- com,
+ network->id(),
+ network->config().disableCompression(),
limit,
gatherLimit,
src,
@@ -273,11 +349,14 @@ void Multicaster::send(
data,
len);
+ if (origin)
+ out.logAsSent(origin);
+
unsigned int count = 0;
- for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
- if (*ast != RR->identity.address()) {
- out.sendAndLog(RR,*ast);
+ for(unsigned int i=0;i<activeBridgeCount;++i) {
+ if (activeBridges[i] != RR->identity.address()) {
+ out.sendAndLog(RR,tPtr,activeBridges[i]);
if (++count >= limit)
break;
}
@@ -286,8 +365,8 @@ void Multicaster::send(
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
- if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
- out.sendAndLog(RR,ma);
+ if (std::find(activeBridges,activeBridges + activeBridgeCount,ma) == (activeBridges + activeBridgeCount)) {
+ out.sendAndLog(RR,tPtr,ma);
++count;
}
}
@@ -299,45 +378,65 @@ void Multicaster::send(
delete [] indexes;
}
-void Multicaster::clean(uint64_t now)
+void Multicaster::clean(int64_t now)
{
- Mutex::Lock _l(_groups_m);
-
- Multicaster::Key *k = (Multicaster::Key *)0;
- MulticastGroupStatus *s = (MulticastGroupStatus *)0;
- Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
- while (mm.next(k,s)) {
- for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
- if ((tx->expired(now))||(tx->atLimit()))
- s->txQueue.erase(tx++);
- else ++tx;
- }
+ {
+ Mutex::Lock _l(_groups_m);
+ Multicaster::Key *k = (Multicaster::Key *)0;
+ MulticastGroupStatus *s = (MulticastGroupStatus *)0;
+ Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
+ while (mm.next(k,s)) {
+ for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
+ if ((tx->expired(now))||(tx->atLimit()))
+ s->txQueue.erase(tx++);
+ else ++tx;
+ }
- unsigned long count = 0;
- {
- std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
- std::vector<MulticastGroupMember>::iterator writer(reader);
- while (reader != s->members.end()) {
- if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
- *writer = *reader;
- ++writer;
- ++count;
+ unsigned long count = 0;
+ {
+ std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
+ std::vector<MulticastGroupMember>::iterator writer(reader);
+ while (reader != s->members.end()) {
+ if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
+ *writer = *reader;
+ ++writer;
+ ++count;
+ }
+ ++reader;
}
- ++reader;
+ }
+
+ if (count) {
+ s->members.resize(count);
+ } else if (s->txQueue.empty()) {
+ _groups.erase(*k);
+ } else {
+ s->members.clear();
}
}
+ }
- if (count) {
- s->members.resize(count);
- } else if (s->txQueue.empty()) {
- _groups.erase(*k);
- } else {
- s->members.clear();
+ {
+ Mutex::Lock _l(_gatherAuth_m);
+ _GatherAuthKey *k = (_GatherAuthKey *)0;
+ uint64_t *ts = NULL;
+ Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
+ while (i.next(k,ts)) {
+ if ((now - *ts) >= ZT_MULTICAST_CREDENTIAL_EXPIRATON)
+ _gatherAuth.erase(*k);
}
}
}
-void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
+void Multicaster::addCredential(void *tPtr,const CertificateOfMembership &com,bool alreadyValidated)
+{
+ if ((alreadyValidated)||(com.verify(RR,tPtr) == 0)) {
+ Mutex::Lock _l(_gatherAuth_m);
+ _gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
+ }
+}
+
+void Multicaster::_add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
{
// assumes _groups_m is locked
@@ -354,13 +453,11 @@ void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,Multi
gs.members.push_back(MulticastGroupMember(member,now));
- //TRACE("..MC %s joined multicast group %.16llx/%s via %s",member.toString().c_str(),nwid,mg.toString().c_str(),((learnedFrom) ? learnedFrom.toString().c_str() : "(direct)"));
-
for(std::list<OutboundMulticast>::iterator tx(gs.txQueue.begin());tx!=gs.txQueue.end();) {
if (tx->atLimit())
gs.txQueue.erase(tx++);
else {
- tx->sendIfNew(RR,member);
+ tx->sendIfNew(RR,tPtr,member);
if (tx->atLimit())
gs.txQueue.erase(tx++);
else ++tx;
diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp
index c43c8d93..e57f81fe 100644
--- a/node/Multicaster.hpp
+++ b/node/Multicaster.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MULTICASTER_HPP
@@ -34,50 +42,20 @@
#include "OutboundMulticast.hpp"
#include "Utils.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
+#include "SharedPtr.hpp"
namespace ZeroTier {
class RuntimeEnvironment;
class CertificateOfMembership;
class Packet;
+class Network;
/**
* Database of known multicast peers within a network
*/
-class Multicaster : NonCopyable
+class Multicaster
{
-private:
- struct Key
- {
- Key() : nwid(0),mg() {}
- Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
-
- uint64_t nwid;
- MulticastGroup mg;
-
- inline bool operator==(const Key &k) const throw() { return ((nwid == k.nwid)&&(mg == k.mg)); }
- inline unsigned long hashCode() const throw() { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
- };
-
- struct MulticastGroupMember
- {
- MulticastGroupMember() {}
- MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
-
- Address address;
- uint64_t timestamp; // time of last notification
- };
-
- struct MulticastGroupStatus
- {
- MulticastGroupStatus() : lastExplicitGather(0) {}
-
- uint64_t lastExplicitGather;
- std::list<OutboundMulticast> txQueue; // pending outbound multicasts
- std::vector<MulticastGroupMember> members; // members of this group
- };
-
public:
Multicaster(const RuntimeEnvironment *renv);
~Multicaster();
@@ -90,10 +68,10 @@ public:
* @param mg Multicast group
* @param member New member address
*/
- inline void add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
+ inline void add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
{
Mutex::Lock _l(_groups_m);
- _add(now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
+ _add(tPtr,now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
}
/**
@@ -101,6 +79,7 @@ public:
*
* It's up to the caller to check bounds on the array before calling this.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
* @param nwid Network ID
* @param mg Multicast group
@@ -108,7 +87,7 @@ public:
* @param count Number of addresses
* @param totalKnown Total number of known addresses as reported by peer
*/
- void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
+ void addMultiple(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
/**
* Remove a multicast group member (if present)
@@ -150,11 +129,10 @@ public:
/**
* Send a multicast
*
- * @param com Certificate of membership to include or NULL for none
- * @param limit Multicast limit
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
- * @param nwid Network ID
- * @param alwaysSendTo Send to these peers first and even if not included in subscriber list
+ * @param network Network
+ * @param origin Origin of multicast (to not return to sender) or NULL if none
* @param mg Multicast group
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
* @param etherType Ethernet frame type
@@ -162,11 +140,10 @@ public:
* @param len Length of packet data
*/
void send(
- const CertificateOfMembership *com,
- unsigned int limit,
- uint64_t now,
- uint64_t nwid,
- const std::vector<Address> &alwaysSendTo,
+ void *tPtr,
+ int64_t now,
+ const SharedPtr<Network> &network,
+ const Address &origin,
const MulticastGroup &mg,
const MAC &src,
unsigned int etherType,
@@ -179,14 +156,84 @@ public:
* @param RR Runtime environment
* @param now Current time
*/
- void clean(uint64_t now);
+ void clean(int64_t now);
+
+ /**
+ * Add an authorization credential
+ *
+ * The Multicaster keeps its own track of when valid credentials of network
+ * membership are presented. This allows it to control MULTICAST_LIKE
+ * GATHER authorization for networks this node does not belong to.
+ *
+ * @param com Certificate of membership
+ * @param alreadyValidated If true, COM has already been checked and found to be valid and signed
+ */
+ void addCredential(void *tPtr,const CertificateOfMembership &com,bool alreadyValidated);
+
+ /**
+ * Check authorization for GATHER and LIKE for non-network-members
+ *
+ * @param a Address of peer
+ * @param nwid Network ID
+ * @param now Current time
+ * @return True if GATHER and LIKE should be allowed
+ */
+ bool cacheAuthorized(const Address &a,const uint64_t nwid,const int64_t now) const
+ {
+ Mutex::Lock _l(_gatherAuth_m);
+ const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
+ return ((p)&&((now - *p) < ZT_MULTICAST_CREDENTIAL_EXPIRATON));
+ }
private:
- void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
+ struct Key
+ {
+ Key() : nwid(0),mg() {}
+ Key(uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {}
+
+ uint64_t nwid;
+ MulticastGroup mg;
+
+ inline bool operator==(const Key &k) const { return ((nwid == k.nwid)&&(mg == k.mg)); }
+ inline unsigned long hashCode() const { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); }
+ };
+
+ struct MulticastGroupMember
+ {
+ MulticastGroupMember() {}
+ MulticastGroupMember(const Address &a,uint64_t ts) : address(a),timestamp(ts) {}
+
+ Address address;
+ uint64_t timestamp; // time of last notification
+ };
+
+ struct MulticastGroupStatus
+ {
+ MulticastGroupStatus() : lastExplicitGather(0) {}
+
+ uint64_t lastExplicitGather;
+ std::list<OutboundMulticast> txQueue; // pending outbound multicasts
+ std::vector<MulticastGroupMember> members; // members of this group
+ };
+
+ void _add(void *tPtr,int64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
+
+ const RuntimeEnvironment *const RR;
- const RuntimeEnvironment *RR;
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
Mutex _groups_m;
+
+ struct _GatherAuthKey
+ {
+ _GatherAuthKey() : member(0),networkId(0) {}
+ _GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {}
+ inline unsigned long hashCode() const { return (unsigned long)(member ^ networkId); }
+ inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); }
+ uint64_t member;
+ uint64_t networkId;
+ };
+ Hashtable< _GatherAuthKey,uint64_t > _gatherAuth;
+ Mutex _gatherAuth_m;
};
} // namespace ZeroTier
diff --git a/node/Mutex.hpp b/node/Mutex.hpp
index d451ede0..a60a00b2 100644
--- a/node/Mutex.hpp
+++ b/node/Mutex.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,74 +14,126 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_MUTEX_HPP
#define ZT_MUTEX_HPP
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#ifdef __UNIX_LIKE__
+#include <stdint.h>
#include <stdlib.h>
#include <pthread.h>
namespace ZeroTier {
-class Mutex : NonCopyable
+#if defined(__GNUC__) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+
+// Inline ticket lock on x64 systems with GCC and CLANG (Mac, Linux) -- this is really fast as long as locking durations are very short
+class Mutex
{
public:
- Mutex()
- throw()
+ Mutex() :
+ nextTicket(0),
+ nowServing(0)
{
- pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
}
- ~Mutex()
+ inline void lock() const
{
- pthread_mutex_destroy(&_mh);
+ const uint16_t myTicket = __sync_fetch_and_add(&(const_cast<Mutex *>(this)->nextTicket),1);
+ while (nowServing != myTicket) {
+ __asm__ __volatile__("rep;nop"::);
+ __asm__ __volatile__("":::"memory");
+ }
}
- inline void lock()
- throw()
+ inline void unlock() const
{
- pthread_mutex_lock(&_mh);
+ ++(const_cast<Mutex *>(this)->nowServing);
}
- inline void unlock()
- throw()
+ /**
+ * Uses C++ contexts and constructor/destructor to lock/unlock automatically
+ */
+ class Lock
+ {
+ public:
+ Lock(Mutex &m) :
+ _m(&m)
+ {
+ m.lock();
+ }
+
+ Lock(const Mutex &m) :
+ _m(const_cast<Mutex *>(&m))
+ {
+ _m->lock();
+ }
+
+ ~Lock()
+ {
+ _m->unlock();
+ }
+
+ private:
+ Mutex *const _m;
+ };
+
+private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
+ uint16_t nextTicket;
+ uint16_t nowServing;
+};
+
+#else
+
+// libpthread based mutex lock
+class Mutex
+{
+public:
+ Mutex()
{
- pthread_mutex_unlock(&_mh);
+ pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
+ }
+
+ ~Mutex()
+ {
+ pthread_mutex_destroy(&_mh);
}
inline void lock() const
- throw()
{
- (const_cast <Mutex *> (this))->lock();
+ pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh));
}
inline void unlock() const
- throw()
{
- (const_cast <Mutex *> (this))->unlock();
+ pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh));
}
- /**
- * Uses C++ contexts and constructor/destructor to lock/unlock automatically
- */
- class Lock : NonCopyable
+ class Lock
{
public:
- Lock(Mutex &m)
- throw() :
+ Lock(Mutex &m) :
_m(&m)
{
m.lock();
}
- Lock(const Mutex &m)
- throw() :
+ Lock(const Mutex &m) :
_m(const_cast<Mutex *>(&m))
{
_m->lock();
@@ -97,9 +149,14 @@ public:
};
private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
pthread_mutex_t _mh;
};
+#endif
+
} // namespace ZeroTier
#endif // Apple / Linux
@@ -111,11 +168,11 @@ private:
namespace ZeroTier {
-class Mutex : NonCopyable
+// Windows critical section based lock
+class Mutex
{
public:
Mutex()
- throw()
{
InitializeCriticalSection(&_cs);
}
@@ -126,41 +183,35 @@ public:
}
inline void lock()
- throw()
{
EnterCriticalSection(&_cs);
}
inline void unlock()
- throw()
{
LeaveCriticalSection(&_cs);
}
inline void lock() const
- throw()
{
(const_cast <Mutex *> (this))->lock();
}
inline void unlock() const
- throw()
{
(const_cast <Mutex *> (this))->unlock();
}
- class Lock : NonCopyable
+ class Lock
{
public:
- Lock(Mutex &m)
- throw() :
+ Lock(Mutex &m) :
_m(&m)
{
m.lock();
}
- Lock(const Mutex &m)
- throw() :
+ Lock(const Mutex &m) :
_m(const_cast<Mutex *>(&m))
{
_m->lock();
@@ -176,6 +227,9 @@ public:
};
private:
+ Mutex(const Mutex &) {}
+ const Mutex &operator=(const Mutex &) { return *this; }
+
CRITICAL_SECTION _cs;
};
diff --git a/node/Network.cpp b/node/Network.cpp
index 25116647..a75d9fd1 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -22,24 +30,527 @@
#include <math.h>
#include "Constants.hpp"
+#include "../version.h"
#include "Network.hpp"
#include "RuntimeEnvironment.hpp"
+#include "MAC.hpp"
+#include "Address.hpp"
+#include "InetAddress.hpp"
#include "Switch.hpp"
-#include "Packet.hpp"
#include "Buffer.hpp"
+#include "Packet.hpp"
#include "NetworkController.hpp"
#include "Node.hpp"
+#include "Peer.hpp"
+#include "Trace.hpp"
-#include "../version.h"
+#include <set>
namespace ZeroTier {
+namespace {
+
+// Returns true if packet appears valid; pos and proto will be set
+static bool _ipv6GetPayload(const uint8_t *frameData,unsigned int frameLen,unsigned int &pos,unsigned int &proto)
+{
+ if (frameLen < 40)
+ return false;
+ pos = 40;
+ proto = frameData[6];
+ while (pos <= frameLen) {
+ switch(proto) {
+ case 0: // hop-by-hop options
+ case 43: // routing
+ case 60: // destination options
+ case 135: // mobility options
+ if ((pos + 8) > frameLen)
+ return false; // invalid!
+ proto = frameData[pos];
+ pos += ((unsigned int)frameData[pos + 1] * 8) + 8;
+ break;
+
+ //case 44: // fragment -- we currently can't parse these and they are deprecated in IPv6 anyway
+ //case 50:
+ //case 51: // IPSec ESP and AH -- we have to stop here since this is encrypted stuff
+ default:
+ return true;
+ }
+ }
+ return false; // overflow == invalid
+}
+
+enum _doZtFilterResult
+{
+ DOZTFILTER_NO_MATCH,
+ DOZTFILTER_DROP,
+ DOZTFILTER_REDIRECT,
+ DOZTFILTER_ACCEPT,
+ DOZTFILTER_SUPER_ACCEPT
+};
+
+static _doZtFilterResult _doZtFilter(
+ const RuntimeEnvironment *RR,
+ Trace::RuleResultLog &rrl,
+ const NetworkConfig &nconf,
+ const Membership *membership, // can be NULL
+ const bool inbound,
+ const Address &ztSource,
+ Address &ztDest, // MUTABLE -- is changed on REDIRECT actions
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const ZT_VirtualNetworkRule *rules, // cannot be NULL
+ const unsigned int ruleCount,
+ Address &cc, // MUTABLE -- set to TEE destination if TEE action is taken or left alone otherwise
+ unsigned int &ccLength, // MUTABLE -- set to length of packet payload to TEE
+ bool &ccWatch) // MUTABLE -- set to true for WATCH target as opposed to normal TEE
+{
+ // Set to true if we are a TEE/REDIRECT/WATCH target
+ bool superAccept = false;
+
+ // The default match state for each set of entries starts as 'true' since an
+ // ACTION with no MATCH entries preceding it is always taken.
+ uint8_t thisSetMatches = 1;
+
+ rrl.clear();
+
+ for(unsigned int rn=0;rn<ruleCount;++rn) {
+ const ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[rn].t & 0x3f);
+
+ // First check if this is an ACTION
+ if ((unsigned int)rt <= (unsigned int)ZT_NETWORK_RULE_ACTION__MAX_ID) {
+ if (thisSetMatches) {
+ switch(rt) {
+ case ZT_NETWORK_RULE_ACTION_DROP:
+ return DOZTFILTER_DROP;
+
+ case ZT_NETWORK_RULE_ACTION_ACCEPT:
+ return (superAccept ? DOZTFILTER_SUPER_ACCEPT : DOZTFILTER_ACCEPT); // match, accept packet
+
+ // These are initially handled together since preliminary logic is common
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT: {
+ const Address fwdAddr(rules[rn].v.fwd.address);
+ if (fwdAddr == ztSource) {
+ // Skip as no-op since source is target
+ } else if (fwdAddr == RR->identity.address()) {
+ if (inbound) {
+ return DOZTFILTER_SUPER_ACCEPT;
+ } else {
+ }
+ } else if (fwdAddr == ztDest) {
+ } else {
+ if (rt == ZT_NETWORK_RULE_ACTION_REDIRECT) {
+ ztDest = fwdAddr;
+ return DOZTFILTER_REDIRECT;
+ } else {
+ cc = fwdAddr;
+ ccLength = (rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen;
+ ccWatch = (rt == ZT_NETWORK_RULE_ACTION_WATCH);
+ }
+ }
+ } continue;
+
+ case ZT_NETWORK_RULE_ACTION_BREAK:
+ return DOZTFILTER_NO_MATCH;
+
+ // Unrecognized ACTIONs are ignored as no-ops
+ default:
+ continue;
+ }
+ } else {
+ // If this is an incoming packet and we are a TEE or REDIRECT target, we should
+ // super-accept if we accept at all. This will cause us to accept redirected or
+ // tee'd packets in spite of MAC and ZT addressing checks.
+ if (inbound) {
+ switch(rt) {
+ case ZT_NETWORK_RULE_ACTION_TEE:
+ case ZT_NETWORK_RULE_ACTION_WATCH:
+ case ZT_NETWORK_RULE_ACTION_REDIRECT:
+ if (RR->identity.address() == rules[rn].v.fwd.address)
+ superAccept = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ thisSetMatches = 1; // reset to default true for next batch of entries
+ continue;
+ }
+ }
+
+ // Circuit breaker: no need to evaluate an AND if the set's match state
+ // is currently false since anything AND false is false.
+ if ((!thisSetMatches)&&(!(rules[rn].t & 0x40))) {
+ rrl.logSkipped(rn,thisSetMatches);
+ continue;
+ }
+
+ // If this was not an ACTION evaluate next MATCH and update thisSetMatches with (AND [result])
+ uint8_t thisRuleMatches = 0;
+ uint64_t ownershipVerificationMask = 1; // this magic value means it hasn't been computed yet -- this is done lazily the first time it's needed
+ switch(rt) {
+ case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
+ thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztSource.toInt());
+ break;
+ case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
+ thisRuleMatches = (uint8_t)(rules[rn].v.zt == ztDest.toInt());
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_ID:
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanId == (uint16_t)vlanId);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
+ // NOT SUPPORTED YET
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanPcp == 0);
+ break;
+ case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
+ // NOT SUPPORTED YET
+ thisRuleMatches = (uint8_t)(rules[rn].v.vlanDei == 0);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
+ thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macSource);
+ break;
+ case ZT_NETWORK_RULE_MATCH_MAC_DEST:
+ thisRuleMatches = (uint8_t)(MAC(rules[rn].v.mac,6) == macDest);
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 12),4,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)&(rules[rn].v.ipv4.ip),4,rules[rn].v.ipv4.mask).containsAddress(InetAddress((const void *)(frameData + 16),4,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
+ if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 8),16,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
+ if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ thisRuleMatches = (uint8_t)(InetAddress((const void *)rules[rn].v.ipv6.ip,16,rules[rn].v.ipv6.mask).containsAddress(InetAddress((const void *)(frameData + 24),16,0)));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_TOS:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ const uint8_t tosMasked = frameData[1] & rules[rn].v.ipTos.mask;
+ thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
+ } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ const uint8_t tosMasked = (((frameData[0] << 4) & 0xf0) | ((frameData[1] >> 4) & 0x0f)) & rules[rn].v.ipTos.mask;
+ thisRuleMatches = (uint8_t)((tosMasked >= rules[rn].v.ipTos.value[0])&&(tosMasked <= rules[rn].v.ipTos.value[1]));
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == frameData[9]);
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ thisRuleMatches = (uint8_t)(rules[rn].v.ipProtocol == (uint8_t)proto);
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
+ thisRuleMatches = (uint8_t)(rules[rn].v.etherType == (uint16_t)etherType);
+ break;
+ case ZT_NETWORK_RULE_MATCH_ICMP:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ if (frameData[9] == 0x01) { // IP protocol == ICMP
+ const unsigned int ihl = (frameData[0] & 0xf) * 4;
+ if (frameLen >= (ihl + 2)) {
+ if (rules[rn].v.icmp.type == frameData[ihl]) {
+ if ((rules[rn].v.icmp.flags & 0x01) != 0) {
+ thisRuleMatches = (uint8_t)(frameData[ihl+1] == rules[rn].v.icmp.code);
+ } else {
+ thisRuleMatches = 1;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ if ((proto == 0x3a)&&(frameLen >= (pos+2))) {
+ if (rules[rn].v.icmp.type == frameData[pos]) {
+ if ((rules[rn].v.icmp.flags & 0x01) != 0) {
+ thisRuleMatches = (uint8_t)(frameData[pos+1] == rules[rn].v.icmp.code);
+ } else {
+ thisRuleMatches = 1;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
+ case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+ int p = -1;
+ switch(frameData[9]) { // IP protocol number
+ // All these start with 16-bit source and destination port in that order
+ case 0x06: // TCP
+ case 0x11: // UDP
+ case 0x84: // SCTP
+ case 0x88: // UDPLite
+ if (frameLen > (headerLen + 4)) {
+ unsigned int pos = headerLen + ((rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) ? 2 : 0);
+ p = (int)frameData[pos++] << 8;
+ p |= (int)frameData[pos];
+ }
+ break;
+ }
+
+ thisRuleMatches = (p >= 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ int p = -1;
+ switch(proto) { // IP protocol number
+ // All these start with 16-bit source and destination port in that order
+ case 0x06: // TCP
+ case 0x11: // UDP
+ case 0x84: // SCTP
+ case 0x88: // UDPLite
+ if (frameLen > (pos + 4)) {
+ if (rt == ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE) pos += 2;
+ p = (int)frameData[pos++] << 8;
+ p |= (int)frameData[pos];
+ }
+ break;
+ }
+ thisRuleMatches = (p > 0) ? (uint8_t)((p >= (int)rules[rn].v.port[0])&&(p <= (int)rules[rn].v.port[1])) : (uint8_t)0;
+ } else {
+ thisRuleMatches = 0;
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ break;
+ case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS: {
+ uint64_t cf = (inbound) ? ZT_RULE_PACKET_CHARACTERISTICS_INBOUND : 0ULL;
+ if (macDest.isMulticast()) cf |= ZT_RULE_PACKET_CHARACTERISTICS_MULTICAST;
+ if (macDest.isBroadcast()) cf |= ZT_RULE_PACKET_CHARACTERISTICS_BROADCAST;
+ if (ownershipVerificationMask == 1) {
+ ownershipVerificationMask = 0;
+ InetAddress src;
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)) {
+ src.set((const void *)(frameData + 12),4,0);
+ } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(frameLen >= 40)) {
+ // IPv6 NDP requires special handling, since the src and dest IPs in the packet are empty or link-local.
+ if ( (frameLen >= (40 + 8 + 16)) && (frameData[6] == 0x3a) && ((frameData[40] == 0x87)||(frameData[40] == 0x88)) ) {
+ if (frameData[40] == 0x87) {
+ // Neighbor solicitations contain no reliable source address, so we implement a small
+ // hack by considering them authenticated. Otherwise you would pretty much have to do
+ // this manually in the rule set for IPv6 to work at all.
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
+ } else {
+ // Neighbor advertisements on the other hand can absolutely be authenticated.
+ src.set((const void *)(frameData + 40 + 8),16,0);
+ }
+ } else {
+ // Other IPv6 packets can be handled normally
+ src.set((const void *)(frameData + 8),16,0);
+ }
+ } else if ((etherType == ZT_ETHERTYPE_ARP)&&(frameLen >= 28)) {
+ src.set((const void *)(frameData + 14),4,0);
+ }
+ if (inbound) {
+ if (membership) {
+ if ((src)&&(membership->hasCertificateOfOwnershipFor<InetAddress>(nconf,src)))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
+ if (membership->hasCertificateOfOwnershipFor<MAC>(nconf,macSource))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED;
+ }
+ } else {
+ for(unsigned int i=0;i<nconf.certificateOfOwnershipCount;++i) {
+ if ((src)&&(nconf.certificatesOfOwnership[i].owns(src)))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
+ if (nconf.certificatesOfOwnership[i].owns(macSource))
+ ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED;
+ }
+ }
+ }
+ cf |= ownershipVerificationMask;
+ if ((etherType == ZT_ETHERTYPE_IPV4)&&(frameLen >= 20)&&(frameData[9] == 0x06)) {
+ const unsigned int headerLen = 4 * (frameData[0] & 0xf);
+ cf |= (uint64_t)frameData[headerLen + 13];
+ cf |= (((uint64_t)(frameData[headerLen + 12] & 0x0f)) << 8);
+ } else if (etherType == ZT_ETHERTYPE_IPV6) {
+ unsigned int pos = 0,proto = 0;
+ if (_ipv6GetPayload(frameData,frameLen,pos,proto)) {
+ if ((proto == 0x06)&&(frameLen > (pos + 14))) {
+ cf |= (uint64_t)frameData[pos + 13];
+ cf |= (((uint64_t)(frameData[pos + 12] & 0x0f)) << 8);
+ }
+ }
+ }
+ thisRuleMatches = (uint8_t)((cf & rules[rn].v.characteristics) != 0);
+ } break;
+ case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
+ thisRuleMatches = (uint8_t)((frameLen >= (unsigned int)rules[rn].v.frameSize[0])&&(frameLen <= (unsigned int)rules[rn].v.frameSize[1]));
+ break;
+ case ZT_NETWORK_RULE_MATCH_RANDOM:
+ thisRuleMatches = (uint8_t)((uint32_t)(RR->node->prng() & 0xffffffffULL) <= rules[rn].v.randomProbability);
+ break;
+ case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR:
+ case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL: {
+ const Tag *const localTag = std::lower_bound(&(nconf.tags[0]),&(nconf.tags[nconf.tagCount]),rules[rn].v.tag.id,Tag::IdComparePredicate());
+ if ((localTag != &(nconf.tags[nconf.tagCount]))&&(localTag->id() == rules[rn].v.tag.id)) {
+ const Tag *const remoteTag = ((membership) ? membership->getTag(nconf,rules[rn].v.tag.id) : (const Tag *)0);
+ if (remoteTag) {
+ const uint32_t ltv = localTag->value();
+ const uint32_t rtv = remoteTag->value();
+ if (rt == ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE) {
+ const uint32_t diff = (ltv > rtv) ? (ltv - rtv) : (rtv - ltv);
+ thisRuleMatches = (uint8_t)(diff <= rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND) {
+ thisRuleMatches = (uint8_t)((ltv & rtv) == rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR) {
+ thisRuleMatches = (uint8_t)((ltv | rtv) == rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR) {
+ thisRuleMatches = (uint8_t)((ltv ^ rtv) == rules[rn].v.tag.value);
+ } else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_EQUAL) {
+ thisRuleMatches = (uint8_t)((ltv == rules[rn].v.tag.value)&&(rtv == rules[rn].v.tag.value));
+ } else { // sanity check, can't really happen
+ thisRuleMatches = 0;
+ }
+ } else {
+ if ((inbound)&&(!superAccept)) {
+ thisRuleMatches = 0;
+ } else {
+ // Outbound side is not strict since if we have to match both tags and
+ // we are sending a first packet to a recipient, we probably do not know
+ // about their tags yet. They will filter on inbound and we will filter
+ // once we get their tag. If we are a tee/redirect target we are also
+ // not strict since we likely do not have these tags.
+ thisRuleMatches = 1;
+ }
+ }
+ } else {
+ thisRuleMatches = 0;
+ }
+ } break;
+ case ZT_NETWORK_RULE_MATCH_TAG_SENDER:
+ case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER: {
+ if (superAccept) {
+ thisRuleMatches = 1;
+ } else if ( ((rt == ZT_NETWORK_RULE_MATCH_TAG_SENDER)&&(inbound)) || ((rt == ZT_NETWORK_RULE_MATCH_TAG_RECEIVER)&&(!inbound)) ) {
+ const Tag *const remoteTag = ((membership) ? membership->getTag(nconf,rules[rn].v.tag.id) : (const Tag *)0);
+ if (remoteTag) {
+ thisRuleMatches = (uint8_t)(remoteTag->value() == rules[rn].v.tag.value);
+ } else {
+ if (rt == ZT_NETWORK_RULE_MATCH_TAG_RECEIVER) {
+ // If we are checking the receiver and this is an outbound packet, we
+ // can't be strict since we may not yet know the receiver's tag.
+ thisRuleMatches = 1;
+ } else {
+ thisRuleMatches = 0;
+ }
+ }
+ } else { // sender and outbound or receiver and inbound
+ const Tag *const localTag = std::lower_bound(&(nconf.tags[0]),&(nconf.tags[nconf.tagCount]),rules[rn].v.tag.id,Tag::IdComparePredicate());
+ if ((localTag != &(nconf.tags[nconf.tagCount]))&&(localTag->id() == rules[rn].v.tag.id)) {
+ thisRuleMatches = (uint8_t)(localTag->value() == rules[rn].v.tag.value);
+ } else {
+ thisRuleMatches = 0;
+ }
+ }
+ } break;
+ case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE: {
+ uint64_t integer = 0;
+ const unsigned int bits = (rules[rn].v.intRange.format & 63) + 1;
+ const unsigned int bytes = ((bits + 8 - 1) / 8); // integer ceiling of division by 8
+ if ((rules[rn].v.intRange.format & 0x80) == 0) {
+ // Big-endian
+ unsigned int idx = rules[rn].v.intRange.idx + (8 - bytes);
+ const unsigned int eof = idx + bytes;
+ if (eof <= frameLen) {
+ while (idx < eof) {
+ integer <<= 8;
+ integer |= frameData[idx++];
+ }
+ }
+ integer &= 0xffffffffffffffffULL >> (64 - bits);
+ } else {
+ // Little-endian
+ unsigned int idx = rules[rn].v.intRange.idx;
+ const unsigned int eof = idx + bytes;
+ if (eof <= frameLen) {
+ while (idx < eof) {
+ integer >>= 8;
+ integer |= ((uint64_t)frameData[idx++]) << 56;
+ }
+ }
+ integer >>= (64 - bits);
+ }
+ thisRuleMatches = (uint8_t)((integer >= rules[rn].v.intRange.start)&&(integer <= (rules[rn].v.intRange.start + (uint64_t)rules[rn].v.intRange.end)));
+ } break;
+
+ // The result of an unsupported MATCH is configurable at the network
+ // level via a flag.
+ default:
+ thisRuleMatches = (uint8_t)((nconf.flags & ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH) != 0);
+ break;
+ }
+
+ rrl.log(rn,thisRuleMatches,thisSetMatches);
+
+ if ((rules[rn].t & 0x40))
+ thisSetMatches |= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
+ else thisSetMatches &= (thisRuleMatches ^ ((rules[rn].t >> 7) & 1));
+ }
+
+ return DOZTFILTER_NO_MATCH;
+}
+
+} // anonymous namespace
+
const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
-Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
+Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf) :
RR(renv),
_uPtr(uptr),
_id(nwid),
+ _lastAnnouncedMulticastGroupsUpstream(0),
_mac(renv->identity.address(),nwid),
_portInitialized(false),
_lastConfigUpdate(0),
@@ -47,43 +558,42 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
_netconfFailure(NETCONF_FAILURE_NONE),
_portError(0)
{
- char confn[128],mcdbn[128];
- Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id);
- Utils::snprintf(mcdbn,sizeof(mcdbn),"networks.d/%.16llx.mcerts",_id);
-
- // These files are no longer used, so clean them.
- RR->node->dataStoreDelete(mcdbn);
-
- if (_id == ZT_TEST_NETWORK_ID) {
- applyConfiguration(NetworkConfig::createTestNetworkConfig(RR->identity.address()));
+ for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i)
+ _incomingConfigChunks[i].ts = 0;
- // Save a one-byte CR to persist membership in the test network
- RR->node->dataStorePut(confn,"\n",1,false);
+ if (nconf) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request since it's likely outdated
} else {
- bool gotConf = false;
+ uint64_t tmp[2];
+ tmp[0] = nwid; tmp[1] = 0;
+
+ bool got = false;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
- std::string conf(RR->node->dataStoreGet(confn));
- if (conf.length()) {
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf(conf.c_str());
- NetworkConfig nconf;
- if (nconf.fromDictionary(dconf)) {
- this->setConfiguration(nconf,false);
- _lastConfigUpdate = 0; // we still want to re-request a new config from the network
- gotConf = true;
- }
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,dict->unsafeData(),ZT_NETWORKCONFIG_DICT_CAPACITY - 1);
+ if (n > 1) {
+ NetworkConfig *nconf = new NetworkConfig();
+ try {
+ if (nconf->fromDictionary(*dict)) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request an update since it's likely outdated
+ got = true;
+ }
+ } catch ( ... ) {}
+ delete nconf;
}
- } catch ( ... ) {} // ignore invalids, we'll re-request
+ } catch ( ... ) {}
+ delete dict;
- if (!gotConf) {
- // Save a one-byte CR to persist membership while we request a real netconf
- RR->node->dataStorePut(confn,"\n",1,false);
- }
+ if (!got)
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,"\n",1);
}
if (!_portInitialized) {
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
- _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
+ _portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
_portInitialized = true;
}
}
@@ -93,16 +603,260 @@ Network::~Network()
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
- char n[128];
if (_destroyed) {
- RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- RR->node->dataStoreDelete(n);
+ // This is done in Node::leave() so we can pass tPtr properly
+ //RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
} else {
- RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
+ RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
}
}
+bool Network::filterOutgoingPacket(
+ void *tPtr,
+ const bool noTee,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId)
+{
+ const int64_t now = RR->node->now();
+ Address ztFinalDest(ztDest);
+ int localCapabilityIndex = -1;
+ int accept = 0;
+ Trace::RuleResultLog rrl,crrl;
+ Address cc;
+ unsigned int ccLength = 0;
+ bool ccWatch = false;
+
+ Mutex::Lock _l(_lock);
+
+ Membership *const membership = (ztDest) ? _memberships.get(ztDest) : (Membership *)0;
+
+ switch(_doZtFilter(RR,rrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+
+ case DOZTFILTER_NO_MATCH: {
+ for(unsigned int c=0;c<_config.capabilityCount;++c) {
+ ztFinalDest = ztDest; // sanity check, shouldn't be possible if there was no match
+ Address cc2;
+ unsigned int ccLength2 = 0;
+ bool ccWatch2 = false;
+ switch (_doZtFilter(RR,crrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2)) {
+ case DOZTFILTER_NO_MATCH:
+ case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
+ break;
+
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side in capabilities
+ localCapabilityIndex = (int)c;
+ accept = 1;
+
+ if ((!noTee)&&(cc2)) {
+ Membership &m2 = _membership(cc2);
+ m2.pushCredentials(RR,tPtr,now,cc2,_config,localCapabilityIndex,false);
+
+ Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch2 ? 0x16 : 0x02));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength2);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+
+ break;
+ }
+ if (accept)
+ break;
+ }
+ } break;
+
+ case DOZTFILTER_DROP:
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+ return false;
+
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ accept = 1;
+ break;
+
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2;
+ break;
+ }
+
+ if (accept) {
+ if (membership)
+ membership->pushCredentials(RR,tPtr,now,ztDest,_config,localCapabilityIndex,false);
+
+ if ((!noTee)&&(cc)) {
+ Membership &m2 = _membership(cc);
+ m2.pushCredentials(RR,tPtr,now,cc,_config,localCapabilityIndex,false);
+
+ Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch ? 0x16 : 0x02));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+
+ if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
+ Membership &m2 = _membership(ztFinalDest);
+ m2.pushCredentials(RR,tPtr,now,ztFinalDest,_config,localCapabilityIndex,false);
+
+ Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)0x04);
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,frameLen);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+ return false; // DROP locally, since we redirected
+ } else {
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,1);
+ return true;
+ }
+ } else {
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0);
+ return false;
+ }
+}
+
+int Network::filterIncomingPacket(
+ void *tPtr,
+ const SharedPtr<Peer> &sourcePeer,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId)
+{
+ Address ztFinalDest(ztDest);
+ Trace::RuleResultLog rrl,crrl;
+ int accept = 0;
+ Address cc;
+ unsigned int ccLength = 0;
+ bool ccWatch = false;
+ const Capability *c = (Capability *)0;
+
+ Mutex::Lock _l(_lock);
+
+ Membership &membership = _membership(sourcePeer->address());
+
+ switch (_doZtFilter(RR,rrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+
+ case DOZTFILTER_NO_MATCH: {
+ Membership::CapabilityIterator mci(membership,_config);
+ while ((c = mci.next())) {
+ ztFinalDest = ztDest; // sanity check, should be unmodified if there was no match
+ Address cc2;
+ unsigned int ccLength2 = 0;
+ bool ccWatch2 = false;
+ switch(_doZtFilter(RR,crrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2)) {
+ case DOZTFILTER_NO_MATCH:
+ case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
+ break;
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ accept = 1; // ACCEPT
+ break;
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2; // super-ACCEPT
+ break;
+ }
+
+ if (accept) {
+ if (cc2) {
+ _membership(cc2).pushCredentials(RR,tPtr,RR->node->now(),cc2,_config,-1,false);
+
+ Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch2 ? 0x1c : 0x08));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength2);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+ break;
+ }
+ }
+ } break;
+
+ case DOZTFILTER_DROP:
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0);
+ return 0; // DROP
+
+ case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
+ case DOZTFILTER_ACCEPT:
+ accept = 1; // ACCEPT
+ break;
+ case DOZTFILTER_SUPER_ACCEPT:
+ accept = 2; // super-ACCEPT
+ break;
+ }
+
+ if (accept) {
+ if (cc) {
+ _membership(cc).pushCredentials(RR,tPtr,RR->node->now(),cc,_config,-1,false);
+
+ Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)(ccWatch ? 0x1c : 0x08));
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,ccLength);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ }
+
+ if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
+ _membership(ztFinalDest).pushCredentials(RR,tPtr,RR->node->now(),ztFinalDest,_config,-1,false);
+
+ Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(_id);
+ outp.append((uint8_t)0x0a);
+ macDest.appendTo(outp);
+ macSource.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(frameData,frameLen);
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0);
+ return 0; // DROP locally, since we redirected
+ }
+ }
+
+ if (_config.remoteTraceTarget)
+ RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,accept);
+ return accept;
+}
+
bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const
{
Mutex::Lock _l(_lock);
@@ -110,150 +864,419 @@ bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBr
return true;
else if (includeBridgedGroups)
return _multicastGroupsBehindMe.contains(mg);
- else return false;
+ return false;
}
-void Network::multicastSubscribe(const MulticastGroup &mg)
+void Network::multicastSubscribe(void *tPtr,const MulticastGroup &mg)
{
- {
- Mutex::Lock _l(_lock);
- if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg))
- return;
- _myMulticastGroups.push_back(mg);
- std::sort(_myMulticastGroups.begin(),_myMulticastGroups.end());
+ Mutex::Lock _l(_lock);
+ if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
+ _myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
+ _sendUpdatesToMembers(tPtr,&mg);
}
- _announceMulticastGroups();
}
void Network::multicastUnsubscribe(const MulticastGroup &mg)
{
Mutex::Lock _l(_lock);
- std::vector<MulticastGroup> nmg;
- for(std::vector<MulticastGroup>::const_iterator i(_myMulticastGroups.begin());i!=_myMulticastGroups.end();++i) {
- if (*i != mg)
- nmg.push_back(*i);
- }
- if (nmg.size() != _myMulticastGroups.size())
- _myMulticastGroups.swap(nmg);
+ std::vector<MulticastGroup>::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg));
+ if ( (i != _myMulticastGroups.end()) && (*i == mg) )
+ _myMulticastGroups.erase(i);
}
-bool Network::tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer)
+uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
{
- Mutex::Lock _l(_lock);
- if (
- (_isAllowed(peer)) ||
- (peer->address() == this->controller()) ||
- (RR->topology->isRoot(peer->identity()))
- ) {
- _announceMulticastGroupsTo(peer,_allMulticastGroups());
- return true;
- }
- return false;
-}
+ if (_destroyed)
+ return 0;
-bool Network::applyConfiguration(const NetworkConfig &conf)
-{
- if (_destroyed) // sanity check
- return false;
- try {
- if ((conf.networkId == _id)&&(conf.issuedTo == RR->identity.address())) {
- ZT_VirtualNetworkConfig ctmp;
- bool portInitialized;
- {
- Mutex::Lock _l(_lock);
- _config = conf;
- _lastConfigUpdate = RR->node->now();
- _netconfFailure = NETCONF_FAILURE_NONE;
- _externalConfig(&ctmp);
- portInitialized = _portInitialized;
- _portInitialized = true;
+ const unsigned int start = ptr;
+
+ ptr += 8; // skip network ID, which is already obviously known
+ const unsigned int chunkLen = chunk.at<uint16_t>(ptr); ptr += 2;
+ const void *chunkData = chunk.field(ptr,chunkLen); ptr += chunkLen;
+
+ NetworkConfig *nc = (NetworkConfig *)0;
+ uint64_t configUpdateId;
+ {
+ Mutex::Lock _l(_lock);
+
+ _IncomingConfigChunk *c = (_IncomingConfigChunk *)0;
+ uint64_t chunkId = 0;
+ unsigned long totalLength,chunkIndex;
+ if (ptr < chunk.size()) {
+ const bool fastPropagate = ((chunk[ptr++] & 0x01) != 0);
+ configUpdateId = chunk.at<uint64_t>(ptr); ptr += 8;
+ totalLength = chunk.at<uint32_t>(ptr); ptr += 4;
+ chunkIndex = chunk.at<uint32_t>(ptr); ptr += 4;
+
+ if (((chunkIndex + chunkLen) > totalLength)||(totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)) // >= since we need room for a null at the end
+ return 0;
+ if ((chunk[ptr] != 1)||(chunk.at<uint16_t>(ptr + 1) != ZT_C25519_SIGNATURE_LEN))
+ return 0;
+ const uint8_t *sig = reinterpret_cast<const uint8_t *>(chunk.field(ptr + 3,ZT_C25519_SIGNATURE_LEN));
+
+ // We can use the signature, which is unique per chunk, to get a per-chunk ID for local deduplication use
+ for(unsigned int i=0;i<16;++i)
+ reinterpret_cast<uint8_t *>(&chunkId)[i & 7] ^= sig[i];
+
+ // Find existing or new slot for this update and check if this is a duplicate chunk
+ for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) {
+ if (_incomingConfigChunks[i].updateId == configUpdateId) {
+ c = &(_incomingConfigChunks[i]);
+
+ for(unsigned long j=0;j<c->haveChunks;++j) {
+ if (c->haveChunkIds[j] == chunkId)
+ return 0;
+ }
+
+ break;
+ } else if ((!c)||(_incomingConfigChunks[i].ts < c->ts)) {
+ c = &(_incomingConfigChunks[i]);
+ }
+ }
+
+ // If it's not a duplicate, check chunk signature
+ const Identity controllerId(RR->topology->getIdentity(tPtr,controller()));
+ if (!controllerId) // we should always have the controller identity by now, otherwise how would we have queried it the first time?
+ return 0;
+ if (!controllerId.verify(chunk.field(start,ptr - start),ptr - start,sig,ZT_C25519_SIGNATURE_LEN))
+ return 0;
+
+ // New properly verified chunks can be flooded "virally" through the network
+ if (fastPropagate) {
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if ((*a != source)&&(*a != controller())) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CONFIG);
+ outp.append(reinterpret_cast<const uint8_t *>(chunk.data()) + start,chunk.size() - start);
+ RR->sw->send(tPtr,outp,true);
+ }
+ }
+ }
+ } else if ((source == controller())||(!source)) { // since old chunks aren't signed, only accept from controller itself (or via cluster backplane)
+ // Legacy support for OK(NETWORK_CONFIG_REQUEST) from older controllers
+ chunkId = packetId;
+ configUpdateId = chunkId;
+ totalLength = chunkLen;
+ chunkIndex = 0;
+
+ if (totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)
+ return 0;
+
+ for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) {
+ if ((!c)||(_incomingConfigChunks[i].ts < c->ts))
+ c = &(_incomingConfigChunks[i]);
}
- _portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(portInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
- return true;
} else {
- TRACE("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id);
+ // Single-chunk unsigned legacy configs are only allowed from the controller itself
+ return 0;
+ }
+
+ ++c->ts; // newer is higher, that's all we need
+
+ if (c->updateId != configUpdateId) {
+ c->updateId = configUpdateId;
+ c->haveChunks = 0;
+ c->haveBytes = 0;
+ }
+ if (c->haveChunks >= ZT_NETWORK_MAX_UPDATE_CHUNKS)
+ return false;
+ c->haveChunkIds[c->haveChunks++] = chunkId;
+
+ ZT_FAST_MEMCPY(c->data.unsafeData() + chunkIndex,chunkData,chunkLen);
+ c->haveBytes += chunkLen;
+
+ if (c->haveBytes == totalLength) {
+ c->data.unsafeData()[c->haveBytes] = (char)0; // ensure null terminated
+
+ nc = new NetworkConfig();
+ try {
+ if (!nc->fromDictionary(c->data)) {
+ delete nc;
+ nc = (NetworkConfig *)0;
+ }
+ } catch ( ... ) {
+ delete nc;
+ nc = (NetworkConfig *)0;
+ }
}
- } catch (std::exception &exc) {
- TRACE("ignored invalid configuration for network %.16llx (%s)",(unsigned long long)_id,exc.what());
- } catch ( ... ) {
- TRACE("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
}
- return false;
+
+ if (nc) {
+ this->setConfiguration(tPtr,*nc,true);
+ delete nc;
+ return configUpdateId;
+ } else {
+ return 0;
+ }
+
+ return 0;
}
-int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
+int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk)
{
+ if (_destroyed)
+ return 0;
+
+ // _lock is NOT locked when this is called
try {
- {
+ if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id))
+ return 0; // invalid config that is not for us or not for this network
+ if (_config == nconf)
+ return 1; // OK config, but duplicate of what we already have
+
+ ZT_VirtualNetworkConfig ctmp;
+ bool oldPortInitialized;
+ { // do things that require lock here, but unlock before calling callbacks
Mutex::Lock _l(_lock);
- if (_config == nconf)
- return 1; // OK config, but duplicate of what we already have
+
+ _config = nconf;
+ _lastConfigUpdate = RR->node->now();
+ _netconfFailure = NETCONF_FAILURE_NONE;
+
+ oldPortInitialized = _portInitialized;
+ _portInitialized = true;
+
+ _externalConfig(&ctmp);
+
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m))
+ m->resetPushState();
}
- if (applyConfiguration(nconf)) {
- if (saveToDisk) {
- char n[64];
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> d;
- if (nconf.toDictionary(d,false))
- RR->node->dataStorePut(n,(const void *)d.data(),d.sizeBytes(),true);
- }
- return 2; // OK and configuration has changed
+
+ _portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
+
+ if (saveToDisk) {
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ try {
+ if (nconf.toDictionary(*d,false)) {
+ uint64_t tmp[2];
+ tmp[0] = _id; tmp[1] = 0;
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,d->data(),d->sizeBytes());
+ }
+ } catch ( ... ) {}
+ delete d;
}
- } catch ( ... ) {
- TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
- }
+
+ return 2; // OK and configuration has changed
+ } catch ( ... ) {} // ignore invalid configs
return 0;
}
-void Network::requestConfiguration()
+void Network::requestConfiguration(void *tPtr)
{
- if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, uses locally generated static config
+ if (_destroyed)
return;
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> rmd;
+ if ((_id >> 56) == 0xff) {
+ if ((_id & 0xffffff) == 0) {
+ const uint16_t startPortRange = (uint16_t)((_id >> 40) & 0xffff);
+ const uint16_t endPortRange = (uint16_t)((_id >> 24) & 0xffff);
+ if (endPortRange >= startPortRange) {
+ NetworkConfig *const nconf = new NetworkConfig();
+
+ nconf->networkId = _id;
+ nconf->timestamp = RR->node->now();
+ nconf->credentialTimeMaxDelta = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ nconf->revision = 1;
+ nconf->issuedTo = RR->identity.address();
+ nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ nconf->mtu = ZT_DEFAULT_MTU;
+ nconf->multicastLimit = 0;
+ nconf->staticIpCount = 1;
+ nconf->ruleCount = 14;
+ nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,RR->identity.address().toInt());
+
+ // Drop everything but IPv6
+ nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE | 0x80; // NOT
+ nconf->rules[0].v.etherType = 0x86dd; // IPv6
+ nconf->rules[1].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+
+ // Allow ICMPv6
+ nconf->rules[2].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ nconf->rules[2].v.ipProtocol = 0x3a; // ICMPv6
+ nconf->rules[3].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Allow destination ports within range
+ nconf->rules[4].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL;
+ nconf->rules[4].v.ipProtocol = 0x11; // UDP
+ nconf->rules[5].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_PROTOCOL | 0x40; // OR
+ nconf->rules[5].v.ipProtocol = 0x06; // TCP
+ nconf->rules[6].t = (uint8_t)ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE;
+ nconf->rules[6].v.port[0] = startPortRange;
+ nconf->rules[6].v.port[1] = endPortRange;
+ nconf->rules[7].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Allow non-SYN TCP packets to permit non-connection-initiating traffic
+ nconf->rules[8].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS | 0x80; // NOT
+ nconf->rules[8].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
+ nconf->rules[9].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ // Also allow SYN+ACK which are replies to SYN
+ nconf->rules[10].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ nconf->rules[10].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_SYN;
+ nconf->rules[11].t = (uint8_t)ZT_NETWORK_RULE_MATCH_CHARACTERISTICS;
+ nconf->rules[11].v.characteristics = ZT_RULE_PACKET_CHARACTERISTICS_TCP_ACK;
+ nconf->rules[12].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
+
+ nconf->type = ZT_NETWORK_TYPE_PUBLIC;
+
+ nconf->name[0] = 'a';
+ nconf->name[1] = 'd';
+ nconf->name[2] = 'h';
+ nconf->name[3] = 'o';
+ nconf->name[4] = 'c';
+ nconf->name[5] = '-';
+ Utils::hex((uint16_t)startPortRange,nconf->name + 6);
+ nconf->name[10] = '-';
+ Utils::hex((uint16_t)endPortRange,nconf->name + 11);
+ nconf->name[15] = (char)0;
+
+ this->setConfiguration(tPtr,*nconf,false);
+ delete nconf;
+ } else {
+ this->setNotFound();
+ }
+ } else if ((_id & 0xff) == 0x01) {
+ // ffAAaaaaaaaaaa01 -- where AA is the IPv4 /8 to use and aaaaaaaaaa is the anchor node for multicast gather and replication
+ const uint64_t myAddress = RR->identity.address().toInt();
+ const uint64_t networkHub = (_id >> 8) & 0xffffffffffULL;
+
+ uint8_t ipv4[4];
+ ipv4[0] = (uint8_t)((_id >> 48) & 0xff);
+ ipv4[1] = (uint8_t)((myAddress >> 16) & 0xff);
+ ipv4[2] = (uint8_t)((myAddress >> 8) & 0xff);
+ ipv4[3] = (uint8_t)(myAddress & 0xff);
+
+ char v4ascii[24];
+ Utils::decimal(ipv4[0],v4ascii);
+
+ NetworkConfig *const nconf = new NetworkConfig();
+
+ nconf->networkId = _id;
+ nconf->timestamp = RR->node->now();
+ nconf->credentialTimeMaxDelta = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA;
+ nconf->revision = 1;
+ nconf->issuedTo = RR->identity.address();
+ nconf->flags = ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION;
+ nconf->mtu = ZT_DEFAULT_MTU;
+ nconf->multicastLimit = 1024;
+ nconf->specialistCount = (networkHub == 0) ? 0 : 1;
+ nconf->staticIpCount = 2;
+ nconf->ruleCount = 1;
+
+ if (networkHub != 0)
+ nconf->specialists[0] = networkHub;
+
+ nconf->staticIps[0] = InetAddress::makeIpv66plane(_id,myAddress);
+ nconf->staticIps[1].set(ipv4,4,8);
+
+ nconf->rules[0].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
+
+ nconf->type = ZT_NETWORK_TYPE_PUBLIC;
+
+ nconf->name[0] = 'a';
+ nconf->name[1] = 'd';
+ nconf->name[2] = 'h';
+ nconf->name[3] = 'o';
+ nconf->name[4] = 'c';
+ nconf->name[5] = '-';
+ unsigned long nn = 6;
+ while ((nconf->name[nn] = v4ascii[nn - 6])) ++nn;
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = '.';
+ nconf->name[nn++] = '0';
+ nconf->name[nn++] = (char)0;
+
+ this->setConfiguration(tPtr,*nconf,false);
+ delete nconf;
+ }
+ return;
+ }
+
+ const Address ctrl(controller());
+
+ Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> rmd;
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR,(uint64_t)ZT_VENDOR_ZEROTIER);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION,(uint64_t)ZT_PROTO_VERSION);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MAJOR);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,(uint64_t)ZEROTIER_ONE_VERSION_MINOR);
rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,(uint64_t)ZEROTIER_ONE_VERSION_REVISION);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES,(uint64_t)ZT_MAX_NETWORK_RULES);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES,(uint64_t)ZT_MAX_NETWORK_CAPABILITIES);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES,(uint64_t)ZT_MAX_CAPABILITY_RULES);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS,(uint64_t)ZT_MAX_NETWORK_TAGS);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS,(uint64_t)0);
+ rmd.add(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV,(uint64_t)ZT_RULES_ENGINE_REVISION);
- if (controller() == RR->identity.address()) {
+ RR->t->networkConfigRequestSent(tPtr,*this,ctrl);
+
+ if (ctrl == RR->identity.address()) {
if (RR->localNetworkController) {
- NetworkConfig nconf;
- switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,nconf)) {
- case NetworkController::NETCONF_QUERY_OK:
- this->setConfiguration(nconf,true);
- return;
- case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND:
- this->setNotFound();
- return;
- case NetworkController::NETCONF_QUERY_ACCESS_DENIED:
- this->setAccessDenied();
- return;
- default:
- return;
- }
+ RR->localNetworkController->request(_id,InetAddress(),0xffffffffffffffffULL,RR->identity,rmd);
} else {
this->setNotFound();
- return;
}
+ return;
}
- TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,controller().toString().c_str());
-
- Packet outp(controller(),RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
+ Packet outp(ctrl,RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append((uint64_t)_id);
const unsigned int rmdSize = rmd.sizeBytes();
outp.append((uint16_t)rmdSize);
outp.append((const void *)rmd.data(),rmdSize);
- outp.append((_config) ? (uint64_t)_config.revision : (uint64_t)0);
+ if (_config) {
+ outp.append((uint64_t)_config.revision);
+ outp.append((uint64_t)_config.timestamp);
+ } else {
+ outp.append((unsigned char)0,16);
+ }
outp.compress();
- RR->sw->send(outp,true,0);
+ RR->node->expectReplyTo(outp.packetId());
+ RR->sw->send(tPtr,outp,true);
+}
+
+bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
+{
+ const int64_t now = RR->node->now();
+ Mutex::Lock _l(_lock);
+ try {
+ if (_config) {
+ Membership *m = _memberships.get(peer->address());
+ if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config))) ) {
+ if (!m)
+ m = &(_membership(peer->address()));
+ if (m->multicastLikeGate(now)) {
+ m->pushCredentials(RR,tPtr,now,peer->address(),_config,-1,false);
+ _announceMulticastGroupsTo(tPtr,peer->address(),_allMulticastGroups());
+ }
+ return true;
+ }
+ }
+ } catch ( ... ) {}
+ return false;
+}
+
+bool Network::recentlyAssociatedWith(const Address &addr)
+{
+ Mutex::Lock _l(_lock);
+ const Membership *m = _memberships.get(addr);
+ return ((m)&&(m->recentlyAssociated(RR->node->now())));
}
void Network::clean()
{
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
Mutex::Lock _l(_lock);
if (_destroyed)
@@ -268,6 +1291,17 @@ void Network::clean()
_multicastGroupsBehindMe.erase(*mg);
}
}
+
+ {
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if (!RR->topology->getPeerNoCache(*a))
+ _memberships.erase(*a);
+ else m->clean(now,_config);
+ }
+ }
}
void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
@@ -307,13 +1341,59 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
}
}
-void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
+void Network::learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now)
{
Mutex::Lock _l(_lock);
const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
_multicastGroupsBehindMe.set(mg,now);
if (tmp != _multicastGroupsBehindMe.size())
- _announceMulticastGroups();
+ _sendUpdatesToMembers(tPtr,&mg);
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const CertificateOfMembership &com)
+{
+ if (com.networkId() != _id)
+ return Membership::ADD_REJECTED;
+ const Address a(com.issuedTo());
+ Mutex::Lock _l(_lock);
+ Membership &m = _membership(a);
+ const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,com);
+ if ((result == Membership::ADD_ACCEPTED_NEW)||(result == Membership::ADD_ACCEPTED_REDUNDANT)) {
+ m.pushCredentials(RR,tPtr,RR->node->now(),a,_config,-1,false);
+ RR->mc->addCredential(tPtr,com,true);
+ }
+ return result;
+}
+
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev)
+{
+ if (rev.networkId() != _id)
+ return Membership::ADD_REJECTED;
+
+ Mutex::Lock _l(_lock);
+ Membership &m = _membership(rev.target());
+
+ const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,rev);
+
+ if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if ((*a != sentFrom)&&(*a != rev.signer())) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ outp.append((uint8_t)0x00); // no COM
+ outp.append((uint16_t)0); // no capabilities
+ outp.append((uint16_t)0); // no tags
+ outp.append((uint16_t)1); // one revocation!
+ rev.serialize(outp);
+ outp.append((uint16_t)0); // no certificates of ownership
+ RR->sw->send(tPtr,outp,true);
+ }
+ }
+ }
+
+ return result;
}
void Network::destroy()
@@ -349,10 +1429,10 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
else ec->name[0] = (char)0;
ec->status = _status();
ec->type = (_config) ? (_config.isPrivate() ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC) : ZT_NETWORK_TYPE_PRIVATE;
- ec->mtu = ZT_IF_MTU;
+ ec->mtu = (_config) ? _config.mtu : ZT_DEFAULT_MTU;
ec->dhcp = 0;
std::vector<Address> ab(_config.activeBridges());
- ec->bridge = ((_config.allowPassiveBridging())||(std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end())) ? 1 : 0;
+ ec->bridge = (std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end()) ? 1 : 0;
ec->broadcastEnabled = (_config) ? (_config.enableBroadcast() ? 1 : 0) : 0;
ec->portError = _portError;
ec->netconfRevision = (_config) ? (unsigned long)_config.revision : 0;
@@ -360,7 +1440,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->assignedAddressCount = 0;
for(unsigned int i=0;i<ZT_MAX_ZT_ASSIGNED_ADDRESSES;++i) {
if (i < _config.staticIpCount) {
- memcpy(&(ec->assignedAddresses[i]),&(_config.staticIps[i]),sizeof(struct sockaddr_storage));
+ ZT_FAST_MEMCPY(&(ec->assignedAddresses[i]),&(_config.staticIps[i]),sizeof(struct sockaddr_storage));
++ec->assignedAddressCount;
} else {
memset(&(ec->assignedAddresses[i]),0,sizeof(struct sockaddr_storage));
@@ -370,7 +1450,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
ec->routeCount = 0;
for(unsigned int i=0;i<ZT_MAX_NETWORK_ROUTES;++i) {
if (i < _config.routeCount) {
- memcpy(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
+ ZT_FAST_MEMCPY(&(ec->routes[i]),&(_config.routes[i]),sizeof(ZT_VirtualNetworkRoute));
++ec->routeCount;
} else {
memset(&(ec->routes[i]),0,sizeof(ZT_VirtualNetworkRoute));
@@ -378,96 +1458,87 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
}
}
-bool Network::_isAllowed(const SharedPtr<Peer> &peer) const
+void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
{
// Assumes _lock is locked
- try {
- if (!_config)
- return false;
- if (_config.isPublic())
- return true;
- return ((_config.com)&&(peer->networkMembershipCertificatesAgree(_id,_config.com)));
- } catch (std::exception &exc) {
- TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer->address().toString().c_str(),exc.what());
- } catch ( ... ) {
- TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer->address().toString().c_str());
+ const int64_t now = RR->node->now();
+
+ std::vector<MulticastGroup> groups;
+ if (newMulticastGroup)
+ groups.push_back(*newMulticastGroup);
+ else groups = _allMulticastGroups();
+
+ std::vector<Address> alwaysAnnounceTo;
+
+ if ((newMulticastGroup)||((now - _lastAnnouncedMulticastGroupsUpstream) >= ZT_MULTICAST_ANNOUNCE_PERIOD)) {
+ if (!newMulticastGroup)
+ _lastAnnouncedMulticastGroupsUpstream = now;
+
+ alwaysAnnounceTo = _config.alwaysContactAddresses();
+ if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),controller()) == alwaysAnnounceTo.end())
+ alwaysAnnounceTo.push_back(controller());
+ const std::vector<Address> upstreams(RR->topology->upstreamAddresses());
+ for(std::vector<Address>::const_iterator a(upstreams.begin());a!=upstreams.end();++a) {
+ if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a) == alwaysAnnounceTo.end())
+ alwaysAnnounceTo.push_back(*a);
+ }
+ std::sort(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end());
+
+ for(std::vector<Address>::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a) {
+ // push COM to non-members so they can do multicast request auth
+ if ( (_config.com) && (!_memberships.contains(*a)) && (*a != RR->identity.address()) ) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ _config.com.serialize(outp);
+ outp.append((uint8_t)0x00);
+ outp.append((uint16_t)0); // no capabilities
+ outp.append((uint16_t)0); // no tags
+ outp.append((uint16_t)0); // no revocations
+ outp.append((uint16_t)0); // no certificates of ownership
+ RR->sw->send(tPtr,outp,true);
+ }
+ _announceMulticastGroupsTo(tPtr,*a,groups);
+ }
}
- return false; // default position on any failure
-}
-class _MulticastAnnounceAll
-{
-public:
- _MulticastAnnounceAll(const RuntimeEnvironment *renv,Network *nw) :
- _now(renv->node->now()),
- _controller(nw->controller()),
- _network(nw),
- _anchors(nw->config().anchors()),
- _rootAddresses(renv->topology->rootAddresses())
- {}
- inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
- if ( (_network->_isAllowed(p)) || // FIXME: this causes multicast LIKEs for public networks to get spammed
- (p->address() == _controller) ||
- (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ||
- (std::find(_anchors.begin(),_anchors.end(),p->address()) != _anchors.end()) ) {
- peers.push_back(p);
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ m->pushCredentials(RR,tPtr,now,*a,_config,-1,false);
+ if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) )
+ _announceMulticastGroupsTo(tPtr,*a,groups);
}
}
- std::vector< SharedPtr<Peer> > peers;
-private:
- const uint64_t _now;
- const Address _controller;
- Network *const _network;
- const std::vector<Address> _anchors;
- const std::vector<Address> _rootAddresses;
-};
-void Network::_announceMulticastGroups()
-{
- // Assumes _lock is locked
- std::vector<MulticastGroup> allMulticastGroups(_allMulticastGroups());
- _MulticastAnnounceAll gpfunc(RR,this);
- RR->topology->eachPeer<_MulticastAnnounceAll &>(gpfunc);
- for(std::vector< SharedPtr<Peer> >::const_iterator i(gpfunc.peers.begin());i!=gpfunc.peers.end();++i)
- _announceMulticastGroupsTo(*i,allMulticastGroups);
}
-void Network::_announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const
+void Network::_announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups)
{
// Assumes _lock is locked
+ Packet outp(peer,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
- // We push COMs ahead of MULTICAST_LIKE since they're used for access control -- a COM is a public
- // credential so "over-sharing" isn't really an issue (and we only do so with roots).
- if ((_config)&&(_config.com)&&(!_config.isPublic())&&(peer->needsOurNetworkMembershipCertificate(_id,RR->node->now(),true))) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
- _config.com.serialize(outp);
- RR->sw->send(outp,true,0);
- }
-
- {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
-
- for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
- if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) {
- RR->sw->send(outp,true,0);
- outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
- }
-
- // network ID, MAC, ADI
- outp.append((uint64_t)_id);
- mg->mac().appendTo(outp);
- outp.append((uint32_t)mg->adi());
+ for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
+ if ((outp.size() + 24) >= ZT_PROTO_MAX_PACKET_LENGTH) {
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+ outp.reset(peer,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
}
- if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH)
- RR->sw->send(outp,true,0);
+ // network ID, MAC, ADI
+ outp.append((uint64_t)_id);
+ mg->mac().appendTo(outp);
+ outp.append((uint32_t)mg->adi());
+ }
+
+ if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) {
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
}
}
std::vector<MulticastGroup> Network::_allMulticastGroups() const
{
// Assumes _lock is locked
-
std::vector<MulticastGroup> mgs;
mgs.reserve(_myMulticastGroups.size() + _multicastGroupsBehindMe.size() + 1);
mgs.insert(mgs.end(),_myMulticastGroups.begin(),_myMulticastGroups.end());
@@ -476,8 +1547,13 @@ std::vector<MulticastGroup> Network::_allMulticastGroups() const
mgs.push_back(Network::BROADCAST);
std::sort(mgs.begin(),mgs.end());
mgs.erase(std::unique(mgs.begin(),mgs.end()),mgs.end());
-
return mgs;
}
+Membership &Network::_membership(const Address &a)
+{
+ // assumes _lock is locked
+ return _memberships[a];
+}
+
} // namespace ZeroTier
diff --git a/node/Network.hpp b/node/Network.hpp
index 17eed4bd..95b5483a 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORK_HPP
@@ -30,7 +38,6 @@
#include <stdexcept>
#include "Constants.hpp"
-#include "NonCopyable.hpp"
#include "Hashtable.hpp"
#include "Address.hpp"
#include "Mutex.hpp"
@@ -40,22 +47,24 @@
#include "MAC.hpp"
#include "Dictionary.hpp"
#include "Multicaster.hpp"
+#include "Membership.hpp"
#include "NetworkConfig.hpp"
#include "CertificateOfMembership.hpp"
+#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
+#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
+
namespace ZeroTier {
class RuntimeEnvironment;
class Peer;
-class _MulticastAnnounceAll;
/**
* A virtual LAN
*/
-class Network : NonCopyable
+class Network
{
friend class SharedPtr<Network>;
- friend class _MulticastAnnounceAll; // internal function object
public:
/**
@@ -64,56 +73,102 @@ public:
static const MulticastGroup BROADCAST;
/**
+ * Compute primary controller device ID from network ID
+ */
+ static inline Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); }
+
+ /**
* Construct a new network
*
* Note that init() should be called immediately after the network is
* constructed to actually configure the port.
*
* @param renv Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param nwid Network ID
* @param uptr Arbitrary pointer used by externally-facing API (for user use)
+ * @param nconf Network config, if known
*/
- Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr);
+ Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf);
~Network();
- /**
- * @return Network ID
- */
- inline uint64_t id() const throw() { return _id; }
-
- /**
- * @return Address of network's controller (most significant 40 bits of ID)
- */
- inline Address controller() const throw() { return Address(_id >> 24); }
+ inline uint64_t id() const { return _id; }
+ inline Address controller() const { return Address(_id >> 24); }
+ inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
+ inline bool hasConfig() const { return (_config); }
+ inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; }
+ inline ZT_VirtualNetworkStatus status() const { Mutex::Lock _l(_lock); return _status(); }
+ inline const NetworkConfig &config() const { return _config; }
+ inline const MAC &mac() const { return _mac; }
/**
- * @param nwid Network ID
- * @return Address of network's controller
+ * Apply filters to an outgoing packet
+ *
+ * This applies filters from our network config and, if that doesn't match,
+ * our capabilities in ascending order of capability ID. Additional actions
+ * such as TEE may be taken, and credentials may be pushed, so this is not
+ * side-effect-free. It's basically step one in sending something over VL2.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param noTee If true, do not TEE anything anywhere (for two-pass filtering as done with multicast and bridging)
+ * @param ztSource Source ZeroTier address
+ * @param ztDest Destination ZeroTier address
+ * @param macSource Ethernet layer source address
+ * @param macDest Ethernet layer destination address
+ * @param frameData Ethernet frame data
+ * @param frameLen Ethernet frame payload length
+ * @param etherType 16-bit ethernet type ID
+ * @param vlanId 16-bit VLAN ID
+ * @return True if packet should be sent, false if dropped or redirected
*/
- static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
+ bool filterOutgoingPacket(
+ void *tPtr,
+ const bool noTee,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId);
/**
- * @return Multicast group memberships for this network's port (local, not learned via bridging)
+ * Apply filters to an incoming packet
+ *
+ * This applies filters from our network config and, if that doesn't match,
+ * the peer's capabilities in ascending order of capability ID. If there is
+ * a match certain actions may be taken such as sending a copy of the packet
+ * to a TEE or REDIRECT target.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param sourcePeer Source Peer
+ * @param ztDest Destination ZeroTier address
+ * @param macSource Ethernet layer source address
+ * @param macDest Ethernet layer destination address
+ * @param frameData Ethernet frame data
+ * @param frameLen Ethernet frame payload length
+ * @param etherType 16-bit ethernet type ID
+ * @param vlanId 16-bit VLAN ID
+ * @return 0 == drop, 1 == accept, 2 == accept even if bridged
*/
- inline std::vector<MulticastGroup> multicastGroups() const
- {
- Mutex::Lock _l(_lock);
- return _myMulticastGroups;
- }
-
- /**
- * @return All multicast groups including learned groups that are behind any bridges we're attached to
- */
- inline std::vector<MulticastGroup> allMulticastGroups() const
- {
- Mutex::Lock _l(_lock);
- return _allMulticastGroups();
- }
+ int filterIncomingPacket(
+ void *tPtr,
+ const SharedPtr<Peer> &sourcePeer,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId);
/**
+ * Check whether we are subscribed to a multicast group
+ *
* @param mg Multicast group
- * @param includeBridgedGroups If true, also include any groups we've learned via bridging
+ * @param includeBridgedGroups If true, also check groups we've learned via bridging
* @return True if this network endpoint / peer is a member
*/
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
@@ -121,9 +176,10 @@ public:
/**
* Subscribe to a multicast group
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param mg New multicast group
*/
- void multicastSubscribe(const MulticastGroup &mg);
+ void multicastSubscribe(void *tPtr,const MulticastGroup &mg);
/**
* Unsubscribe from a multicast group
@@ -133,29 +189,30 @@ public:
void multicastUnsubscribe(const MulticastGroup &mg);
/**
- * Announce multicast groups to a peer if that peer is authorized on this network
+ * Handle an inbound network config chunk
*
- * @param peer Peer to try to announce multicast groups to
- * @return True if peer was authorized and groups were announced
- */
- bool tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer);
-
- /**
- * Apply a NetworkConfig to this network
+ * This is called from IncomingPacket to handle incoming network config
+ * chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
+ * each chunk and once assembled applies the configuration.
*
- * @param conf Configuration in NetworkConfig form
- * @return True if configuration was accepted
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param packetId Packet ID or 0 if none (e.g. via cluster path)
+ * @param source Address of sender of chunk or NULL if none (e.g. via cluster path)
+ * @param chunk Buffer containing chunk
+ * @param ptr Index of chunk and related fields in packet
+ * @return Update ID if update was fully assembled and accepted or 0 otherwise
*/
- bool applyConfiguration(const NetworkConfig &conf);
+ uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
/**
- * Set or update this network's configuration
+ * Set network configuration
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param nconf Network configuration
- * @param saveToDisk IF true (default), write config to disk
- * @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
+ * @param saveToDisk Save to disk? Used during loading, should usually be true otherwise.
+ * @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new
*/
- int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
+ int setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk);
/**
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
@@ -167,7 +224,7 @@ public:
}
/**
- * Set netconf failure to 'not found' -- called by PacketDecider when controller reports this
+ * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
*/
inline void setNotFound()
{
@@ -177,151 +234,194 @@ public:
/**
* Causes this network to request an updated configuration from its master node now
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
*/
- void requestConfiguration();
+ void requestConfiguration(void *tPtr);
/**
+ * Determine whether this peer is permitted to communicate on this network
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param peer Peer to check
- * @return True if peer is allowed to communicate on this network
*/
- inline bool isAllowed(const SharedPtr<Peer> &peer) const
- {
- Mutex::Lock _l(_lock);
- return _isAllowed(peer);
- }
+ bool gate(void *tPtr,const SharedPtr<Peer> &peer);
/**
- * Perform cleanup and possibly save state
+ * Check whether a given peer has recently had an association with this network
+ *
+ * This checks whether a peer has communicated with us recently about this
+ * network and has possessed a valid certificate of membership. This may return
+ * true even if the peer has been offline for a while or no longer has a valid
+ * certificate of membership but had one recently.
+ *
+ * @param addr Peer address
+ * @return True if peer has recently associated
*/
- void clean();
+ bool recentlyAssociatedWith(const Address &addr);
/**
- * @return Time of last updated configuration or 0 if none
+ * Do periodic cleanup and housekeeping tasks
*/
- inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
+ void clean();
/**
- * @return Status of this network
+ * Push state to members such as multicast group memberships and latest COM (if needed)
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
*/
- inline ZT_VirtualNetworkStatus status() const
+ inline void sendUpdatesToMembers(void *tPtr)
{
Mutex::Lock _l(_lock);
- return _status();
+ _sendUpdatesToMembers(tPtr,(const MulticastGroup *)0);
}
/**
- * @param ec Buffer to fill with externally-visible network configuration
+ * Find the node on this network that has this MAC behind it (if any)
+ *
+ * @param mac MAC address
+ * @return ZeroTier address of bridge to this MAC
*/
- inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
+ inline Address findBridgeTo(const MAC &mac) const
{
Mutex::Lock _l(_lock);
- _externalConfig(ec);
+ const Address *const br = _remoteBridgeRoutes.get(mac);
+ return ((br) ? *br : Address());
}
/**
- * Get current network config
+ * Set a bridge route
*
- * This returns a const reference to the network config in place, which is safe
- * to concurrently access but *may* change during access. Normally this isn't a
- * problem, but if it is use configCopy().
+ * @param mac MAC address of destination
+ * @param addr Bridge this MAC is reachable behind
+ */
+ void learnBridgeRoute(const MAC &mac,const Address &addr);
+
+ /**
+ * Learn a multicast group that is bridged to our tap device
*
- * @return Network configuration (may be a null config if we don't have one yet)
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param mg Multicast group
+ * @param now Current time
*/
- inline const NetworkConfig &config() const { return _config; }
+ void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now);
/**
- * @return A thread-safe copy of our NetworkConfig instead of a const reference
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline NetworkConfig configCopy() const
+ Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com);
+
+ /**
+ * Validate a credential and learn it if it passes certificate and other checks
+ */
+ inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap)
{
+ if (cap.networkId() != _id)
+ return Membership::ADD_REJECTED;
Mutex::Lock _l(_lock);
- return _config;
+ return _membership(cap.issuedTo()).addCredential(RR,tPtr,_config,cap);
}
/**
- * @return True if this network has a valid config
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline bool hasConfig() const { return (_config); }
+ inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag)
+ {
+ if (tag.networkId() != _id)
+ return Membership::ADD_REJECTED;
+ Mutex::Lock _l(_lock);
+ return _membership(tag.issuedTo()).addCredential(RR,tPtr,_config,tag);
+ }
/**
- * @return Ethernet MAC address for this network's local interface
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline const MAC &mac() const throw() { return _mac; }
+ Membership::AddCredentialResult addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev);
/**
- * Find the node on this network that has this MAC behind it (if any)
- *
- * @param mac MAC address
- * @return ZeroTier address of bridge to this MAC
+ * Validate a credential and learn it if it passes certificate and other checks
*/
- inline Address findBridgeTo(const MAC &mac) const
+ inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo)
{
+ if (coo.networkId() != _id)
+ return Membership::ADD_REJECTED;
Mutex::Lock _l(_lock);
- const Address *const br = _remoteBridgeRoutes.get(mac);
- if (br)
- return *br;
- return Address();
+ return _membership(coo.issuedTo()).addCredential(RR,tPtr,_config,coo);
}
/**
- * Set a bridge route
- *
- * @param mac MAC address of destination
- * @param addr Bridge this MAC is reachable behind
- */
- void learnBridgeRoute(const MAC &mac,const Address &addr);
-
- /**
- * Learn a multicast group that is bridged to our tap device
+ * Force push credentials (COM, etc.) to a peer now
*
- * @param mg Multicast group
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param to Destination peer address
* @param now Current time
*/
- void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
+ inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now)
+ {
+ Mutex::Lock _l(_lock);
+ _membership(to).pushCredentials(RR,tPtr,now,to,_config,-1,true);
+ }
/**
* Destroy this network
*
- * This causes the network to disable itself, destroy its tap device, and on
- * delete to delete all trace of itself on disk and remove any persistent tap
- * device instances. Call this when a network is being removed from the system.
+ * This sets the network to completely remove itself on delete. This also prevents the
+ * call of the normal port shutdown event on delete.
*/
void destroy();
/**
- * @return Pointer to user PTR (modifiable user ptr used in API)
+ * Get this network's config for export via the ZT core API
+ *
+ * @param ec Buffer to fill with externally-visible network configuration
*/
- inline void **userPtr() throw() { return &_uPtr; }
+ inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
+ {
+ Mutex::Lock _l(_lock);
+ _externalConfig(ec);
+ }
- inline bool operator==(const Network &n) const throw() { return (_id == n._id); }
- inline bool operator!=(const Network &n) const throw() { return (_id != n._id); }
- inline bool operator<(const Network &n) const throw() { return (_id < n._id); }
- inline bool operator>(const Network &n) const throw() { return (_id > n._id); }
- inline bool operator<=(const Network &n) const throw() { return (_id <= n._id); }
- inline bool operator>=(const Network &n) const throw() { return (_id >= n._id); }
+ /**
+ * @return Externally usable pointer-to-pointer exported via the core API
+ */
+ inline void **userPtr() { return &_uPtr; }
private:
ZT_VirtualNetworkStatus _status() const;
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
- bool _isAllowed(const SharedPtr<Peer> &peer) const;
- void _announceMulticastGroups();
- void _announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const;
+ bool _gate(const SharedPtr<Peer> &peer);
+ void _sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup);
+ void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
std::vector<MulticastGroup> _allMulticastGroups() const;
+ Membership &_membership(const Address &a);
- const RuntimeEnvironment *RR;
+ const RuntimeEnvironment *const RR;
void *_uPtr;
- uint64_t _id;
+ const uint64_t _id;
+ uint64_t _lastAnnouncedMulticastGroupsUpstream;
MAC _mac; // local MAC address
- volatile bool _portInitialized;
+ bool _portInitialized;
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
NetworkConfig _config;
- volatile uint64_t _lastConfigUpdate;
+ uint64_t _lastConfigUpdate;
- volatile bool _destroyed;
+ struct _IncomingConfigChunk
+ {
+ _IncomingConfigChunk() { memset(this,0,sizeof(_IncomingConfigChunk)); }
+ uint64_t ts;
+ uint64_t updateId;
+ uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
+ unsigned long haveChunks;
+ unsigned long haveBytes;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> data;
+ };
+ _IncomingConfigChunk _incomingConfigChunks[ZT_NETWORK_MAX_INCOMING_UPDATES];
+
+ bool _destroyed;
enum {
NETCONF_FAILURE_NONE,
@@ -329,7 +429,9 @@ private:
NETCONF_FAILURE_NOT_FOUND,
NETCONF_FAILURE_INIT_FAILED
} _netconfFailure;
- volatile int _portError; // return value from port config callback
+ int _portError; // return value from port config callback
+
+ Hashtable<Address,Membership> _memberships;
Mutex _lock;
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index 9d5c5f17..db051699 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,252 +14,185 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
+#include <algorithm>
+
#include "NetworkConfig.hpp"
-#include "Utils.hpp"
namespace ZeroTier {
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
{
- Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
-
- d.clear();
-
- // Try to put the more human-readable fields first
+ Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ char tmp2[128];
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
+ try {
+ d.clear();
+
+ // Try to put the more human-readable fields first
+
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET,this->remoteTraceTarget.toString(tmp2))) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL,(uint64_t)this->remoteTraceLevel)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,(uint64_t)this->mtu)) return false;
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
- if (includeLegacy) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
-
- std::string v4s;
- for(unsigned int i=0;i<staticIpCount;++i) {
- if (this->staticIps[i].ss_family == AF_INET) {
- if (v4s.length() > 0)
- v4s.push_back(',');
- v4s.append(this->staticIps[i].toString());
+ if (includeLegacy) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
+
+ std::string v4s;
+ for(unsigned int i=0;i<staticIpCount;++i) {
+ if (this->staticIps[i].ss_family == AF_INET) {
+ if (v4s.length() > 0)
+ v4s.push_back(',');
+ char buf[64];
+ v4s.append(this->staticIps[i].toString(buf));
+ }
}
- }
- if (v4s.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
- }
- std::string v6s;
- for(unsigned int i=0;i<staticIpCount;++i) {
- if (this->staticIps[i].ss_family == AF_INET6) {
- if (v6s.length() > 0)
- v6s.push_back(',');
- v6s.append(this->staticIps[i].toString());
+ if (v4s.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
}
- }
- if (v6s.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
- }
-
- std::string ets;
- unsigned int et = 0;
- ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
- for(unsigned int i=0;i<ruleCount;++i) {
- ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
- if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
- et = rules[i].v.etherType;
- } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
- if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
- if (ets.length() > 0)
- ets.push_back(',');
- char tmp[16];
- Utils::snprintf(tmp,sizeof(tmp),"%x",et);
- ets.append(tmp);
+ std::string v6s;
+ for(unsigned int i=0;i<staticIpCount;++i) {
+ if (this->staticIps[i].ss_family == AF_INET6) {
+ if (v6s.length() > 0)
+ v6s.push_back(',');
+ char buf[64];
+ v6s.append(this->staticIps[i].toString(buf));
}
- et = 0;
}
- lastrt = rt;
- }
- if (ets.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
- }
+ if (v6s.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
+ }
- if (this->com) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
- }
+ std::string ets;
+ unsigned int et = 0;
+ ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ for(unsigned int i=0;i<ruleCount;++i) {
+ ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
+ if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
+ et = rules[i].v.etherType;
+ } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
+ if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
+ if (ets.length() > 0)
+ ets.push_back(',');
+ char tmp2[16];
+ ets.append(Utils::hex((uint16_t)et,tmp2));
+ }
+ et = 0;
+ }
+ lastrt = rt;
+ }
+ if (ets.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
+ }
- std::string ab;
- for(unsigned int i=0;i<this->specialistCount;++i) {
- if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
- if (ab.length() > 0)
- ab.push_back(',');
- ab.append(Address(this->specialists[i]).toString().c_str());
+ if (this->com) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
}
- }
- if (ab.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
- }
- std::vector<Relay> rvec(this->relays());
- std::string rl;
- for(std::vector<Relay>::const_iterator i(rvec.begin());i!=rvec.end();++i) {
- if (rl.length() > 0)
- rl.push_back(',');
- rl.append(i->address.toString());
- if (i->phy4) {
- rl.push_back(';');
- rl.append(i->phy4.toString());
- } else if (i->phy6) {
- rl.push_back(';');
- rl.append(i->phy6.toString());
+ std::string ab;
+ for(unsigned int i=0;i<this->specialistCount;++i) {
+ if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
+ if (ab.length() > 0)
+ ab.push_back(',');
+ char tmp2[16];
+ ab.append(Address(this->specialists[i]).toString(tmp2));
+ }
+ }
+ if (ab.length() > 0) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
}
}
- if (rl.length() > 0) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,rl.c_str())) return false;
- }
- }
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
- // Then add binary blobs
+ // Then add binary blobs
- if (this->com) {
- tmp.clear();
- this->com.serialize(tmp);
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) return false;
- }
+ if (this->com) {
+ tmp->clear();
+ this->com.serialize(*tmp);
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->specialistCount;++i) {
- tmp.append((uint64_t)this->specialists[i]);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->capabilityCount;++i)
+ this->capabilities[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->routeCount;++i) {
- reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(tmp);
- reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(tmp);
- tmp.append((uint16_t)this->routes[i].flags);
- tmp.append((uint16_t)this->routes[i].metric);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->tagCount;++i)
+ this->tags[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->staticIpCount;++i) {
- this->staticIps[i].serialize(tmp);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i)
+ this->certificatesOfOwnership[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->pinnedCount;++i) {
- this->pinned[i].zt.appendTo(tmp);
- this->pinned[i].phy.serialize(tmp);
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) return false;
- }
+ tmp->clear();
+ for(unsigned int i=0;i<this->specialistCount;++i)
+ tmp->append((uint64_t)this->specialists[i]);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) return false;
+ }
- tmp.clear();
- for(unsigned int i=0;i<this->ruleCount;++i) {
- tmp.append((uint8_t)rules[i].t);
- switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
- //case ZT_NETWORK_RULE_ACTION_DROP:
- //case ZT_NETWORK_RULE_ACTION_ACCEPT:
- default:
- tmp.append((uint8_t)0);
- break;
- case ZT_NETWORK_RULE_ACTION_TEE:
- case ZT_NETWORK_RULE_ACTION_REDIRECT:
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
- tmp.append((uint8_t)5);
- Address(rules[i].v.zt).appendTo(tmp);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_ID:
- tmp.append((uint8_t)2);
- tmp.append((uint16_t)rules[i].v.vlanId);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.vlanPcp);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.vlanDei);
- break;
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
- tmp.append((uint8_t)2);
- tmp.append((uint16_t)rules[i].v.etherType);
- break;
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
- case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- tmp.append((uint8_t)6);
- tmp.append(rules[i].v.mac,6);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- tmp.append((uint8_t)5);
- tmp.append(&(rules[i].v.ipv4.ip),4);
- tmp.append((uint8_t)rules[i].v.ipv4.mask);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- tmp.append((uint8_t)17);
- tmp.append(rules[i].v.ipv6.ip,16);
- tmp.append((uint8_t)rules[i].v.ipv6.mask);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_TOS:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.ipTos);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
- tmp.append((uint8_t)1);
- tmp.append((uint8_t)rules[i].v.ipProtocol);
- break;
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
- tmp.append((uint8_t)4);
- tmp.append((uint16_t)rules[i].v.port[0]);
- tmp.append((uint16_t)rules[i].v.port[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
- tmp.append((uint8_t)8);
- tmp.append((uint64_t)rules[i].v.characteristics);
- break;
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
- tmp.append((uint8_t)4);
- tmp.append((uint16_t)rules[i].v.frameSize[0]);
- tmp.append((uint16_t)rules[i].v.frameSize[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- tmp.append((uint8_t)8);
- tmp.append((uint32_t)rules[i].v.tcpseq[0]);
- tmp.append((uint32_t)rules[i].v.tcpseq[1]);
- break;
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
- tmp.append((uint8_t)16);
- tmp.append((uint64_t)rules[i].v.comIV[0]);
- tmp.append((uint64_t)rules[i].v.comIV[1]);
- break;
+ tmp->clear();
+ for(unsigned int i=0;i<this->routeCount;++i) {
+ reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(*tmp);
+ reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(*tmp);
+ tmp->append((uint16_t)this->routes[i].flags);
+ tmp->append((uint16_t)this->routes[i].metric);
}
- }
- if (tmp.size()) {
- if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) return false;
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) return false;
+ }
+
+ tmp->clear();
+ for(unsigned int i=0;i<this->staticIpCount;++i)
+ this->staticIps[i].serialize(*tmp);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) return false;
+ }
+
+ if (this->ruleCount) {
+ tmp->clear();
+ Capability::serializeRules(*tmp,rules,ruleCount);
+ if (tmp->size()) {
+ if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) return false;
+ }
+ }
+
+ delete tmp;
+ } catch ( ... ) {
+ delete tmp;
+ throw;
}
return true;
@@ -267,29 +200,41 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
{
- try {
- Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> tmp;
- char tmp2[ZT_NETWORKCONFIG_DICT_CAPACITY];
+ Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ try {
memset(this,0,sizeof(NetworkConfig));
// Fields that are always present, new or old
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
- if (!this->networkId)
+ if (!this->networkId) {
+ delete tmp;
return false;
+ }
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
+ this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
- if (!this->issuedTo)
+ if (!this->issuedTo) {
+ delete tmp;
return false;
+ }
+ this->remoteTraceTarget = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET);
+ this->remoteTraceLevel = (Trace::Level)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL);
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
+ this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU);
+ if (this->mtu < 1280)
+ this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others
+ else if (this->mtu > ZT_MAX_MTU)
+ this->mtu = ZT_MAX_MTU;
+
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
+ char tmp2[1024];
+
// Decode legacy fields if version is old
- if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
- this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD))
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
@@ -338,36 +283,11 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
char *saveptr = (char *)0;
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
- this->addSpecialist(Address(f),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
- }
- }
-
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD,tmp2,sizeof(tmp2)) > 0) {
- char *saveptr = (char *)0;
- for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
- char tmp3[256];
- Utils::scopy(tmp3,sizeof(tmp3),f);
-
- InetAddress phy;
- char *semi = tmp3;
- while (*semi) {
- if (*semi == ';') {
- *semi = (char)0;
- ++semi;
- phy = InetAddress(semi);
- } else ++semi;
- }
- Address zt(tmp3);
-
- this->addSpecialist(zt,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
- if ((phy)&&(this->pinnedCount < ZT_MAX_NETWORK_PINNED)) {
- this->pinned[this->pinnedCount].zt = zt;
- this->pinned[this->pinnedCount].phy = phy;
- ++this->pinnedCount;
- }
+ this->addSpecialist(Address(Utils::hexStrToU64(f)),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
}
}
#else
+ delete tmp;
return false;
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
} else {
@@ -375,116 +295,76 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp)) {
- this->com.deserialize(tmp,0);
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
+ this->com.deserialize(*tmp,0);
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
+ try {
+ unsigned int p = 0;
+ while (p < tmp->size()) {
+ Capability cap;
+ p += cap.deserialize(*tmp,p);
+ this->capabilities[this->capabilityCount++] = cap;
+ }
+ } catch ( ... ) {}
+ std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
+ try {
+ unsigned int p = 0;
+ while (p < tmp->size()) {
+ Tag tag;
+ p += tag.deserialize(*tmp,p);
+ this->tags[this->tagCount++] = tag;
+ }
+ } catch ( ... ) {}
+ std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
+ }
+
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
unsigned int p = 0;
- while (((p + 8) <= tmp.size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
- this->specialists[this->specialistCount++] = tmp.at<uint64_t>(p);
- p += 8;
+ while (p < tmp->size()) {
+ if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
+ p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
+ else {
+ CertificateOfOwnership foo;
+ p += foo.deserialize(*tmp,p);
+ }
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
unsigned int p = 0;
- while ((p < tmp.size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
- p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(tmp,p);
- p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(tmp,p);
- this->routes[this->routeCount].flags = tmp.at<uint16_t>(p); p += 2;
- this->routes[this->routeCount].metric = tmp.at<uint16_t>(p); p += 2;
- ++this->routeCount;
+ while ((p + 8) <= tmp->size()) {
+ if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS)
+ this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
+ p += 8;
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
unsigned int p = 0;
- while ((p < tmp.size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
- p += this->staticIps[this->staticIpCount++].deserialize(tmp,p);
+ while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
+ p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
+ p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
+ this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
+ this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
+ ++this->routeCount;
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_PINNED,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
unsigned int p = 0;
- while ((p < tmp.size())&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
- this->pinned[this->pinnedCount].zt.setTo(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
- p += this->pinned[this->pinnedCount].phy.deserialize(tmp,p);
- ++this->pinnedCount;
+ while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
+ p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
}
}
- if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,tmp)) {
+ if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
+ this->ruleCount = 0;
unsigned int p = 0;
- while ((p < tmp.size())&&(ruleCount < ZT_MAX_NETWORK_RULES)) {
- rules[ruleCount].t = (uint8_t)tmp[p++];
- unsigned int fieldLen = (unsigned int)tmp[p++];
- switch((ZT_VirtualNetworkRuleType)(rules[ruleCount].t & 0x7f)) {
- default:
- break;
- case ZT_NETWORK_RULE_ACTION_TEE:
- case ZT_NETWORK_RULE_ACTION_REDIRECT:
- case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
- case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
- rules[ruleCount].v.zt = Address(tmp.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_ID:
- rules[ruleCount].v.vlanId = tmp.at<uint16_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
- rules[ruleCount].v.vlanPcp = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
- rules[ruleCount].v.vlanDei = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
- rules[ruleCount].v.etherType = tmp.at<uint16_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
- case ZT_NETWORK_RULE_MATCH_MAC_DEST:
- memcpy(rules[ruleCount].v.mac,tmp.field(p,6),6);
- break;
- case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
- memcpy(&(rules[ruleCount].v.ipv4.ip),tmp.field(p,4),4);
- rules[ruleCount].v.ipv4.mask = (uint8_t)tmp[p + 4];
- break;
- case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
- case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
- memcpy(rules[ruleCount].v.ipv6.ip,tmp.field(p,16),16);
- rules[ruleCount].v.ipv6.mask = (uint8_t)tmp[p + 16];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_TOS:
- rules[ruleCount].v.ipTos = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
- rules[ruleCount].v.ipProtocol = (uint8_t)tmp[p];
- break;
- case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
- case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
- rules[ruleCount].v.port[0] = tmp.at<uint16_t>(p);
- rules[ruleCount].v.port[1] = tmp.at<uint16_t>(p + 2);
- break;
- case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
- rules[ruleCount].v.characteristics = tmp.at<uint64_t>(p);
- break;
- case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
- rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p);
- rules[ruleCount].v.frameSize[0] = tmp.at<uint16_t>(p + 2);
- break;
- case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
- rules[ruleCount].v.tcpseq[0] = tmp.at<uint32_t>(p);
- rules[ruleCount].v.tcpseq[1] = tmp.at<uint32_t>(p + 4);
- break;
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_GE:
- case ZT_NETWORK_RULE_MATCH_COM_FIELD_LE:
- rules[ruleCount].v.comIV[0] = tmp.at<uint64_t>(p);
- rules[ruleCount].v.comIV[1] = tmp.at<uint64_t>(p + 8);
- break;
- }
- p += fieldLen;
- ++ruleCount;
- }
+ Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
}
}
@@ -492,8 +372,10 @@ bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACI
//dump();
//printf("~~~\n");
+ delete tmp;
return true;
} catch ( ... ) {
+ delete tmp;
return false;
}
}
diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp
index 5271c5ad..44066c86 100644
--- a/node/NetworkConfig.hpp
+++ b/node/NetworkConfig.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORKCONFIG_HPP
@@ -35,12 +43,30 @@
#include "MulticastGroup.hpp"
#include "Address.hpp"
#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
+#include "Capability.hpp"
+#include "Tag.hpp"
#include "Dictionary.hpp"
+#include "Hashtable.hpp"
+#include "Identity.hpp"
+#include "Utils.hpp"
+#include "Trace.hpp"
/**
- * Flag: allow passive bridging (experimental)
+ * Default maximum time delta for COMs, tags, and capabilities
+ *
+ * The current value is two hours, providing ample time for a controller to
+ * experience fail-over, etc.
*/
-#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL
+#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA 7200000ULL
+
+/**
+ * Default minimum credential TTL and maxDelta for COM timestamps
+ *
+ * This is just slightly over three minutes and provides three retries for
+ * all currently online members to refresh.
+ */
+#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA 185000ULL
/**
* Flag: enable broadcast
@@ -53,36 +79,72 @@
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
/**
- * Device is a network preferred relay
+ * Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
*/
-#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY 0x0000010000000000ULL
+#define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL
/**
- * Device is an active bridge
+ * Flag: disable frame compression
+ */
+#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
+
+/**
+ * Device can bridge to other Ethernet networks and gets unknown recipient multicasts
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
/**
- * An anchor is a device that is willing to be one and has been online/stable for a long time on this network
+ * Anchors are stable devices on this network that can act like roots when none are up
*/
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
+/**
+ * Designated multicast replicators replicate multicast in place of sender-side replication
+ */
+#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000080000000000ULL
+
namespace ZeroTier {
-// Maximum size of a network config dictionary (can be increased)
-#define ZT_NETWORKCONFIG_DICT_CAPACITY 8194
+// Dictionary capacity needed for max size network config
+#define ZT_NETWORKCONFIG_DICT_CAPACITY (1024 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP))
+
+// Dictionary capacity needed for max size network meta-data
+#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
// Network config version
-#define ZT_NETWORKCONFIG_VERSION 6
+#define ZT_NETWORKCONFIG_VERSION 7
// Fields for meta-data sent with network config requests
+
+// Network config version
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
+// Protocol version (see Packet.hpp)
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
+// Software vendor
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR "vend"
+// Software major version
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
+// Software minor version
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
+// Software revision
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
+// Rules engine revision
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
+// Maximum number of rules per network this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES "mr"
+// Maximum number of capabilities this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES "mc"
+// Maximum number of rules per capability this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES "mcr"
+// Maximum number of tags this node can accept
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
+// Network join authorization token (if any)
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH "a"
+// Network configuration meta-data flags
+#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
// These dictionary keys are short so they don't take up much room.
+// By convention we use upper case for binary blobs, but it doesn't really matter.
// network config version
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
@@ -94,6 +156,10 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
// address of member
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
+// remote trace target
+#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_TARGET "tt"
+// remote trace level
+#define ZT_NETWORKCONFIG_DICT_KEY_REMOTE_TRACE_LEVEL "tl"
// flags(hex)
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
// integer(hex)
@@ -102,6 +168,10 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
// text
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
+// network MTU
+#define ZT_NETWORKCONFIG_DICT_KEY_MTU "mtu"
+// credential time max delta in ms
+#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd"
// binary serialized certificate of membership
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
// specialists (binary array of uint64_t)
@@ -110,16 +180,18 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
// static IPs (binary blob)
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
-// pinned address physical route mappings (binary blob)
-#define ZT_NETWORKCONFIG_DICT_KEY_PINNED "P"
// rules (binary blob)
#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
+// capabilities (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP"
+// tags (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
+// tags (binary blobs)
+#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP "COO"
// Legacy fields -- these are obsoleted but are included when older clients query
// boolean (now a flag)
-#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
-// boolean (now a flag)
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
// IP/bits[,IP/bits,...]
// Note that IPs that end in all zeroes are routes with no assignment in them.
@@ -138,6 +210,8 @@ namespace ZeroTier {
// node;IP/port[,node;IP/port]
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
+// End legacy fields
+
/**
* Network configuration received from network controller nodes
*
@@ -147,93 +221,9 @@ namespace ZeroTier {
class NetworkConfig
{
public:
- /**
- * Network preferred relay with optional physical endpoint addresses
- *
- * This is used by the convenience relays() method.
- */
- struct Relay
- {
- Address address;
- InetAddress phy4,phy6;
- };
-
- /**
- * Create an instance of a NetworkConfig for the test network ID
- *
- * The test network ID is defined as ZT_TEST_NETWORK_ID. This is a
- * "fake" network with no real controller and default options.
- *
- * @param self This node's ZT address
- * @return Configuration for test network ID
- */
- static inline NetworkConfig createTestNetworkConfig(const Address &self)
- {
- NetworkConfig nc;
-
- nc.networkId = ZT_TEST_NETWORK_ID;
- nc.timestamp = 1;
- nc.revision = 1;
- nc.issuedTo = self;
- nc.multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
- nc.flags = ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
- nc.type = ZT_NETWORK_TYPE_PUBLIC;
-
- nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
- nc.ruleCount = 1;
-
- Utils::snprintf(nc.name,sizeof(nc.name),"ZT_TEST_NETWORK");
-
- // Make up a V4 IP from 'self' in the 10.0.0.0/8 range -- no
- // guarantee of uniqueness but collisions are unlikely.
- uint32_t ip = (uint32_t)((self.toInt() & 0x00ffffff) | 0x0a000000); // 10.x.x.x
- if ((ip & 0x000000ff) == 0x000000ff) ip ^= 0x00000001; // but not ending in .255
- if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0
- nc.staticIps[0] = InetAddress(Utils::hton(ip),8);
-
- // Assign an RFC4193-compliant IPv6 address -- will never collide
- nc.staticIps[1] = InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt());
-
- nc.staticIpCount = 2;
-
- return nc;
- }
-
- NetworkConfig()
- {
- memset(this,0,sizeof(NetworkConfig));
- }
-
- NetworkConfig(const NetworkConfig &nc)
- {
- memcpy(this,&nc,sizeof(NetworkConfig));
- }
-
- inline NetworkConfig &operator=(const NetworkConfig &nc)
- {
- memcpy(this,&nc,sizeof(NetworkConfig));
- return *this;
- }
-
- /**
- * @param etherType Ethernet frame type to check
- * @return True if allowed on this network
- */
- inline bool permitsEtherType(unsigned int etherType) const
- {
- unsigned int et = 0;
- for(unsigned int i=0;i<ruleCount;++i) {
- ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
- if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
- et = rules[i].v.etherType;
- } else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
- if ((!et)||(et == etherType))
- return true;
- et = 0;
- }
- }
- return false;
- }
+ NetworkConfig() { memset(this,0,sizeof(NetworkConfig)); }
+ NetworkConfig(const NetworkConfig &nc) { ZT_FAST_MEMCPY(this,&nc,sizeof(NetworkConfig)); }
+ inline NetworkConfig &operator=(const NetworkConfig &nc) { ZT_FAST_MEMCPY(this,&nc,sizeof(NetworkConfig)); return *this; }
/**
* Write this network config to a dictionary for transport
@@ -247,35 +237,35 @@ public:
/**
* Read this network config from a dictionary
*
- * @param d Dictionary
+ * @param d Dictionary (non-const since it might be modified during parse, should not be used after call)
* @return True if dictionary was valid and network config successfully initialized
*/
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
/**
- * @return True if passive bridging is allowed (experimental)
+ * @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
*/
- inline bool allowPassiveBridging() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0); }
+ inline bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
/**
- * @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network
+ * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
*/
- inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
+ inline bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
/**
- * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
+ * @return True if frames should not be compressed
*/
- inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
+ inline bool disableCompression() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); }
/**
* @return Network type is public (no access control)
*/
- inline bool isPublic() const throw() { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
+ inline bool isPublic() const { return (this->type == ZT_NETWORK_TYPE_PUBLIC); }
/**
* @return Network type is private (certificate access control)
*/
- inline bool isPrivate() const throw() { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
+ inline bool isPrivate() const { return (this->type == ZT_NETWORK_TYPE_PRIVATE); }
/**
* @return ZeroTier addresses of devices on this network designated as active bridges
@@ -290,9 +280,25 @@ public:
return r;
}
- /**
- * @return ZeroTier addresses of "anchor" devices on this network
- */
+ inline unsigned int activeBridges(Address ab[ZT_MAX_NETWORK_SPECIALISTS]) const
+ {
+ unsigned int c = 0;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)
+ ab[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline bool isActiveBridge(const Address &a) const
+ {
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0)&&(a == specialists[i]))
+ return true;
+ }
+ return false;
+ }
+
inline std::vector<Address> anchors() const
{
std::vector<Address> r;
@@ -303,130 +309,81 @@ public:
return r;
}
- /**
- * Get pinned physical address for a given ZeroTier address, if any
- *
- * @param zt ZeroTier address
- * @param af Address family (e.g. AF_INET) or 0 for the first we find of any type
- * @return Physical address, if any
- */
- inline InetAddress findPinnedAddress(const Address &zt,unsigned int af) const
+ inline std::vector<Address> multicastReplicators() const
{
- for(unsigned int i=0;i<pinnedCount;++i) {
- if (pinned[i].zt == zt) {
- if ((af == 0)||((unsigned int)pinned[i].phy.ss_family == af))
- return pinned[i].phy;
- }
+ std::vector<Address> r;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
+ r.push_back(Address(specialists[i]));
}
- return InetAddress();
+ return r;
}
- /**
- * This gets network preferred relays with their static physical address if one is defined
- *
- * @return Network-preferred relays for this network (if none, only roots will be used)
- */
- inline std::vector<Relay> relays() const
+ inline unsigned int multicastReplicators(Address mr[ZT_MAX_NETWORK_SPECIALISTS]) const
{
- std::vector<Relay> r;
+ unsigned int c = 0;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
- r.push_back(Relay());
- r.back().address = specialists[i];
- r.back().phy4 = findPinnedAddress(r.back().address,AF_INET);
- r.back().phy6 = findPinnedAddress(r.back().address,AF_INET6);
- }
+ if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
+ mr[c++] = specialists[i];
}
- return r;
+ return c;
}
- /**
- * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
- * @return True if this network allows bridging
- */
- inline bool permitsBridging(const Address &fromPeer) const
+ inline bool isMulticastReplicator(const Address &a) const
{
- if ((flags & ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING) != 0)
- return true;
for(unsigned int i=0;i<specialistCount;++i) {
- if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0))
+ if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)&&(a == specialists[i]))
return true;
}
return false;
}
- /**
- * Iterate through relays efficiently
- *
- * @param ptr Value-result parameter -- start by initializing with zero, then call until return is null
- * @return Address of relay or NULL if no more
- */
- Address nextRelay(unsigned int &ptr) const
+ inline std::vector<Address> alwaysContactAddresses() const
+ {
+ std::vector<Address> r;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+ r.push_back(Address(specialists[i]));
+ }
+ return r;
+ }
+
+ inline unsigned int alwaysContactAddresses(Address ac[ZT_MAX_NETWORK_SPECIALISTS]) const
+ {
+ unsigned int c = 0;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+ ac[c++] = specialists[i];
+ }
+ return c;
+ }
+
+ inline void alwaysContactAddresses(Hashtable< Address,std::vector<InetAddress> > &a) const
{
- while (ptr < specialistCount) {
- if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
- return Address(specialists[ptr++]);
- } else {
- ++ptr;
+ for(unsigned int i=0;i<specialistCount;++i) {
+ if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
+ a[Address(specialists[i])];
}
}
- return Address();
}
/**
- * @param zt ZeroTier address
- * @return True if this address is a relay
+ * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
+ * @return True if this network allows bridging
*/
- bool isRelay(const Address &zt) const
+ inline bool permitsBridging(const Address &fromPeer) const
{
for(unsigned int i=0;i<specialistCount;++i) {
- if ((zt == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0))
+ if ((fromPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0))
return true;
}
return false;
}
- /**
- * @return True if this network config is non-NULL
- */
- inline operator bool() const throw() { return (networkId != 0); }
-
+ inline operator bool() const { return (networkId != 0); }
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
- /*
- inline void dump() const
- {
- printf("networkId==%.16llx\n",networkId);
- printf("timestamp==%llu\n",timestamp);
- printf("revision==%llu\n",revision);
- printf("issuedTo==%.10llx\n",issuedTo.toInt());
- printf("multicastLimit==%u\n",multicastLimit);
- printf("flags=%.8lx\n",(unsigned long)flags);
- printf("specialistCount==%u\n",specialistCount);
- for(unsigned int i=0;i<specialistCount;++i)
- printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
- printf("routeCount==%u\n",routeCount);
- for(unsigned int i=0;i<routeCount;++i) {
- printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
- printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
- printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
- printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
- }
- printf("staticIpCount==%u\n",staticIpCount);
- for(unsigned int i=0;i<staticIpCount;++i)
- printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
- printf("pinnedCount==%u\n",pinnedCount);
- for(unsigned int i=0;i<pinnedCount;++i) {
- printf(" pinned[i].zt==%s\n",pinned[i].zt.toString().c_str());
- printf(" pinned[i].phy==%s\n",pinned[i].phy.toString().c_str());
- }
- printf("ruleCount==%u\n",ruleCount);
- printf("name==%s\n",name);
- printf("com==%s\n",com.toString().c_str());
- }
- */
-
/**
* Add a specialist or mask flags if already present
*
@@ -453,6 +410,24 @@ public:
return false;
}
+ const Capability *capability(const uint32_t id) const
+ {
+ for(unsigned int i=0;i<capabilityCount;++i) {
+ if (capabilities[i].id() == id)
+ return &(capabilities[i]);
+ }
+ return (Capability *)0;
+ }
+
+ const Tag *tag(const uint32_t id) const
+ {
+ for(unsigned int i=0;i<tagCount;++i) {
+ if (tags[i].id() == id)
+ return &(tags[i]);
+ }
+ return (Tag *)0;
+ }
+
/**
* Network ID that this configuration applies to
*/
@@ -461,7 +436,12 @@ public:
/**
* Controller-side time of config generation/issue
*/
- uint64_t timestamp;
+ int64_t timestamp;
+
+ /**
+ * Max difference between timestamp and tag/capability timestamp
+ */
+ int64_t credentialTimeMaxDelta;
/**
* Controller-side revision counter for this configuration
@@ -474,11 +454,26 @@ public:
Address issuedTo;
/**
+ * If non-NULL, remote traces related to this network are sent here
+ */
+ Address remoteTraceTarget;
+
+ /**
* Flags (64-bit)
*/
uint64_t flags;
/**
+ * Remote trace level
+ */
+ Trace::Level remoteTraceLevel;
+
+ /**
+ * Network MTU
+ */
+ unsigned int mtu;
+
+ /**
* Maximum number of recipients per multicast (not including active bridges)
*/
unsigned int multicastLimit;
@@ -499,14 +494,24 @@ public:
unsigned int staticIpCount;
/**
- * Number of pinned devices (devices with physical address hints)
+ * Number of rule table entries
*/
- unsigned int pinnedCount;
+ unsigned int ruleCount;
/**
- * Number of rule table entries
+ * Number of capabilities
*/
- unsigned int ruleCount;
+ unsigned int capabilityCount;
+
+ /**
+ * Number of tags
+ */
+ unsigned int tagCount;
+
+ /**
+ * Number of certificates of ownership
+ */
+ unsigned int certificateOfOwnershipCount;
/**
* Specialist devices
@@ -527,20 +532,24 @@ public:
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
/**
- * Pinned devices with physical address hints
- *
- * These can be used to specify a physical address where a given device
- * can be reached. It's usually used with network relays (specialists).
+ * Base network rules
*/
- struct {
- Address zt;
- InetAddress phy;
- } pinned[ZT_MAX_NETWORK_PINNED];
+ ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
/**
- * Rules table
+ * Capabilities for this node on this network, in ascending order of capability ID
*/
- ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
+ Capability capabilities[ZT_MAX_NETWORK_CAPABILITIES];
+
+ /**
+ * Tags for this node on this network, in ascending order of tag ID
+ */
+ Tag tags[ZT_MAX_NETWORK_TAGS];
+
+ /**
+ * Certificates of ownership for this network member
+ */
+ CertificateOfOwnership certificatesOfOwnership[ZT_MAX_CERTIFICATES_OF_OWNERSHIP];
/**
* Network type (currently just public or private)
diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp
index fa90fb75..393bcc91 100644
--- a/node/NetworkController.hpp
+++ b/node/NetworkController.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NETWORKCONFIGMASTER_HPP
@@ -24,12 +32,12 @@
#include "Constants.hpp"
#include "Dictionary.hpp"
#include "NetworkConfig.hpp"
+#include "Revocation.hpp"
+#include "Address.hpp"
namespace ZeroTier {
-class RuntimeEnvironment;
class Identity;
-class Address;
struct InetAddress;
/**
@@ -38,45 +46,77 @@ struct InetAddress;
class NetworkController
{
public:
+ enum ErrorCode
+ {
+ NC_ERROR_NONE = 0,
+ NC_ERROR_OBJECT_NOT_FOUND = 1,
+ NC_ERROR_ACCESS_DENIED = 2,
+ NC_ERROR_INTERNAL_SERVER_ERROR = 3
+ };
+
/**
- * Return value of doNetworkConfigRequest
+ * Interface for sender used to send pushes and replies
*/
- enum ResultCode
+ class Sender
{
- NETCONF_QUERY_OK = 0,
- NETCONF_QUERY_OBJECT_NOT_FOUND = 1,
- NETCONF_QUERY_ACCESS_DENIED = 2,
- NETCONF_QUERY_INTERNAL_SERVER_ERROR = 3,
- NETCONF_QUERY_IGNORE = 4
+ public:
+ /**
+ * Send a configuration to a remote peer
+ *
+ * @param nwid Network ID
+ * @param requestPacketId Request packet ID to send OK(NETWORK_CONFIG_REQUEST) or 0 to send NETWORK_CONFIG (push)
+ * @param destination Destination peer Address
+ * @param nc Network configuration to send
+ * @param sendLegacyFormatConfig If true, send an old-format network config
+ */
+ virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig) = 0;
+
+ /**
+ * Send revocation to a node
+ *
+ * @param destination Destination node address
+ * @param rev Revocation to send
+ */
+ virtual void ncSendRevocation(const Address &destination,const Revocation &rev) = 0;
+
+ /**
+ * Send a network configuration request error
+ *
+ * @param nwid Network ID
+ * @param requestPacketId Request packet ID or 0 if none
+ * @param destination Destination peer Address
+ * @param errorCode Error code
+ */
+ virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode) = 0;
};
NetworkController() {}
virtual ~NetworkController() {}
/**
- * Handle a network config request, sending replies if necessary
- *
- * This call is permitted to block, and may be called concurrently from more
- * than one thread. Implementations must use locks if needed.
+ * Called when this is added to a Node to initialize and supply info
*
- * On internal server errors, the 'error' field in result can be filled in
- * to indicate the error.
+ * @param signingId Identity for signing of network configurations, certs, etc.
+ * @param sender Sender implementation for sending replies or config pushes
+ */
+ virtual void init(const Identity &signingId,Sender *sender) = 0;
+
+ /**
+ * Handle a network configuration request
*
- * @param fromAddr Originating wire address or null address if packet is not direct (or from self)
- * @param signingId Identity that should be used to sign results -- must include private key
- * @param identity Originating peer ZeroTier identity
* @param nwid 64-bit network ID
+ * @param fromAddr Originating wire address or null address if packet is not direct (or from self)
+ * @param requestPacketId Packet ID of request packet or 0 if not initiated by remote request
+ * @param identity ZeroTier identity of originating peer
* @param metaData Meta-data bundled with request (if any)
- * @param nc NetworkConfig to fill with results
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
*/
- virtual NetworkController::ResultCode doNetworkConfigRequest(
+ virtual void request(
+ uint64_t nwid,
const InetAddress &fromAddr,
- const Identity &signingId,
+ uint64_t requestPacketId,
const Identity &identity,
- uint64_t nwid,
- const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,
- NetworkConfig &nc) = 0;
+ const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData) = 0;
};
} // namespace ZeroTier
diff --git a/node/Node.cpp b/node/Node.cpp
index 13085028..db511430 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -25,6 +33,7 @@
#include "../version.h"
#include "Constants.hpp"
+#include "SharedPtr.hpp"
#include "Node.hpp"
#include "RuntimeEnvironment.hpp"
#include "NetworkController.hpp"
@@ -36,10 +45,8 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
-#include "DeferredPackets.hpp"
-
-const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
+#include "Network.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
@@ -47,112 +54,126 @@ namespace ZeroTier {
/* Public Node interface (C++, exposed via CAPI bindings) */
/****************************************************************************/
-Node::Node(
- uint64_t now,
- void *uptr,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback) :
+Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now) :
_RR(this),
RR(&_RR),
_uPtr(uptr),
- _dataStoreGetFunction(dataStoreGetFunction),
- _dataStorePutFunction(dataStorePutFunction),
- _wirePacketSendFunction(wirePacketSendFunction),
- _virtualNetworkFrameFunction(virtualNetworkFrameFunction),
- _virtualNetworkConfigFunction(virtualNetworkConfigFunction),
- _pathCheckFunction(pathCheckFunction),
- _eventCallback(eventCallback),
- _networks(),
- _networks_m(),
- _prngStreamPtr(0),
+ _networks(8),
_now(now),
_lastPingCheck(0),
- _lastHousekeepingRun(0)
+ _lastHousekeepingRun(0),
+ _lastMemoizedTraceSettings(0)
{
+ if (callbacks->version != 0)
+ throw ZT_EXCEPTION_INVALID_ARGUMENT;
+ ZT_FAST_MEMCPY(&_cb,callbacks,sizeof(ZT_Node_Callbacks));
+
+ // Initialize non-cryptographic PRNG from a good random source
+ Utils::getSecureRandom((void *)_prngState,sizeof(_prngState));
+
_online = false;
- // Use Salsa20 alone as a high-quality non-crypto PRNG
- {
- char foo[32];
- Utils::getSecureRandom(foo,32);
- _prng.init(foo,256,foo);
- memset(_prngStream,0,sizeof(_prngStream));
- _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
+ memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
+ memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
+ memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
+
+ uint64_t idtmp[2];
+ idtmp[0] = 0; idtmp[1] = 0;
+ char tmp[2048];
+ int n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,tmp,sizeof(tmp) - 1);
+ if (n > 0) {
+ tmp[n] = (char)0;
+ if (RR->identity.fromString(tmp)) {
+ RR->identity.toString(false,RR->publicIdentityStr);
+ RR->identity.toString(true,RR->secretIdentityStr);
+ } else {
+ n = -1;
+ }
}
- {
- std::string idtmp(dataStoreGet("identity.secret"));
- if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
- TRACE("identity.secret not found, generating...");
- RR->identity.generate();
- idtmp = RR->identity.toString(true);
- if (!dataStorePut("identity.secret",idtmp,true))
- throw std::runtime_error("unable to write identity.secret");
- }
- RR->publicIdentityStr = RR->identity.toString(false);
- RR->secretIdentityStr = RR->identity.toString(true);
- idtmp = dataStoreGet("identity.public");
- if (idtmp != RR->publicIdentityStr) {
- if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
- throw std::runtime_error("unable to write identity.public");
+ if (n <= 0) {
+ RR->identity.generate();
+ RR->identity.toString(false,RR->publicIdentityStr);
+ RR->identity.toString(true,RR->secretIdentityStr);
+ idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,RR->secretIdentityStr,(unsigned int)strlen(RR->secretIdentityStr));
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
+ } else {
+ idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
+ n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,tmp,sizeof(tmp) - 1);
+ if ((n > 0)&&(n < (int)sizeof(RR->publicIdentityStr))&&(n < (int)sizeof(tmp))) {
+ if (memcmp(tmp,RR->publicIdentityStr,n))
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr));
}
}
+ char *m = (char *)0;
try {
- RR->sw = new Switch(RR);
- RR->mc = new Multicaster(RR);
- RR->topology = new Topology(RR);
- RR->sa = new SelfAwareness(RR);
- RR->dp = new DeferredPackets(RR);
+ const unsigned long ts = sizeof(Trace) + (((sizeof(Trace) & 0xf) != 0) ? (16 - (sizeof(Trace) & 0xf)) : 0);
+ const unsigned long sws = sizeof(Switch) + (((sizeof(Switch) & 0xf) != 0) ? (16 - (sizeof(Switch) & 0xf)) : 0);
+ const unsigned long mcs = sizeof(Multicaster) + (((sizeof(Multicaster) & 0xf) != 0) ? (16 - (sizeof(Multicaster) & 0xf)) : 0);
+ const unsigned long topologys = sizeof(Topology) + (((sizeof(Topology) & 0xf) != 0) ? (16 - (sizeof(Topology) & 0xf)) : 0);
+ const unsigned long sas = sizeof(SelfAwareness) + (((sizeof(SelfAwareness) & 0xf) != 0) ? (16 - (sizeof(SelfAwareness) & 0xf)) : 0);
+
+ m = reinterpret_cast<char *>(::malloc(16 + ts + sws + mcs + topologys + sas));
+ if (!m)
+ throw std::bad_alloc();
+ RR->rtmem = m;
+ while (((uintptr_t)m & 0xf) != 0) ++m;
+
+ RR->t = new (m) Trace(RR);
+ m += ts;
+ RR->sw = new (m) Switch(RR);
+ m += sws;
+ RR->mc = new (m) Multicaster(RR);
+ m += mcs;
+ RR->topology = new (m) Topology(RR,tptr);
+ m += topologys;
+ RR->sa = new (m) SelfAwareness(RR);
} catch ( ... ) {
- delete RR->dp;
- delete RR->sa;
- delete RR->topology;
- delete RR->mc;
- delete RR->sw;
+ if (RR->sa) RR->sa->~SelfAwareness();
+ if (RR->topology) RR->topology->~Topology();
+ if (RR->mc) RR->mc->~Multicaster();
+ if (RR->sw) RR->sw->~Switch();
+ if (RR->t) RR->t->~Trace();
+ ::free(m);
throw;
}
- postEvent(ZT_EVENT_UP);
+ postEvent(tptr,ZT_EVENT_UP);
}
Node::~Node()
{
- Mutex::Lock _l(_networks_m);
-
- _networks.clear(); // ensure that networks are destroyed before shutdow
-
- RR->dpEnabled = 0;
- delete RR->dp;
- delete RR->sa;
- delete RR->topology;
- delete RR->mc;
- delete RR->sw;
-#ifdef ZT_ENABLE_CLUSTER
- delete RR->cluster;
-#endif
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.clear(); // destroy all networks before shutdown
+ }
+ if (RR->sa) RR->sa->~SelfAwareness();
+ if (RR->topology) RR->topology->~Topology();
+ if (RR->mc) RR->mc->~Multicaster();
+ if (RR->sw) RR->sw->~Switch();
+ if (RR->t) RR->t->~Trace();
+ ::free(RR->rtmem);
}
ZT_ResultCode Node::processWirePacket(
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
- RR->sw->onRemotePacket(*(reinterpret_cast<const InetAddress *>(localAddress)),*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
+ RR->sw->onRemotePacket(tptr,localSocket,*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
return ZT_RESULT_OK;
}
ZT_ResultCode Node::processVirtualNetworkFrame(
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -160,157 +181,145 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
SharedPtr<Network> nw(this->network(nwid));
if (nw) {
- RR->sw->onLocalEthernet(nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
+ RR->sw->onLocalEthernet(tptr,nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
return ZT_RESULT_OK;
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
+// Closure used to ping upstream and active/online peers
class _PingPeersThatNeedPing
{
public:
- _PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector<NetworkConfig::Relay> &relays) :
- lastReceiveFromUpstream(0),
+ _PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &alwaysContact,int64_t now) :
RR(renv),
+ _tPtr(tPtr),
+ _alwaysContact(alwaysContact),
_now(now),
- _relays(relays),
- _world(RR->topology->world())
+ _bestCurrentUpstream(RR->topology->getUpstreamPeer())
{
}
- uint64_t lastReceiveFromUpstream; // tracks last time we got a packet from an 'upstream' peer like a root or a relay
-
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
- bool upstream = false;
- InetAddress stableEndpoint4,stableEndpoint6;
-
- // If this is a world root, pick (if possible) both an IPv4 and an IPv6 stable endpoint to use if link isn't currently alive.
- for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
- if (r->identity == p->identity()) {
- upstream = true;
- for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) {
- const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()];
- if (!stableEndpoint4) {
- if (addr.ss_family == AF_INET)
- stableEndpoint4 = addr;
- }
- if (!stableEndpoint6) {
- if (addr.ss_family == AF_INET6)
- stableEndpoint6 = addr;
+ const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address());
+ if (alwaysContactEndpoints) {
+ const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now);
+ bool contacted = (sent != 0);
+
+ if ((sent & 0x1) == 0) { // bit 0x1 == IPv4 sent
+ for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)alwaysContactEndpoints->size();++k) {
+ const InetAddress &addr = (*alwaysContactEndpoints)[ptr++ % alwaysContactEndpoints->size()];
+ if (addr.ss_family == AF_INET) {
+ p->sendHELLO(_tPtr,-1,addr,_now);
+ contacted = true;
+ break;
}
}
- break;
- }
- }
-
- if (!upstream) {
- // If I am a root server, only ping other root servers -- roots don't ping "down"
- // since that would just be a waste of bandwidth and could potentially cause route
- // flapping in Cluster mode.
- if (RR->topology->amRoot())
- return;
-
- // Check for network preferred relays, also considered 'upstream' and thus always
- // pinged to keep links up. If they have stable addresses we will try them there.
- for(std::vector<NetworkConfig::Relay>::const_iterator r(_relays.begin());r!=_relays.end();++r) {
- if (r->address == p->address()) {
- stableEndpoint4 = r->phy4;
- stableEndpoint6 = r->phy6;
- upstream = true;
- break;
- }
}
- }
- if (upstream) {
- // "Upstream" devices are roots and relays and get special treatment -- they stay alive
- // forever and we try to keep (if available) both IPv4 and IPv6 channels open to them.
- bool needToContactIndirect = true;
- if (p->doPingAndKeepalive(_now,AF_INET)) {
- needToContactIndirect = false;
- } else {
- if (stableEndpoint4) {
- needToContactIndirect = false;
- p->sendHELLO(InetAddress(),stableEndpoint4,_now);
- }
- }
- if (p->doPingAndKeepalive(_now,AF_INET6)) {
- needToContactIndirect = false;
- } else {
- if (stableEndpoint6) {
- needToContactIndirect = false;
- p->sendHELLO(InetAddress(),stableEndpoint6,_now);
+ if ((sent & 0x2) == 0) { // bit 0x2 == IPv6 sent
+ for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)alwaysContactEndpoints->size();++k) {
+ const InetAddress &addr = (*alwaysContactEndpoints)[ptr++ % alwaysContactEndpoints->size()];
+ if (addr.ss_family == AF_INET6) {
+ p->sendHELLO(_tPtr,-1,addr,_now);
+ contacted = true;
+ break;
+ }
}
}
- if (needToContactIndirect) {
- // If this is an upstream and we have no stable endpoint for either IPv4 or IPv6,
- // send a NOP indirectly if possible to see if we can get to this peer in any
- // way whatsoever. This will e.g. find network preferred relays that lack
- // stable endpoints by using root servers.
- Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
- RR->sw->send(outp,true,0);
+ if ((!contacted)&&(_bestCurrentUpstream)) {
+ const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
+ if (up)
+ p->sendHELLO(_tPtr,up->localSocket(),up->address(),_now);
}
- lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
- } else if (p->activelyTransferringFrames(_now)) {
- // Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
- p->doPingAndKeepalive(_now,0);
+ _alwaysContact.erase(p->address()); // after this we'll WHOIS all upstreams that remain
+ } else if (p->isActive(_now)) {
+ p->doPingAndKeepalive(_tPtr,_now);
}
}
private:
const RuntimeEnvironment *RR;
- uint64_t _now;
- const std::vector<NetworkConfig::Relay> &_relays;
- World _world;
+ void *_tPtr;
+ Hashtable< Address,std::vector<InetAddress> > &_alwaysContact;
+ const int64_t _now;
+ const SharedPtr<Peer> _bestCurrentUpstream;
};
-ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
{
_now = now;
Mutex::Lock bl(_backgroundTasksLock);
unsigned long timeUntilNextPingCheck = ZT_PING_CHECK_INVERVAL;
- const uint64_t timeSinceLastPingCheck = now - _lastPingCheck;
+ const int64_t timeSinceLastPingCheck = now - _lastPingCheck;
if (timeSinceLastPingCheck >= ZT_PING_CHECK_INVERVAL) {
try {
_lastPingCheck = now;
- // Get relays and networks that need config without leaving the mutex locked
- std::vector< NetworkConfig::Relay > networkRelays;
- std::vector< SharedPtr<Network> > needConfig;
+ // Get designated VL1 upstreams
+ Hashtable< Address,std::vector<InetAddress> > alwaysContact;
+ RR->topology->getUpstreamsToContact(alwaysContact);
+
+ // Check last receive time on designated upstreams to see if we seem to be online
+ int64_t lastReceivedFromUpstream = 0;
{
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig())) {
- needConfig.push_back(n->second);
- }
- if (n->second->hasConfig()) {
- std::vector<NetworkConfig::Relay> r(n->second->config().relays());
- networkRelays.insert(networkRelays.end(),r.begin(),r.end());
- }
+ Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
+ Address *upstreamAddress = (Address *)0;
+ std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
+ while (i.next(upstreamAddress,upstreamStableEndpoints)) {
+ SharedPtr<Peer> p(RR->topology->getPeerNoCache(*upstreamAddress));
+ if (p)
+ lastReceivedFromUpstream = std::max(p->lastReceive(),lastReceivedFromUpstream);
}
}
- // Request updated configuration for networks that need it
- for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
- (*n)->requestConfiguration();
+ // Get peers we should stay connected to according to network configs
+ // Also get networks and whether they need config so we only have to do one pass over networks
+ std::vector< std::pair< SharedPtr<Network>,bool > > networkConfigNeeded;
+ {
+ Mutex::Lock l(_networks_m);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *nwid = (uint64_t *)0;
+ SharedPtr<Network> *network = (SharedPtr<Network> *)0;
+ while (i.next(nwid,network)) {
+ (*network)->config().alwaysContactAddresses(alwaysContact);
+ networkConfigNeeded.push_back( std::pair< SharedPtr<Network>,bool >(*network,(((now - (*network)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*network)->hasConfig()))) );
+ }
+ }
- // Do pings and keepalives
- _PingPeersThatNeedPing pfunc(RR,now,networkRelays);
+ // Ping active peers, upstreams, and others that we should always contact
+ _PingPeersThatNeedPing pfunc(RR,tptr,alwaysContact,now);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
+ // Run WHOIS to create Peer for alwaysContact addresses that could not be contacted
+ {
+ Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
+ Address *upstreamAddress = (Address *)0;
+ std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
+ while (i.next(upstreamAddress,upstreamStableEndpoints))
+ RR->sw->requestWhois(tptr,now,*upstreamAddress);
+ }
+
+ // Refresh network config or broadcast network updates to members as needed
+ for(std::vector< std::pair< SharedPtr<Network>,bool > >::const_iterator n(networkConfigNeeded.begin());n!=networkConfigNeeded.end();++n) {
+ if (n->second)
+ n->first->requestConfiguration(tptr);
+ n->first->sendUpdatesToMembers(tptr);
+ }
+
// Update online status, post status change as event
const bool oldOnline = _online;
- _online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
+ _online = (((now - lastReceivedFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amUpstream()));
if (oldOnline != _online)
- postEvent(_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
+ postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
@@ -318,10 +327,15 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
timeUntilNextPingCheck -= (unsigned long)timeSinceLastPingCheck;
}
+ if ((now - _lastMemoizedTraceSettings) >= (ZT_HOUSEKEEPING_PERIOD / 4)) {
+ _lastMemoizedTraceSettings = now;
+ RR->t->updateMemoizedSettings();
+ }
+
if ((now - _lastHousekeepingRun) >= ZT_HOUSEKEEPING_PERIOD) {
+ _lastHousekeepingRun = now;
try {
- _lastHousekeepingRun = now;
- RR->topology->clean(now);
+ RR->topology->doPeriodicTasks(tptr,now);
RR->sa->clean(now);
RR->mc->clean(now);
} catch ( ... ) {
@@ -330,18 +344,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
}
try {
-#ifdef ZT_ENABLE_CLUSTER
- // If clustering is enabled we have to call cluster->doPeriodicTasks() very often, so we override normal timer deadline behavior
- if (RR->cluster) {
- RR->sw->doTimerTasks(now);
- RR->cluster->doPeriodicTasks();
- *nextBackgroundTaskDeadline = now + ZT_CLUSTER_PERIODIC_TASK_PERIOD; // this is really short so just tick at this rate
- } else {
-#endif
- *nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
-#ifdef ZT_ENABLE_CLUSTER
- }
-#endif
+ *nextBackgroundTaskDeadline = now + (int64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(tptr,now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
@@ -349,38 +352,51 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
return ZT_RESULT_OK;
}
-ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
+ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr)
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(!nw)
- _networks.push_back(std::pair< uint64_t,SharedPtr<Network> >(nwid,SharedPtr<Network>(new Network(RR,nwid,uptr))));
- std::sort(_networks.begin(),_networks.end()); // will sort by nwid since it's the first in a pair<>
+ SharedPtr<Network> &nw = _networks[nwid];
+ if (!nw)
+ nw = SharedPtr<Network>(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0));
return ZT_RESULT_OK;
}
-ZT_ResultCode Node::leave(uint64_t nwid,void **uptr)
+ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
{
- std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (n->first != nwid)
- newn.push_back(*n);
- else {
- if (uptr)
- *uptr = n->second->userPtr();
- n->second->destroy();
- }
+ ZT_VirtualNetworkConfig ctmp;
+ void **nUserPtr = (void **)0;
+ {
+ Mutex::Lock _l(_networks_m);
+ SharedPtr<Network> *nw = _networks.get(nwid);
+ if (!nw)
+ return ZT_RESULT_OK;
+ if (uptr)
+ *uptr = (*nw)->userPtr();
+ (*nw)->externalConfig(&ctmp);
+ (*nw)->destroy();
+ nUserPtr = (*nw)->userPtr();
}
- _networks.swap(newn);
+
+ if (nUserPtr)
+ RR->node->configureVirtualNetworkPort(tptr,nwid,nUserPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
+
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.erase(nwid);
+ }
+
+ uint64_t tmp[2];
+ tmp[0] = nwid; tmp[1] = 0;
+ RR->node->stateObjectDelete(tptr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp);
+
return ZT_RESULT_OK;
}
-ZT_ResultCode Node::multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
+ZT_ResultCode Node::multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
{
SharedPtr<Network> nw(this->network(nwid));
if (nw) {
- nw->multicastSubscribe(MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff)));
+ nw->multicastSubscribe(tptr,MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff)));
return ZT_RESULT_OK;
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
@@ -394,6 +410,18 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u
} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
}
+ZT_ResultCode Node::orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
+{
+ RR->topology->addMoon(tptr,moonWorldId,Address(moonSeed));
+ return ZT_RESULT_OK;
+}
+
+ZT_ResultCode Node::deorbit(void *tptr,uint64_t moonWorldId)
+{
+ RR->topology->removeMoon(tptr,moonWorldId);
+ return ZT_RESULT_OK;
+}
+
uint64_t Node::address() const
{
return RR->identity.address().toInt();
@@ -402,10 +430,8 @@ uint64_t Node::address() const
void Node::status(ZT_NodeStatus *status) const
{
status->address = RR->identity.address().toInt();
- status->worldId = RR->topology->worldId();
- status->worldTimestamp = RR->topology->worldTimestamp();
- status->publicIdentity = RR->publicIdentityStr.c_str();
- status->secretIdentity = RR->secretIdentityStr.c_str();
+ status->publicIdentity = RR->publicIdentityStr;
+ status->secretIdentity = RR->secretIdentityStr;
status->online = _online ? 1 : 0;
}
@@ -424,8 +450,6 @@ ZT_PeerList *Node::peers() const
for(std::vector< std::pair< Address,SharedPtr<Peer> > >::iterator pi(peers.begin());pi!=peers.end();++pi) {
ZT_Peer *p = &(pl->peers[pl->peerCount++]);
p->address = pi->second->address().toInt();
- p->lastUnicastFrame = pi->second->lastUnicastFrame();
- p->lastMulticastFrame = pi->second->lastMulticastFrame();
if (pi->second->remoteVersionKnown()) {
p->versionMajor = pi->second->remoteVersionMajor();
p->versionMinor = pi->second->remoteVersionMinor();
@@ -435,19 +459,21 @@ ZT_PeerList *Node::peers() const
p->versionMinor = -1;
p->versionRev = -1;
}
- p->latency = pi->second->latency();
- p->role = RR->topology->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF;
+ p->latency = pi->second->latency(_now);
+ if (p->latency >= 0xffff)
+ p->latency = -1;
+ p->role = RR->topology->role(pi->second->identity().address());
- std::vector<Path> paths(pi->second->paths());
- Path *bestPath = pi->second->getBestPath(_now);
+ std::vector< SharedPtr<Path> > paths(pi->second->paths(_now));
+ SharedPtr<Path> bestp(pi->second->getBestPath(_now,false));
p->pathCount = 0;
- for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
- memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage));
- p->paths[p->pathCount].lastSend = path->lastSend();
- p->paths[p->pathCount].lastReceive = path->lastReceived();
- p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
- p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
- p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->address());
+ for(std::vector< SharedPtr<Path> >::iterator path(paths.begin());path!=paths.end();++path) {
+ ZT_FAST_MEMCPY(&(p->paths[p->pathCount].address),&((*path)->address()),sizeof(struct sockaddr_storage));
+ p->paths[p->pathCount].lastSend = (*path)->lastOut();
+ p->paths[p->pathCount].lastReceive = (*path)->lastIn();
+ p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address());
+ p->paths[p->pathCount].expired = 0;
+ p->paths[p->pathCount].preferred = ((*path) == bestp) ? 1 : 0;
++p->pathCount;
}
}
@@ -458,10 +484,10 @@ ZT_PeerList *Node::peers() const
ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(nw) {
+ const SharedPtr<Network> *nw = _networks.get(nwid);
+ if (nw) {
ZT_VirtualNetworkConfig *nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig));
- nw->externalConfig(nc);
+ (*nw)->externalConfig(nc);
return nc;
}
return (ZT_VirtualNetworkConfig *)0;
@@ -478,8 +504,11 @@ ZT_VirtualNetworkList *Node::networks() const
nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList));
nl->networkCount = 0;
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n)
- n->second->externalConfig(&(nl->networks[nl->networkCount++]));
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > *>(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ (*v)->externalConfig(&(nl->networks[nl->networkCount++]));
return nl;
}
@@ -508,247 +537,188 @@ void Node::clearLocalInterfaceAddresses()
_directPaths.clear();
}
+int Node::sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
+{
+ try {
+ if (RR->identity.address().toInt() != dest) {
+ Packet outp(Address(dest),RR->identity.address(),Packet::VERB_USER_MESSAGE);
+ outp.append(typeId);
+ outp.append(data,len);
+ outp.compress();
+ RR->sw->send(tptr,outp,true);
+ return 1;
+ }
+ } catch ( ... ) {}
+ return 0;
+}
+
void Node::setNetconfMaster(void *networkControllerInstance)
{
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
+ if (networkControllerInstance)
+ RR->localNetworkController->init(RR->identity,this);
}
-ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+/****************************************************************************/
+/* Node methods used only within node/ */
+/****************************************************************************/
+
+bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress)
{
- if (test->hopCount > 0) {
- try {
- Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST);
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)((test->reportAtEveryHop != 0) ? 0x03 : 0x02));
- outp.append((uint64_t)test->timestamp);
- outp.append((uint64_t)test->testId);
- outp.append((uint16_t)0); // originator credential length, updated later
- if (test->credentialNetworkId) {
- outp.append((uint8_t)0x01);
- outp.append((uint64_t)test->credentialNetworkId);
- outp.setAt<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 23,(uint16_t)9);
- }
- outp.append((uint16_t)0);
- C25519::Signature sig(RR->identity.sign(reinterpret_cast<const char *>(outp.data()) + ZT_PACKET_IDX_PAYLOAD,outp.size() - ZT_PACKET_IDX_PAYLOAD));
- outp.append((uint16_t)sig.size());
- outp.append(sig.data,(unsigned int)sig.size());
- outp.append((uint16_t)0); // originator doesn't need an extra credential, since it's the originator
- for(unsigned int h=1;h<test->hopCount;++h) {
- outp.append((uint8_t)0);
- outp.append((uint8_t)(test->hops[h].breadth & 0xff));
- for(unsigned int a=0;a<test->hops[h].breadth;++a)
- Address(test->hops[h].addresses[a]).appendTo(outp);
- }
+ if (!Path::isAddressValidForPath(remoteAddress))
+ return false;
- for(unsigned int a=0;a<test->hops[0].breadth;++a) {
- outp.newInitializationVector();
- outp.setDestination(Address(test->hops[0].addresses[a]));
- RR->sw->send(outp,true,0);
- }
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
- }
- }
+ if (RR->topology->isProhibitedEndpoint(ztaddr,remoteAddress))
+ return false;
{
- test->_internalPtr = reinterpret_cast<void *>(reportCallback);
- Mutex::Lock _l(_circuitTests_m);
- if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end())
- _circuitTests.push_back(test);
+ Mutex::Lock _l(_networks_m);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v)) {
+ if ((*v)->hasConfig()) {
+ for(unsigned int k=0;k<(*v)->config().staticIpCount;++k) {
+ if ((*v)->config().staticIps[k].containsAddress(remoteAddress))
+ return false;
+ }
+ }
+ }
}
- return ZT_RESULT_OK;
-}
-
-void Node::circuitTestEnd(ZT_CircuitTest *test)
-{
- Mutex::Lock _l(_circuitTests_m);
- for(;;) {
- std::vector< ZT_CircuitTest * >::iterator ct(std::find(_circuitTests.begin(),_circuitTests.end(),test));
- if (ct == _circuitTests.end())
- break;
- else _circuitTests.erase(ct);
- }
+ return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
}
-ZT_ResultCode Node::clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
+uint64_t Node::prng()
{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- return ZT_RESULT_ERROR_BAD_PARAMETER;
-
- std::vector<InetAddress> eps;
- for(unsigned int i=0;i<numZeroTierPhysicalEndpoints;++i)
- eps.push_back(InetAddress(zeroTierPhysicalEndpoints[i]));
- std::sort(eps.begin(),eps.end());
- RR->cluster = new Cluster(RR,myId,eps,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
-
- return ZT_RESULT_OK;
-#else
- return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
-#endif
+ // https://en.wikipedia.org/wiki/Xorshift#xorshift.2B
+ uint64_t x = _prngState[0];
+ const uint64_t y = _prngState[1];
+ _prngState[0] = y;
+ x ^= x << 23;
+ const uint64_t z = x ^ y ^ (x >> 17) ^ (y >> 26);
+ _prngState[1] = z;
+ return z + y;
}
-ZT_ResultCode Node::clusterAddMember(unsigned int memberId)
+ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork, const ZT_PhysicalPathConfiguration *pathConfig)
{
-#ifdef ZT_ENABLE_CLUSTER
- if (!RR->cluster)
- return ZT_RESULT_ERROR_BAD_PARAMETER;
- RR->cluster->addMember((uint16_t)memberId);
+ RR->topology->setPhysicalPathConfiguration(pathNetwork,pathConfig);
return ZT_RESULT_OK;
-#else
- return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
-#endif
-}
-
-void Node::clusterRemoveMember(unsigned int memberId)
-{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->removeMember((uint16_t)memberId);
-#endif
}
-void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len)
+World Node::planet() const
{
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->handleIncomingStateMessage(msg,len);
-#endif
+ return RR->topology->planet();
}
-void Node::clusterStatus(ZT_ClusterStatus *cs)
+std::vector<World> Node::moons() const
{
- if (!cs)
- return;
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->status(*cs);
- else
-#endif
- memset(cs,0,sizeof(ZT_ClusterStatus));
+ return RR->topology->moons();
}
-void Node::backgroundThreadMain()
+void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
{
- ++RR->dpEnabled;
- for(;;) {
+ if (destination == RR->identity.address()) {
+ SharedPtr<Network> n(network(nwid));
+ if (!n) return;
+ n->setConfiguration((void *)0,nc,true);
+ } else {
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
- if (RR->dp->process() < 0)
- break;
- } catch ( ... ) {} // sanity check -- should not throw
- }
- --RR->dpEnabled;
-}
+ if (nc.toDictionary(*dconf,sendLegacyFormatConfig)) {
+ uint64_t configUpdateId = prng();
+ if (!configUpdateId) ++configUpdateId;
+
+ const unsigned int totalSize = dconf->sizeBytes();
+ unsigned int chunkIndex = 0;
+ while (chunkIndex < totalSize) {
+ const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_PROTO_MAX_PACKET_LENGTH - (ZT_PACKET_IDX_PAYLOAD + 256)));
+ Packet outp(destination,RR->identity.address(),(requestPacketId) ? Packet::VERB_OK : Packet::VERB_NETWORK_CONFIG);
+ if (requestPacketId) {
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ }
-/****************************************************************************/
-/* Node methods used only within node/ */
-/****************************************************************************/
+ const unsigned int sigStart = outp.size();
+ outp.append(nwid);
+ outp.append((uint16_t)chunkLen);
+ outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
-std::string Node::dataStoreGet(const char *name)
-{
- char buf[1024];
- std::string r;
- unsigned long olen = 0;
- do {
- long n = _dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
- if (n <= 0)
- return std::string();
- r.append(buf,n);
- } while (r.length() < olen);
- return r;
-}
+ outp.append((uint8_t)0); // no flags
+ outp.append((uint64_t)configUpdateId);
+ outp.append((uint32_t)totalSize);
+ outp.append((uint32_t)chunkIndex);
-bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress)
-{
- if (!Path::isAddressValidForPath(remoteAddress))
- return false;
+ C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
+ outp.append((uint8_t)1);
+ outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
+ outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
- {
- Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->second->hasConfig()) {
- for(unsigned int k=0;k<i->second->config().staticIpCount;++k) {
- if (i->second->config().staticIps[k].containsAddress(remoteAddress))
- return false;
+ outp.compress();
+ RR->sw->send((void *)0,outp,true);
+ chunkIndex += chunkLen;
}
}
+ delete dconf;
+ } catch ( ... ) {
+ delete dconf;
+ throw;
}
}
-
- if (_pathCheckFunction)
- return (_pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0);
- else return true;
}
-#ifdef ZT_TRACE
-void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
+void Node::ncSendRevocation(const Address &destination,const Revocation &rev)
{
- static Mutex traceLock;
-
- va_list ap;
- char tmp1[1024],tmp2[1024],tmp3[256];
-
- Mutex::Lock _l(traceLock);
-
- time_t now = (time_t)(_now / 1000ULL);
-#ifdef __WINDOWS__
- ctime_s(tmp3,sizeof(tmp3),&now);
- char *nowstr = tmp3;
-#else
- char *nowstr = ctime_r(&now,tmp3);
-#endif
- unsigned long nowstrlen = (unsigned long)strlen(nowstr);
- if (nowstr[nowstrlen-1] == '\n')
- nowstr[--nowstrlen] = (char)0;
- if (nowstr[nowstrlen-1] == '\r')
- nowstr[--nowstrlen] = (char)0;
-
- va_start(ap,fmt);
- vsnprintf(tmp2,sizeof(tmp2),fmt,ap);
- va_end(ap);
- tmp2[sizeof(tmp2)-1] = (char)0;
-
- Utils::snprintf(tmp1,sizeof(tmp1),"[%s] %s:%u %s",nowstr,module,line,tmp2);
- postEvent(ZT_EVENT_TRACE,tmp1);
-}
-#endif // ZT_TRACE
-
-uint64_t Node::prng()
-{
- unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t)));
- if (!p)
- _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
- return _prngStream[p];
-}
+ if (destination == RR->identity.address()) {
+ SharedPtr<Network> n(network(rev.networkId()));
+ if (!n) return;
+ n->addCredential((void *)0,RR->identity.address(),rev);
+ } else {
+ Packet outp(destination,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ outp.append((uint8_t)0x00);
+ outp.append((uint16_t)0);
+ outp.append((uint16_t)0);
+ outp.append((uint16_t)1);
+ rev.serialize(outp);
+ outp.append((uint16_t)0);
+ RR->sw->send((void *)0,outp,true);
+ }
+}
+
+void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode)
+{
+ if (destination == RR->identity.address()) {
+ SharedPtr<Network> n(network(nwid));
+ if (!n) return;
+ switch(errorCode) {
+ case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
+ case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
+ n->setNotFound();
+ break;
+ case NetworkController::NC_ERROR_ACCESS_DENIED:
+ n->setAccessDenied();
+ break;
-void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
-{
- std::vector< ZT_CircuitTest * > toNotify;
- {
- Mutex::Lock _l(_circuitTests_m);
- for(std::vector< ZT_CircuitTest * >::iterator i(_circuitTests.begin());i!=_circuitTests.end();++i) {
- if ((*i)->testId == report->testId)
- toNotify.push_back(*i);
+ default: break;
}
- }
- for(std::vector< ZT_CircuitTest * >::iterator i(toNotify.begin());i!=toNotify.end();++i)
- (reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
-}
-
-void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
-{
- RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
+ } else if (requestPacketId) {
+ Packet outp(destination,RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+ outp.append(requestPacketId);
+ switch(errorCode) {
+ //case NetworkController::NC_ERROR_OBJECT_NOT_FOUND:
+ //case NetworkController::NC_ERROR_INTERNAL_SERVER_ERROR:
+ default:
+ outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
+ break;
+ case NetworkController::NC_ERROR_ACCESS_DENIED:
+ outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
+ break;
+ }
+ outp.append(nwid);
+ RR->sw->send((void *)0,outp,true);
+ } // else we can't send an ERROR() in response to nothing, so discard
}
} // namespace ZeroTier
@@ -759,21 +729,11 @@ void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_
extern "C" {
-enum ZT_ResultCode ZT_Node_new(
- ZT_Node **node,
- void *uptr,
- uint64_t now,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback)
+enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now)
{
*node = (ZT_Node *)0;
try {
- *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback));
+ *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,tptr,callbacks,now));
return ZT_RESULT_OK;
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
@@ -793,15 +753,16 @@ void ZT_Node_delete(ZT_Node *node)
enum ZT_ResultCode ZT_Node_processWirePacket(
ZT_Node *node,
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(now,localAddress,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localSocket,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -811,7 +772,8 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
ZT_Node *node,
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -819,10 +781,10 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline)
+ volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(tptr,now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -830,10 +792,10 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
}
}
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(now,nextBackgroundTaskDeadline);
+ return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(tptr,now,nextBackgroundTaskDeadline);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -841,10 +803,10 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
}
}
-enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
+enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->join(nwid,uptr);
+ return reinterpret_cast<ZeroTier::Node *>(node)->join(nwid,uptr,tptr);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -852,10 +814,10 @@ enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
}
}
-enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr)
+enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->leave(nwid,uptr);
+ return reinterpret_cast<ZeroTier::Node *>(node)->leave(nwid,uptr,tptr);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -863,10 +825,10 @@ enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr)
}
}
-enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
+enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->multicastSubscribe(nwid,multicastGroup,multicastAdi);
+ return reinterpret_cast<ZeroTier::Node *>(node)->multicastSubscribe(tptr,nwid,multicastGroup,multicastAdi);
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
@@ -885,6 +847,24 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
}
}
+enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
+{
+ try {
+ return reinterpret_cast<ZeroTier::Node *>(node)->orbit(tptr,moonWorldId,moonSeed);
+ } catch ( ... ) {
+ return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ }
+}
+
+enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
+{
+ try {
+ return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(tptr,moonWorldId);
+ } catch ( ... ) {
+ return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ }
+}
+
uint64_t ZT_Node_address(ZT_Node *node)
{
return reinterpret_cast<ZeroTier::Node *>(node)->address();
@@ -947,93 +927,31 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node)
} catch ( ... ) {}
}
-void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->setNetconfMaster(networkControllerInstance);
- } catch ( ... ) {}
-}
-
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(test,reportCallback);
+ return reinterpret_cast<ZeroTier::Node *>(node)->sendUserMessage(tptr,dest,typeId,data,len);
} catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ return 0;
}
}
-void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test)
+void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
{
try {
- reinterpret_cast<ZeroTier::Node *>(node)->circuitTestEnd(test);
+ reinterpret_cast<ZeroTier::Node *>(node)->setNetconfMaster(networkControllerInstance);
} catch ( ... ) {}
}
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
-{
- try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
- }
-}
-
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId)
+enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterAddMember(memberId);
+ return reinterpret_cast<ZeroTier::Node *>(node)->setPhysicalPathConfiguration(pathNetwork,pathConfig);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterRemoveMember(memberId);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterHandleIncomingMessage(msg,len);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterStatus(cs);
- } catch ( ... ) {}
-}
-
-void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
- } catch ( ... ) {}
-}
-
-void ZT_Node_backgroundThreadMain(ZT_Node *node)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->backgroundThreadMain();
- } catch ( ... ) {}
-}
-
void ZT_version(int *major,int *minor,int *revision)
{
if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;
diff --git a/node/Node.hpp b/node/Node.hpp
index 0a39d1ee..79284b63 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_NODE_HPP
@@ -24,6 +32,7 @@
#include <string.h>
#include <map>
+#include <vector>
#include "Constants.hpp"
@@ -36,48 +45,47 @@
#include "Network.hpp"
#include "Path.hpp"
#include "Salsa20.hpp"
+#include "NetworkController.hpp"
+#include "Hashtable.hpp"
-#undef TRACE
-#ifdef ZT_TRACE
-#define TRACE(f,...) RR->node->postTrace(__FILE__,__LINE__,f,##__VA_ARGS__)
-#else
-#define TRACE(f,...) {}
-#endif
+// Bit mask for "expecting reply" hash
+#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
+#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
namespace ZeroTier {
+class World;
+
/**
* Implementation of Node object as defined in CAPI
*
* The pointer returned by ZT_Node_new() is an instance of this class.
*/
-class Node
+class Node : public NetworkController::Sender
{
public:
- Node(
- uint64_t now,
- void *uptr,
- ZT_DataStoreGetFunction dataStoreGetFunction,
- ZT_DataStorePutFunction dataStorePutFunction,
- ZT_WirePacketSendFunction wirePacketSendFunction,
- ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
- ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
- ZT_PathCheckFunction pathCheckFunction,
- ZT_EventCallback eventCallback);
-
- ~Node();
+ Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now);
+ virtual ~Node();
+
+ // Get rid of alignment warnings on 32-bit Windows and possibly improve performance
+#ifdef __WINDOWS__
+ void * operator new(size_t i) { return _mm_malloc(i,16); }
+ void operator delete(void* p) { _mm_free(p); }
+#endif
// Public API Functions ----------------------------------------------------
ZT_ResultCode processWirePacket(
- uint64_t now,
- const struct sockaddr_storage *localAddress,
+ void *tptr,
+ int64_t now,
+ int64_t localSocket,
const struct sockaddr_storage *remoteAddress,
const void *packetData,
unsigned int packetLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
+ volatile int64_t *nextBackgroundTaskDeadline);
ZT_ResultCode processVirtualNetworkFrame(
- uint64_t now,
+ void *tptr,
+ int64_t now,
uint64_t nwid,
uint64_t sourceMac,
uint64_t destMac,
@@ -85,12 +93,14 @@ public:
unsigned int vlanId,
const void *frameData,
unsigned int frameLength,
- volatile uint64_t *nextBackgroundTaskDeadline);
- ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
- ZT_ResultCode join(uint64_t nwid,void *uptr);
- ZT_ResultCode leave(uint64_t nwid,void **uptr);
- ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ volatile int64_t *nextBackgroundTaskDeadline);
+ ZT_ResultCode processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline);
+ ZT_ResultCode join(uint64_t nwid,void *uptr,void *tptr);
+ ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
+ ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+ ZT_ResultCode orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
+ ZT_ResultCode deorbit(void *tptr,uint64_t moonWorldId);
uint64_t address() const;
void status(ZT_NodeStatus *status) const;
ZT_PeerList *peers() const;
@@ -99,80 +109,32 @@ public:
void freeQueryResult(void *qr);
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
void clearLocalInterfaceAddresses();
+ int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
void setNetconfMaster(void *networkControllerInstance);
- ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
- void circuitTestEnd(ZT_CircuitTest *test);
- ZT_ResultCode clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
- ZT_ResultCode clusterAddMember(unsigned int memberId);
- void clusterRemoveMember(unsigned int memberId);
- void clusterHandleIncomingMessage(const void *msg,unsigned int len);
- void clusterStatus(ZT_ClusterStatus *cs);
- void backgroundThreadMain();
// Internal functions ------------------------------------------------------
- /**
- * Convenience threadMain() for easy background thread launch
- *
- * This allows background threads to be launched with Thread::start
- * that will run against this node.
- */
- inline void threadMain() throw() { this->backgroundThreadMain(); }
-
- /**
- * @return Time as of last call to run()
- */
- inline uint64_t now() const throw() { return _now; }
+ inline int64_t now() const { return _now; }
- /**
- * Enqueue a ZeroTier message to be sent
- *
- * @param localAddress Local address
- * @param addr Destination address
- * @param data Packet data
- * @param len Packet length
- * @param ttl Desired TTL (default: 0 for unchanged/default TTL)
- * @return True if packet appears to have been sent
- */
- inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
+ inline bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
{
- return (_wirePacketSendFunction(
+ return (_cb.wirePacketSendFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
- reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
+ tPtr,
+ localSocket,
reinterpret_cast<const struct sockaddr_storage *>(&addr),
data,
len,
ttl) == 0);
}
- /**
- * Enqueue a frame to be injected into a tap device (port)
- *
- * @param nwid Network ID
- * @param nuptr Network user ptr
- * @param source Source MAC
- * @param dest Destination MAC
- * @param etherType 16-bit ethernet type
- * @param vlanId VLAN ID or 0 if none
- * @param data Frame data
- * @param len Frame length
- */
- inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+ inline void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
- _virtualNetworkFrameFunction(
+ _cb.virtualNetworkFrameFunction(
reinterpret_cast<ZT_Node *>(this),
_uPtr,
+ tPtr,
nwid,
nuptr,
source.toInt(),
@@ -183,123 +145,150 @@ public:
len);
}
- /**
- * @param localAddress Local address
- * @param remoteAddress Remote address
- * @return True if path should be used
- */
- bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress);
-
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- return _network(nwid);
+ const SharedPtr<Network> *n = _networks.get(nwid);
+ if (n)
+ return *n;
+ return SharedPtr<Network>();
}
inline bool belongsToNetwork(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return true;
- }
- return false;
+ return _networks.contains(nwid);
}
inline std::vector< SharedPtr<Network> > allNetworks() const
{
std::vector< SharedPtr<Network> > nw;
Mutex::Lock _l(_networks_m);
- nw.reserve(_networks.size());
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i)
- nw.push_back(i->second);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > * >(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ nw.push_back(*v);
return nw;
}
- /**
- * @return Potential direct paths to me a.k.a. local interface addresses
- */
inline std::vector<InetAddress> directPaths() const
{
Mutex::Lock _l(_directPaths_m);
return _directPaths;
}
- inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
- inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
- inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
- std::string dataStoreGet(const char *name);
+ inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
+
+ inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
+
+ inline bool online() const { return _online; }
+
+ inline int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); }
+ inline void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); }
+ inline void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); }
+
+ bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress);
+ inline bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
+
+ uint64_t prng();
+ ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
+
+ World planet() const;
+ std::vector<World> moons() const;
+
+ inline const Identity &identity() const { return _RR.identity; }
/**
- * Post an event to the external user
+ * Register that we are expecting a reply to a packet ID
*
- * @param ev Event type
- * @param md Meta-data (default: NULL/none)
+ * This only uses the most significant bits of the packet ID, both to save space
+ * and to avoid using the higher bits that can be modified during armor() to
+ * mask against the packet send counter used for QoS detection.
+ *
+ * @param packetId Packet ID to expect reply to
*/
- inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
+ inline void expectReplyTo(const uint64_t packetId)
+ {
+ const unsigned long pid2 = (unsigned long)(packetId >> 32);
+ const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
+ _expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = (uint32_t)pid2;
+ }
/**
- * Update virtual network port configuration
+ * Check whether a given packet ID is something we are expecting a reply to
+ *
+ * This only uses the most significant bits of the packet ID, both to save space
+ * and to avoid using the higher bits that can be modified during armor() to
+ * mask against the packet send counter used for QoS detection.
*
- * @param nwid Network ID
- * @param nuptr Network user ptr
- * @param op Configuration operation
- * @param nc Network configuration
+ * @param packetId Packet ID to check
+ * @return True if we're expecting a reply
*/
- inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
-
- inline bool online() const throw() { return _online; }
-
-#ifdef ZT_TRACE
- void postTrace(const char *module,unsigned int line,const char *fmt,...);
-#endif
-
- uint64_t prng();
- void postCircuitTestReport(const ZT_CircuitTestReport *report);
- void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
+ inline bool expectingReplyTo(const uint64_t packetId) const
+ {
+ const uint32_t pid2 = (uint32_t)(packetId >> 32);
+ const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
+ for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
+ if (_expectingRepliesTo[bucket][i] == pid2)
+ return true;
+ }
+ return false;
+ }
-private:
- inline SharedPtr<Network> _network(uint64_t nwid) const
+ /**
+ * Check whether we should do potentially expensive identity verification (rate limit)
+ *
+ * @param now Current time
+ * @param from Source address of packet
+ * @return True if within rate limits
+ */
+ inline bool rateGateIdentityVerification(const int64_t now,const InetAddress &from)
{
- // assumes _networks_m is locked
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return i->second;
+ unsigned long iph = from.rateGateHash();
+ if ((now - _lastIdentityVerification[iph]) >= ZT_IDENTITY_VALIDATION_SOURCE_RATE_LIMIT) {
+ _lastIdentityVerification[iph] = now;
+ return true;
}
- return SharedPtr<Network>();
+ return false;
}
+ virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig);
+ virtual void ncSendRevocation(const Address &destination,const Revocation &rev);
+ virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
+
+ inline const Address &remoteTraceTarget() const { return _remoteTraceTarget; }
+ inline Trace::Level remoteTraceLevel() const { return _remoteTraceLevel; }
+
+private:
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
-
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
+ ZT_Node_Callbacks _cb;
- ZT_DataStoreGetFunction _dataStoreGetFunction;
- ZT_DataStorePutFunction _dataStorePutFunction;
- ZT_WirePacketSendFunction _wirePacketSendFunction;
- ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
- ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
- ZT_PathCheckFunction _pathCheckFunction;
- ZT_EventCallback _eventCallback;
+ // For tracking packet IDs to filter out OK/ERROR replies to packets we did not send
+ uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
+ uint32_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
- std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
- Mutex _networks_m;
+ // Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
+ int64_t _lastIdentityVerification[16384];
- std::vector< ZT_CircuitTest * > _circuitTests;
- Mutex _circuitTests_m;
+ Hashtable< uint64_t,SharedPtr<Network> > _networks;
+ Mutex _networks_m;
std::vector<InetAddress> _directPaths;
Mutex _directPaths_m;
Mutex _backgroundTasksLock;
- unsigned int _prngStreamPtr;
- Salsa20 _prng;
- uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
+ Address _remoteTraceTarget;
+ enum Trace::Level _remoteTraceLevel;
- uint64_t _now;
- uint64_t _lastPingCheck;
- uint64_t _lastHousekeepingRun;
+ volatile int64_t _now;
+ int64_t _lastPingCheck;
+ int64_t _lastHousekeepingRun;
+ int64_t _lastMemoizedTraceSettings;
+ volatile int64_t _prngState[2];
bool _online;
};
diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp
index eea1132c..d7a7b4d8 100644
--- a/node/OutboundMulticast.cpp
+++ b/node/OutboundMulticast.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Constants.hpp"
@@ -21,8 +29,9 @@
#include "OutboundMulticast.hpp"
#include "Switch.hpp"
#include "Network.hpp"
-#include "CertificateOfMembership.hpp"
#include "Node.hpp"
+#include "Peer.hpp"
+#include "Topology.hpp"
namespace ZeroTier {
@@ -30,7 +39,7 @@ void OutboundMulticast::init(
const RuntimeEnvironment *RR,
uint64_t timestamp,
uint64_t nwid,
- const CertificateOfMembership *com,
+ bool disableCompression,
unsigned int limit,
unsigned int gatherLimit,
const MAC &src,
@@ -39,75 +48,51 @@ void OutboundMulticast::init(
const void *payload,
unsigned int len)
{
+ uint8_t flags = 0;
+
_timestamp = timestamp;
_nwid = nwid;
+ if (src) {
+ _macSrc = src;
+ flags |= 0x04;
+ } else {
+ _macSrc.fromAddress(RR->identity.address(),nwid);
+ }
+ _macDest = dest.mac();
_limit = limit;
+ _frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
+ _etherType = etherType;
- uint8_t flags = 0;
if (gatherLimit) flags |= 0x02;
- if (src) flags |= 0x04;
-
- /*
- TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u com==%d",
- (unsigned long long)this,
- nwid,
- dest.toString().c_str(),
- limit,
- gatherLimit,
- (src) ? src.toString().c_str() : MAC(RR->identity.address(),nwid).toString().c_str(),
- dest.toString().c_str(),
- len,
- (com) ? 1 : 0);
- */
-
- _packetNoCom.setSource(RR->identity.address());
- _packetNoCom.setVerb(Packet::VERB_MULTICAST_FRAME);
- _packetNoCom.append((uint64_t)nwid);
- _packetNoCom.append(flags);
- if (gatherLimit) _packetNoCom.append((uint32_t)gatherLimit);
- if (src) src.appendTo(_packetNoCom);
- dest.mac().appendTo(_packetNoCom);
- _packetNoCom.append((uint32_t)dest.adi());
- _packetNoCom.append((uint16_t)etherType);
- _packetNoCom.append(payload,len);
- _packetNoCom.compress();
- if (com) {
- _haveCom = true;
- flags |= 0x01;
+ _packet.setSource(RR->identity.address());
+ _packet.setVerb(Packet::VERB_MULTICAST_FRAME);
+ _packet.append((uint64_t)nwid);
+ _packet.append(flags);
+ if (gatherLimit) _packet.append((uint32_t)gatherLimit);
+ if (src) src.appendTo(_packet);
+ dest.mac().appendTo(_packet);
+ _packet.append((uint32_t)dest.adi());
+ _packet.append((uint16_t)etherType);
+ _packet.append(payload,_frameLen);
+ if (!disableCompression)
+ _packet.compress();
- _packetWithCom.setSource(RR->identity.address());
- _packetWithCom.setVerb(Packet::VERB_MULTICAST_FRAME);
- _packetWithCom.append((uint64_t)nwid);
- _packetWithCom.append(flags);
- com->serialize(_packetWithCom);
- if (gatherLimit) _packetWithCom.append((uint32_t)gatherLimit);
- if (src) src.appendTo(_packetWithCom);
- dest.mac().appendTo(_packetWithCom);
- _packetWithCom.append((uint32_t)dest.adi());
- _packetWithCom.append((uint16_t)etherType);
- _packetWithCom.append(payload,len);
- _packetWithCom.compress();
- } else _haveCom = false;
+ ZT_FAST_MEMCPY(_frameData,payload,_frameLen);
}
-void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
+void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
{
- if (_haveCom) {
- SharedPtr<Peer> peer(RR->topology->getPeer(toAddr));
- if ( (!peer) || (peer->needsOurNetworkMembershipCertificate(_nwid,RR->node->now(),true)) ) {
- //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str());
- _packetWithCom.newInitializationVector();
- _packetWithCom.setDestination(toAddr);
- RR->sw->send(_packetWithCom,true,_nwid);
- return;
- }
- }
+ const SharedPtr<Network> nw(RR->node->network(_nwid));
+ const Address toAddr2(toAddr);
+ if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
+ _packet.newInitializationVector();
+ _packet.setDestination(toAddr2);
+ RR->node->expectReplyTo(_packet.packetId());
- //TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str());
- _packetNoCom.newInitializationVector();
- _packetNoCom.setDestination(toAddr);
- RR->sw->send(_packetNoCom,true,_nwid);
+ Packet tmp(_packet); // make a copy of packet so as not to garble the original -- GitHub issue #461
+ RR->sw->send(tPtr,tmp,true);
+ }
}
} // namespace ZeroTier
diff --git a/node/OutboundMulticast.hpp b/node/OutboundMulticast.hpp
index 3818172e..a735f52b 100644
--- a/node/OutboundMulticast.hpp
+++ b/node/OutboundMulticast.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OUTBOUNDMULTICAST_HPP
@@ -56,7 +64,7 @@ public:
* @param RR Runtime environment
* @param timestamp Creation time
* @param nwid Network ID
- * @param com Certificate of membership or NULL if none available
+ * @param disableCompression Disable compression of frame payload
* @param limit Multicast limit for desired number of packets to send
* @param gatherLimit Number to lazily/implicitly gather with this frame or 0 for none
* @param src Source MAC address of frame or NULL to imply compute from sender ZT address
@@ -70,7 +78,7 @@ public:
const RuntimeEnvironment *RR,
uint64_t timestamp,
uint64_t nwid,
- const CertificateOfMembership *com,
+ bool disableCompression,
unsigned int limit,
unsigned int gatherLimit,
const MAC &src,
@@ -82,62 +90,80 @@ public:
/**
* @return Multicast creation time
*/
- inline uint64_t timestamp() const throw() { return _timestamp; }
+ inline uint64_t timestamp() const { return _timestamp; }
/**
* @param now Current time
* @return True if this multicast is expired (has exceeded transmit timeout)
*/
- inline bool expired(uint64_t now) const throw() { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
+ inline bool expired(int64_t now) const { return ((now - _timestamp) >= ZT_MULTICAST_TRANSMIT_TIMEOUT); }
/**
* @return True if this outbound multicast has been sent to enough peers
*/
- inline bool atLimit() const throw() { return (_alreadySentTo.size() >= _limit); }
+ inline bool atLimit() const { return (_alreadySentTo.size() >= _limit); }
/**
* Just send without checking log
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param toAddr Destination address
*/
- void sendOnly(const RuntimeEnvironment *RR,const Address &toAddr);
+ void sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr);
/**
* Just send and log but do not check sent log
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param toAddr Destination address
*/
- inline void sendAndLog(const RuntimeEnvironment *RR,const Address &toAddr)
+ inline void sendAndLog(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
+ {
+ _alreadySentTo.push_back(toAddr);
+ sendOnly(RR,tPtr,toAddr);
+ }
+
+ /**
+ * Log an address as having been used so we will not send there in the future
+ *
+ * @param toAddr Address to log as sent
+ */
+ inline void logAsSent(const Address &toAddr)
{
_alreadySentTo.push_back(toAddr);
- sendOnly(RR,toAddr);
}
/**
* Try to send this to a given peer if it hasn't been sent to them already
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param toAddr Destination address
* @return True if address is new and packet was sent to switch, false if duplicate
*/
- inline bool sendIfNew(const RuntimeEnvironment *RR,const Address &toAddr)
+ inline bool sendIfNew(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
{
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
- sendAndLog(RR,toAddr);
+ sendAndLog(RR,tPtr,toAddr);
return true;
- } else return false;
+ } else {
+ return false;
+ }
}
private:
uint64_t _timestamp;
uint64_t _nwid;
+ MAC _macSrc;
+ MAC _macDest;
unsigned int _limit;
- Packet _packetNoCom;
- Packet _packetWithCom;
+ unsigned int _frameLen;
+ unsigned int _etherType;
+ Packet _packet;
std::vector<Address> _alreadySentTo;
- bool _haveCom;
+ uint8_t _frameData[ZT_MAX_MTU];
};
} // namespace ZeroTier
diff --git a/node/Packet.cpp b/node/Packet.cpp
index 3330a927..2eeceffa 100644
--- a/node/Packet.cpp
+++ b/node/Packet.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,143 +14,1013 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
#include "Packet.hpp"
+#ifdef ZT_USE_X64_ASM_SALSA2012
+#include "../ext/x64-salsa2012-asm/salsa2012.h"
+#endif
+#ifdef ZT_USE_ARM32_NEON_ASM_SALSA2012
+#include "../ext/arm32-neon-salsa2012-asm/salsa2012.h"
+#endif
+
+#ifdef _MSC_VER
+#define FORCE_INLINE static __forceinline
+#include <intrin.h>
+#pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#pragma warning(disable : 4293) /* disable: C4293: too large shift (32-bits) */
+#else
+#define FORCE_INLINE static inline
+#endif
+
namespace ZeroTier {
-const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+/************************************************************************** */
+
+/* Set up macros for fast single-pass ASM Salsa20/12 crypto, if we have it */
+
+// x64 SSE crypto
+#ifdef ZT_USE_X64_ASM_SALSA2012
+#define ZT_HAS_FAST_CRYPTO() (true)
+#define ZT_FAST_SINGLE_PASS_SALSA2012(b,l,n,k) zt_salsa2012_amd64_xmm6(reinterpret_cast<unsigned char *>(b),(l),reinterpret_cast<const unsigned char *>(n),reinterpret_cast<const unsigned char *>(k))
+#endif
+
+// ARM (32-bit) NEON crypto (must be detected)
+#ifdef ZT_USE_ARM32_NEON_ASM_SALSA2012
+class _FastCryptoChecker
+{
+public:
+ _FastCryptoChecker() : canHas(zt_arm_has_neon()) {}
+ bool canHas;
+};
+static const _FastCryptoChecker _ZT_FAST_CRYPTO_CHECK;
+#define ZT_HAS_FAST_CRYPTO() (_ZT_FAST_CRYPTO_CHECK.canHas)
+#define ZT_FAST_SINGLE_PASS_SALSA2012(b,l,n,k) zt_salsa2012_armneon3_xor(reinterpret_cast<unsigned char *>(b),(const unsigned char *)0,(l),reinterpret_cast<const unsigned char *>(n),reinterpret_cast<const unsigned char *>(k))
+#endif
+
+// No fast crypto available
+#ifndef ZT_HAS_FAST_CRYPTO
+#define ZT_HAS_FAST_CRYPTO() (false)
+#define ZT_FAST_SINGLE_PASS_SALSA2012(b,l,n,k) {}
+#endif
+
+/************************************************************************** */
+
+/* LZ4 is shipped encapsulated into Packet in an anonymous namespace.
+ *
+ * We're doing this as a deliberate workaround for various Linux distribution
+ * policies that forbid static linking of support libraries.
+ *
+ * The reason is that relying on distribution versions of LZ4 has been too
+ * big a source of bugs and compatibility issues. The LZ4 API is not stable
+ * enough across versions, and dependency hell ensues. So fark it. */
+
+/* Needless to say the code in this anonymous namespace should be considered
+ * BSD 2-clause licensed. */
+
+namespace {
+
+/* lz4.h ------------------------------------------------------------------ */
+
+/*
+ * LZ4 - Fast LZ compression algorithm
+ * Header File
+ * Copyright (C) 2011-2016, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER 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.
+
+ You can contact the author at :
+ - LZ4 homepage : http://www.lz4.org
+ - LZ4 source repository : https://github.com/lz4/lz4
+*/
-//#ifdef ZT_TRACE
-
-const char *Packet::verbString(Verb v)
- throw()
-{
- switch(v) {
- case VERB_NOP: return "NOP";
- case VERB_HELLO: return "HELLO";
- case VERB_ERROR: return "ERROR";
- case VERB_OK: return "OK";
- case VERB_WHOIS: return "WHOIS";
- case VERB_RENDEZVOUS: return "RENDEZVOUS";
- case VERB_FRAME: return "FRAME";
- case VERB_EXT_FRAME: return "EXT_FRAME";
- case VERB_ECHO: return "ECHO";
- case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
- case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
- case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
- case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
- case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
- case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
- case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
- case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
- case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
- case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK";
+/**
+ Introduction
+
+ LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+ scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+ multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+ The LZ4 compression library provides in-memory compression and decompression functions.
+ Compression can be done in:
+ - a single step (described as Simple Functions)
+ - a single step, reusing a context (described in Advanced Functions)
+ - unbounded multiple steps (described as Streaming compression)
+
+ lz4.h provides block compression functions. It gives full buffer control to user.
+ Decompressing an lz4-compressed block also requires metadata (such as compressed size).
+ Each application is free to encode such metadata in whichever way it wants.
+
+ An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
+ take care of encoding standard metadata alongside LZ4-compressed blocks.
+ If your application requires interoperability, it's recommended to use it.
+ A library is provided to take care of it, see lz4frame.h.
+*/
+
+#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
+#define LZ4_VERSION_MINOR 7 /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
+#define LZ4_MEMORY_USAGE 14
+#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
+
+static inline void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
+
+typedef struct {
+ uint32_t hashTable[LZ4_HASH_SIZE_U32];
+ uint32_t currentOffset;
+ uint32_t initCheck;
+ const uint8_t* dictionary;
+ uint8_t* bufferStart; /* obsolete, used for slideInputBuffer */
+ uint32_t dictSize;
+} LZ4_stream_t_internal;
+
+typedef struct {
+ const uint8_t* externalDict;
+ size_t extDictSize;
+ const uint8_t* prefixEnd;
+ size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+union LZ4_stream_u {
+ unsigned long long table[LZ4_STREAMSIZE_U64];
+ LZ4_stream_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_stream_t */
+
+#define LZ4_STREAMDECODESIZE_U64 4
+#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+union LZ4_streamDecode_u {
+ unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+ LZ4_streamDecode_t_internal internal_donotuse;
+} ; /* previously typedef'd to LZ4_streamDecode_t */
+
+#ifndef HEAPMODE
+#define HEAPMODE 0
+#endif
+
+#ifdef ZT_NO_TYPE_PUNNING
+#define LZ4_FORCE_MEMORY_ACCESS 0
+#else
+#define LZ4_FORCE_MEMORY_ACCESS 2
+#endif
+
+#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for Windows CE does not support Hardware bit count */
+#define LZ4_FORCE_SW_BITCOUNT
+#endif
+
+#ifndef FORCE_INLINE
+#define FORCE_INLINE static inline
+#endif
+
+#define ALLOCATOR(n,s) calloc(n,s)
+#define FREEMEM free
+#define MEM_INIT memset
+
+typedef uint8_t BYTE;
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+typedef uintptr_t uptrval;
+typedef uintptr_t reg_t;
+
+static inline unsigned LZ4_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+
+#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
+static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
+static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
+static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
+static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
+#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
+typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign;
+static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
+static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; }
+static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
+static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
+#else /* safe and portable access through memcpy() */
+static inline U16 LZ4_read16(const void* memPtr)
+{
+ U16 val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
+}
+static inline U32 LZ4_read32(const void* memPtr)
+{
+ U32 val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
+}
+static inline reg_t LZ4_read_ARCH(const void* memPtr)
+{
+ reg_t val; ZT_FAST_MEMCPY(&val, memPtr, sizeof(val)); return val;
+}
+static inline void LZ4_write16(void* memPtr, U16 value)
+{
+ ZT_FAST_MEMCPY(memPtr, &value, sizeof(value));
+}
+static inline void LZ4_write32(void* memPtr, U32 value)
+{
+ ZT_FAST_MEMCPY(memPtr, &value, sizeof(value));
+}
+#endif /* LZ4_FORCE_MEMORY_ACCESS */
+
+static inline U16 LZ4_readLE16(const void* memPtr)
+{
+ if (LZ4_isLittleEndian()) {
+ return LZ4_read16(memPtr);
+ } else {
+ const BYTE* p = (const BYTE*)memPtr;
+ return (U16)((U16)p[0] + (p[1]<<8));
}
- return "(unknown)";
-}
-
-const char *Packet::errorString(ErrorCode e)
- throw()
-{
- switch(e) {
- case ERROR_NONE: return "NONE";
- case ERROR_INVALID_REQUEST: return "INVALID_REQUEST";
- case ERROR_BAD_PROTOCOL_VERSION: return "BAD_PROTOCOL_VERSION";
- case ERROR_OBJ_NOT_FOUND: return "OBJECT_NOT_FOUND";
- case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
- case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
- case ERROR_NEED_MEMBERSHIP_CERTIFICATE: return "NEED_MEMBERSHIP_CERTIFICATE";
- case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED";
- case ERROR_UNWANTED_MULTICAST: return "UNWANTED_MULTICAST";
+}
+
+static inline void LZ4_writeLE16(void* memPtr, U16 value)
+{
+ if (LZ4_isLittleEndian()) {
+ LZ4_write16(memPtr, value);
+ } else {
+ BYTE* p = (BYTE*)memPtr;
+ p[0] = (BYTE) value;
+ p[1] = (BYTE)(value>>8);
}
- return "(unknown)";
}
-//#endif // ZT_TRACE
+static inline void LZ4_copy8(void* dst, const void* src)
+{
+ ZT_FAST_MEMCPY(dst,src,8);
+}
+
+static inline void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+ BYTE* d = (BYTE*)dstPtr;
+ const BYTE* s = (const BYTE*)srcPtr;
+ BYTE* const e = (BYTE*)dstEnd;
+ do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
+}
+
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (WILDCOPYLENGTH+MINMATCH)
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define MAXD_LOG 16
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+
+#define ML_BITS 4
+#define ML_MASK ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */
+
+static inline unsigned LZ4_NbCommonBytes (register reg_t val)
+{
+ if (LZ4_isLittleEndian()) {
+ if (sizeof(val)==8) {
+# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanForward64( &r, (U64)val );
+ return (int)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_ctzll((U64)val) >> 3);
+# else
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+ } else /* 32 bits */ {
+# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r;
+ _BitScanForward( &r, (U32)val );
+ return (int)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_ctz((U32)val) >> 3);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+ }
+ } else /* Big Endian CPU */ {
+ if (sizeof(val)==8) {
+# if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanReverse64( &r, val );
+ return (unsigned)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_clzll((U64)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } else /* 32 bits */ {
+# if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ unsigned long r = 0;
+ _BitScanReverse( &r, (unsigned long)val );
+ return (unsigned)(r>>3);
+# elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+ return (__builtin_clz((U32)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ }
+ }
+}
+
+#define STEPSIZE sizeof(reg_t)
+static inline unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+ const BYTE* const pStart = pIn;
+
+ while (likely(pIn<pInLimit-(STEPSIZE-1))) {
+ reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+ if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+ pIn += LZ4_NbCommonBytes(diff);
+ return (unsigned)(pIn - pStart);
+ }
+
+ if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+ if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+ if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
+ return (unsigned)(pIn - pStart);
+}
+
+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
+static const U32 LZ4_skipTrigger = 6; /* Increase this value ==> compression run slower on incompressible data */
+
+typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
+typedef enum { byPtr, byU32, byU16 } tableType_t;
+
+typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+static inline int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); }
+
+static inline U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+ if (tableType == byU16)
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+ else
+ return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+static inline U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+ static const U64 prime5bytes = 889523592379ULL;
+ static const U64 prime8bytes = 11400714785074694791ULL;
+ const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+ if (LZ4_isLittleEndian())
+ return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+ else
+ return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+}
+
+FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+ if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+ return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static inline void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+{
+ switch (tableType)
+ {
+ case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
+ case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
+ case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
+ }
+}
+
+FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+static inline const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
+ if (tableType == byU32) { const U32* const hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
+ { const U16* const hashTable = (U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */
+}
+
+FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+ U32 const h = LZ4_hashPosition(p, tableType);
+ return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+FORCE_INLINE int LZ4_compress_generic(
+ LZ4_stream_t_internal* const cctx,
+ const char* const source,
+ char* const dest,
+ const int inputSize,
+ const int maxOutputSize,
+ const limitedOutput_directive outputLimited,
+ const tableType_t tableType,
+ const dict_directive dict,
+ const dictIssue_directive dictIssue,
+ const U32 acceleration)
+{
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* base;
+ const BYTE* lowLimit;
+ const BYTE* const lowRefLimit = ip - cctx->dictSize;
+ const BYTE* const dictionary = cctx->dictionary;
+ const BYTE* const dictEnd = dictionary + cctx->dictSize;
+ const ptrdiff_t dictDelta = dictEnd - (const BYTE*)source;
+ const BYTE* anchor = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+ const BYTE* const mflimit = iend - MFLIMIT;
+ const BYTE* const matchlimit = iend - LASTLITERALS;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const olimit = op + maxOutputSize;
+
+ U32 forwardH;
+
+ /* Init conditions */
+ if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported inputSize, too large (or negative) */
+ switch(dict)
+ {
+ case noDict:
+ default:
+ base = (const BYTE*)source;
+ lowLimit = (const BYTE*)source;
+ break;
+ case withPrefix64k:
+ base = (const BYTE*)source - cctx->currentOffset;
+ lowLimit = (const BYTE*)source - cctx->dictSize;
+ break;
+ case usingExtDict:
+ base = (const BYTE*)source - cctx->currentOffset;
+ lowLimit = (const BYTE*)source;
+ break;
+ }
+ if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0; /* Size too large (not within 64K limit) */
+ if (inputSize<LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */
+
+ /* First Byte */
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+ /* Main Loop */
+ for ( ; ; ) {
+ ptrdiff_t refDelta = 0;
+ const BYTE* match;
+ BYTE* token;
+
+ /* Find a match */
+ { const BYTE* forwardIp = ip;
+ unsigned step = 1;
+ unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+ do {
+ U32 const h = forwardH;
+ ip = forwardIp;
+ forwardIp += step;
+ step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+ if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+ match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
+ if (dict==usingExtDict) {
+ if (match < (const BYTE*)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE*)source;
+ } }
+ forwardH = LZ4_hashPosition(forwardIp, tableType);
+ LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+ } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
+ || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+ || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
+ }
+
+ /* Catch up */
+ while (((ip>anchor) & (match+refDelta > lowLimit)) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
+
+ /* Encode Literals */
+ { unsigned const litLength = (unsigned)(ip - anchor);
+ token = op++;
+ if ((outputLimited) && /* Check output buffer overflow */
+ (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
+ return 0;
+ if (litLength >= RUN_MASK) {
+ int len = (int)litLength-RUN_MASK;
+ *token = (RUN_MASK<<ML_BITS);
+ for(; len >= 255 ; len-=255) *op++ = 255;
+ *op++ = (BYTE)len;
+ }
+ else *token = (BYTE)(litLength<<ML_BITS);
+
+ /* Copy Literals */
+ LZ4_wildCopy(op, anchor, op+litLength);
+ op+=litLength;
+ }
+
+_next_match:
+ /* Encode Offset */
+ LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+
+ /* Encode MatchLength */
+ { unsigned matchCode;
+
+ if ((dict==usingExtDict) && (lowLimit==dictionary)) {
+ const BYTE* limit;
+ match += refDelta;
+ limit = ip + (dictEnd-match);
+ if (limit > matchlimit) limit = matchlimit;
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+ ip += MINMATCH + matchCode;
+ if (ip==limit) {
+ unsigned const more = LZ4_count(ip, (const BYTE*)source, matchlimit);
+ matchCode += more;
+ ip += more;
+ }
+ } else {
+ matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+ ip += MINMATCH + matchCode;
+ }
+
+ if ( outputLimited && /* Check output buffer overflow */
+ (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
+ return 0;
+ if (matchCode >= ML_MASK) {
+ *token += ML_MASK;
+ matchCode -= ML_MASK;
+ LZ4_write32(op, 0xFFFFFFFF);
+ while (matchCode >= 4*255) op+=4, LZ4_write32(op, 0xFFFFFFFF), matchCode -= 4*255;
+ op += matchCode / 255;
+ *op++ = (BYTE)(matchCode % 255);
+ } else
+ *token += (BYTE)(matchCode);
+ }
+
+ anchor = ip;
+
+ /* Test end of chunk */
+ if (ip > mflimit) break;
+
+ /* Fill table */
+ LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
+
+ /* Test next position */
+ match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+ if (dict==usingExtDict) {
+ if (match < (const BYTE*)source) {
+ refDelta = dictDelta;
+ lowLimit = dictionary;
+ } else {
+ refDelta = 0;
+ lowLimit = (const BYTE*)source;
+ } }
+ LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+ if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
+ && (match+MAX_DISTANCE>=ip)
+ && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
+ { token=op++; *token=0; goto _next_match; }
+
+ /* Prepare next loop */
+ forwardH = LZ4_hashPosition(++ip, tableType);
+ }
+
+_last_literals:
+ /* Encode Last Literals */
+ { size_t const lastRun = (size_t)(iend - anchor);
+ if ( (outputLimited) && /* Check output buffer overflow */
+ ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
+ return 0;
+ if (lastRun >= RUN_MASK) {
+ size_t accumulator = lastRun - RUN_MASK;
+ *op++ = RUN_MASK << ML_BITS;
+ for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+ *op++ = (BYTE) accumulator;
+ } else {
+ *op++ = (BYTE)(lastRun<<ML_BITS);
+ }
+ ZT_FAST_MEMCPY(op, anchor, lastRun);
+ op += lastRun;
+ }
+
+ /* End */
+ return (int) (((char*)op)-dest);
+}
+
+static inline int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+ LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+ LZ4_resetStream((LZ4_stream_t*)state);
+ //if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+ if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source, dest, inputSize, 0, notLimited, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ } else {
+ if (inputSize < LZ4_64Klimit)
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);
+ else
+ return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+ }
+}
+
+static inline int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+#if (HEAPMODE)
+ void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */
+#else
+ LZ4_stream_t ctx;
+ void* const ctxPtr = &ctx;
+#endif
+
+ int const result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+#if (HEAPMODE)
+ FREEMEM(ctxPtr);
+#endif
+ return result;
+}
+
+static inline void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+ MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+FORCE_INLINE int LZ4_decompress_generic(
+ const char* const source,
+ char* const dest,
+ int inputSize,
+ int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
+
+ int endOnInput, /* endOnOutputSize, endOnInputSize */
+ int partialDecoding, /* full, partial */
+ int targetOutputSize, /* only used if partialDecoding==partial */
+ int dict, /* noDict, withPrefix64k, usingExtDict */
+ const BYTE* const lowPrefix, /* == dest when no prefix */
+ const BYTE* const dictStart, /* only if dict==usingExtDict */
+ const size_t dictSize /* note : = 0 if noDict */
+ )
+{
+ /* Local Variables */
+ const BYTE* ip = (const BYTE*) source;
+ const BYTE* const iend = ip + inputSize;
+
+ BYTE* op = (BYTE*) dest;
+ BYTE* const oend = op + outputSize;
+ BYTE* cpy;
+ BYTE* oexit = op + targetOutputSize;
+ const BYTE* const lowLimit = lowPrefix - dictSize;
+
+ const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+ const unsigned dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4};
+ const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+
+ const int safeDecode = (endOnInput==endOnInputSize);
+ const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+ /* Special cases */
+ if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */
+ if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */
+ if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+ /* Main Loop : decode sequences */
+ while (1) {
+ size_t length;
+ const BYTE* match;
+ size_t offset;
+
+ /* get literal length */
+ unsigned const token = *ip++;
+ if ((length=(token>>ML_BITS)) == RUN_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ length += s;
+ } while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) & (s==255) );
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) goto _output_error; /* overflow detection */
+ if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) goto _output_error; /* overflow detection */
+ }
+
+ /* copy literals */
+ cpy = op+length;
+ if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+ || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+ {
+ if (partialDecoding) {
+ if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */
+ if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */
+ } else {
+ if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */
+ if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */
+ }
+ ZT_FAST_MEMCPY(op, ip, length);
+ ip += length;
+ op += length;
+ break; /* Necessarily EOF, due to parsing restrictions */
+ }
+ LZ4_wildCopy(op, ip, cpy);
+ ip += length; op = cpy;
+
+ /* get offset */
+ offset = LZ4_readLE16(ip); ip+=2;
+ match = op - offset;
+ if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside buffers */
+ LZ4_write32(op, (U32)offset); /* costs ~1%; silence an msan warning when offset==0 */
+
+ /* get matchlength */
+ length = token & ML_MASK;
+ if (length == ML_MASK) {
+ unsigned s;
+ do {
+ s = *ip++;
+ if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+ length += s;
+ } while (s==255);
+ if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */
+ }
+ length += MINMATCH;
+
+ /* check external dictionary */
+ if ((dict==usingExtDict) && (match < lowPrefix)) {
+ if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */
+
+ if (length <= (size_t)(lowPrefix-match)) {
+ /* match can be copied as a single segment from external dictionary */
+ memmove(op, dictEnd - (lowPrefix-match), length);
+ op += length;
+ } else {
+ /* match encompass external dictionary and current block */
+ size_t const copySize = (size_t)(lowPrefix-match);
+ size_t const restSize = length - copySize;
+ ZT_FAST_MEMCPY(op, dictEnd - copySize, copySize);
+ op += copySize;
+ if (restSize > (size_t)(op-lowPrefix)) { /* overlap copy */
+ BYTE* const endOfMatch = op + restSize;
+ const BYTE* copyFrom = lowPrefix;
+ while (op < endOfMatch) *op++ = *copyFrom++;
+ } else {
+ ZT_FAST_MEMCPY(op, lowPrefix, restSize);
+ op += restSize;
+ } }
+ continue;
+ }
+
+ /* copy match within block */
+ cpy = op + length;
+ if (unlikely(offset<8)) {
+ const int dec64 = dec64table[offset];
+ op[0] = match[0];
+ op[1] = match[1];
+ op[2] = match[2];
+ op[3] = match[3];
+ match += dec32table[offset];
+ ZT_FAST_MEMCPY(op+4, match, 4);
+ match -= dec64;
+ } else { LZ4_copy8(op, match); match+=8; }
+ op += 8;
+
+ if (unlikely(cpy>oend-12)) {
+ BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+ if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+ if (op < oCopyLimit) {
+ LZ4_wildCopy(op, match, oCopyLimit);
+ match += oCopyLimit - op;
+ op = oCopyLimit;
+ }
+ while (op<cpy) *op++ = *match++;
+ } else {
+ LZ4_copy8(op, match);
+ if (length>16) LZ4_wildCopy(op+8, match+8, cpy);
+ }
+ op=cpy; /* correction */
+ }
+
+ /* end of decoding */
+ if (endOnInput)
+ return (int) (((char*)op)-dest); /* Nb of output bytes decoded */
+ else
+ return (int) (((const char*)ip)-source); /* Nb of input bytes read */
+
+ /* Overflow error detected */
+_output_error:
+ return (int) (-(((const char*)ip)-source))-1;
+}
+
+static inline int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+ return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+}
+
+} // anonymous namespace
+
+/************************************************************************** */
+/************************************************************************** */
+
+const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
void Packet::armor(const void *key,bool encryptPayload)
{
- unsigned char mangledKey[32];
- unsigned char macKey[32];
- unsigned char mac[16];
- const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
- unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen);
+ uint8_t mangledKey[32];
+ uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
// Set flag now, since it affects key mangle function
setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE);
_salsa20MangleKey((const unsigned char *)key,mangledKey);
- Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
-
- // MAC key is always the first 32 bytes of the Salsa20 key stream
- // This is the same construction DJB's NaCl library uses
- s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
- if (encryptPayload)
- s20.encrypt12(payload,payload,payloadLen);
-
- Poly1305::compute(mac,payload,payloadLen,macKey);
- memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8);
+ if (ZT_HAS_FAST_CRYPTO()) {
+ const unsigned int encryptLen = (encryptPayload) ? (size() - ZT_PACKET_IDX_VERB) : 0;
+ uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
+ ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,encryptLen + 64,(data + ZT_PACKET_IDX_IV),mangledKey);
+ Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast<const uint8_t *>(keyStream + 8),encryptLen);
+ uint64_t mac[2];
+ Poly1305::compute(mac,data + ZT_PACKET_IDX_VERB,size() - ZT_PACKET_IDX_VERB,keyStream);
+#ifdef ZT_NO_TYPE_PUNNING
+ memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
+#else
+ (*reinterpret_cast<uint64_t *>(data + ZT_PACKET_IDX_MAC)) = mac[0];
+#endif
+ } else {
+ Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV);
+ uint64_t macKey[4];
+ s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
+ uint8_t *const payload = data + ZT_PACKET_IDX_VERB;
+ const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
+ if (encryptPayload)
+ s20.crypt12(payload,payload,payloadLen);
+ uint64_t mac[2];
+ Poly1305::compute(mac,payload,payloadLen,macKey);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_MAC,mac,8);
+ }
}
bool Packet::dearmor(const void *key)
{
- unsigned char mangledKey[32];
- unsigned char macKey[32];
- unsigned char mac[16];
+ uint8_t mangledKey[32];
+ uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
- unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen);
- unsigned int cs = cipher();
+ unsigned char *const payload = data + ZT_PACKET_IDX_VERB;
+ const unsigned int cs = cipher();
if ((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)||(cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)) {
_salsa20MangleKey((const unsigned char *)key,mangledKey);
- Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
-
- s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
- Poly1305::compute(mac,payload,payloadLen,macKey);
- if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8))
- return false;
-
- if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
- s20.decrypt12(payload,payload,payloadLen);
+ if (ZT_HAS_FAST_CRYPTO()) {
+ uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
+ ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012) ? (payloadLen + 64) : 64),(data + ZT_PACKET_IDX_IV),mangledKey);
+ uint64_t mac[2];
+ Poly1305::compute(mac,payload,payloadLen,keyStream);
+#ifdef ZT_NO_TYPE_PUNNING
+ if (!Utils::secureEq(mac,data + ZT_PACKET_IDX_MAC,8))
+ return false;
+#else
+ if ((*reinterpret_cast<const uint64_t *>(data + ZT_PACKET_IDX_MAC)) != mac[0]) // also secure, constant time
+ return false;
+#endif
+ if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
+ Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast<const uint8_t *>(keyStream + 8),payloadLen);
+ } else {
+ Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV);
+ uint64_t macKey[4];
+ s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
+ uint64_t mac[2];
+ Poly1305::compute(mac,payload,payloadLen,macKey);
+#ifdef ZT_NO_TYPE_PUNNING
+ if (!Utils::secureEq(mac,data + ZT_PACKET_IDX_MAC,8))
+ return false;
+#else
+ if ((*reinterpret_cast<const uint64_t *>(data + ZT_PACKET_IDX_MAC)) != mac[0]) // also secure, constant time
+ return false;
+#endif
+ if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
+ s20.crypt12(payload,payload,payloadLen);
+ }
return true;
- } else return false; // unrecognized cipher suite
+ } else {
+ return false; // unrecognized cipher suite
+ }
+}
+
+void Packet::cryptField(const void *key,unsigned int start,unsigned int len)
+{
+ uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
+ uint8_t iv[8];
+ for(int i=0;i<8;++i) iv[i] = data[i];
+ iv[7] &= 0xf8; // mask off least significant 3 bits of packet ID / IV since this is unset when this function gets called
+ Salsa20 s20(key,iv);
+ s20.crypt12(data + start,data + start,len);
}
bool Packet::compress()
{
- unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
- if ((!compressed())&&(size() > (ZT_PACKET_IDX_PAYLOAD + 32))) {
+ char *const data = reinterpret_cast<char *>(unsafeData());
+ char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
+
+ if ((!compressed())&&(size() > (ZT_PACKET_IDX_PAYLOAD + 64))) { // don't bother compressing tiny packets
int pl = (int)(size() - ZT_PACKET_IDX_PAYLOAD);
- int cl = LZ4_compress((const char *)field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)pl),(char *)buf,pl);
+ int cl = LZ4_compress_fast(data + ZT_PACKET_IDX_PAYLOAD,buf,pl,ZT_PROTO_MAX_PACKET_LENGTH * 2,2);
if ((cl > 0)&&(cl < pl)) {
- (*this)[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
+ data[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
setSize((unsigned int)cl + ZT_PACKET_IDX_PAYLOAD);
- memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)cl),buf,cl);
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_PAYLOAD,buf,cl);
return true;
}
}
- (*this)[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+ data[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+
return false;
}
bool Packet::uncompress()
{
- unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH];
+ char *const data = reinterpret_cast<char *>(unsafeData());
+ char buf[ZT_PROTO_MAX_PACKET_LENGTH];
+
if ((compressed())&&(size() >= ZT_PROTO_MIN_PACKET_LENGTH)) {
if (size() > ZT_PACKET_IDX_PAYLOAD) {
unsigned int compLen = size() - ZT_PACKET_IDX_PAYLOAD;
- int ucl = LZ4_decompress_safe((const char *)field(ZT_PACKET_IDX_PAYLOAD,compLen),(char *)buf,compLen,sizeof(buf));
+ int ucl = LZ4_decompress_safe((const char *)data + ZT_PACKET_IDX_PAYLOAD,buf,compLen,sizeof(buf));
if ((ucl > 0)&&(ucl <= (int)(capacity() - ZT_PACKET_IDX_PAYLOAD))) {
setSize((unsigned int)ucl + ZT_PACKET_IDX_PAYLOAD);
- memcpy(field(ZT_PACKET_IDX_PAYLOAD,(unsigned int)ucl),buf,ucl);
- } else return false;
+ ZT_FAST_MEMCPY(data + ZT_PACKET_IDX_PAYLOAD,buf,ucl);
+ } else {
+ return false;
+ }
}
- (*this)[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+ data[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
}
+
return true;
}
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 3d95b0ba..27da6fb5 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_N_PACKET_HPP
@@ -34,12 +42,6 @@
#include "Utils.hpp"
#include "Buffer.hpp"
-#ifdef ZT_USE_SYSTEM_LZ4
-#include <lz4.h>
-#else
-#include "../ext/lz4/lz4.h"
-#endif
-
/**
* Protocol version -- incremented only for major changes
*
@@ -51,19 +53,23 @@
* + Yet another multicast redesign
* + New crypto completely changes key agreement cipher
* 4 - 0.6.0 ... 1.0.6
- * + New identity format based on hashcash design
+ * + BREAKING CHANGE: New identity format based on hashcash design
* 5 - 1.1.0 ... 1.1.5
- * + Supports circuit test, proof of work, and echo
+ * + Supports echo
* + Supports in-band world (root server definition) updates
* + Clustering! (Though this will work with protocol v4 clients.)
* + Otherwise backward compatible with protocol v4
* 6 - 1.1.5 ... 1.1.10
- * + Deprecate old dictionary-based network config format
- * + Introduce new binary serialized network config and meta-data
- * 7 - 1.1.10 -- CURRENT
+ * + Network configuration format revisions including binary values
+ * 7 - 1.1.10 ... 1.1.17
* + Introduce trusted paths for local SDN use
+ * 8 - 1.1.17 ... 1.2.0
+ * + Multipart network configurations for large network configs
+ * + Tags and Capabilities
+ * + Inline push of CertificateOfMembership deprecated
+ * 9 - 1.2.0 ... CURRENT
*/
-#define ZT_PROTO_VERSION 7
+#define ZT_PROTO_VERSION 9
/**
* Minimum supported protocol version
@@ -217,12 +223,8 @@
/**
* Packet buffer size (can be changed)
- *
- * The current value is big enough for ZT_MAX_PACKET_FRAGMENTS, the pragmatic
- * packet fragment limit, times the default UDP MTU. Most packets won't be
- * this big.
*/
-#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_UDP_DEFAULT_PAYLOAD_MTU)
+#define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_DEFAULT_PHYSMTU)
/**
* Minimum viable packet length (a.k.a. header length)
@@ -303,6 +305,7 @@
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS + 1)
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC + 6)
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI + 4)
+#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT + 4)
// Note: COM, GATHER_LIMIT, and SOURCE_MAC are optional, and so are specified without size
#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
@@ -346,7 +349,7 @@ namespace ZeroTier {
* ZeroTier packet
*
* Packet format:
- * <[8] 64-bit random packet ID and crypto initialization vector>
+ * <[8] 64-bit packet ID / crypto IV / packet counter>
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher/hops>
@@ -357,6 +360,14 @@ namespace ZeroTier {
*
* Packets smaller than 28 bytes are invalid and silently discarded.
*
+ * The 64-bit packet ID is a strongly random value used as a crypto IV.
+ * Its least significant 3 bits are also used as a monotonically increasing
+ * (and looping) counter for sending packets to a particular recipient. This
+ * can be used for link quality monitoring and reporting and has no crypto
+ * impact as it does not increase the likelihood of an IV collision. (The
+ * crypto we use is not sensitive to the nature of the IV, only that it does
+ * not repeat.)
+ *
* The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
* selection allowing up to 7 cipher suites, F is outside-envelope flags,
* and H is hop count.
@@ -407,8 +418,7 @@ public:
}
template<unsigned int C2>
- Fragment(const Buffer<C2> &b)
- throw(std::out_of_range) :
+ Fragment(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
{
}
@@ -426,10 +436,8 @@ public:
* @param fragLen Length of fragment in bytes
* @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
* @param fragTotal Total number of fragments (including 0)
- * @throws std::out_of_range Packet size would exceed buffer
*/
Fragment(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
- throw(std::out_of_range)
{
init(p,fragStart,fragLen,fragNo,fragTotal);
}
@@ -442,13 +450,11 @@ public:
* @param fragLen Length of fragment in bytes
* @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
* @param fragTotal Total number of fragments (including 0)
- * @throws std::out_of_range Packet size would exceed buffer
*/
inline void init(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
- throw(std::out_of_range)
{
if ((fragStart + fragLen) > p.size())
- throw std::out_of_range("Packet::Fragment: tried to construct fragment of packet past its length");
+ throw ZT_EXCEPTION_OUT_OF_BOUNDS;
setSize(fragLen + ZT_PROTO_MIN_FRAGMENT_LENGTH);
// NOTE: this copies both the IV/packet ID and the destination address.
@@ -523,50 +529,56 @@ public:
/**
* No operation (ignored, no reply)
*/
- VERB_NOP = 0,
+ VERB_NOP = 0x00,
/**
- * Announcement of a node's existence:
+ * Announcement of a node's existence and vitals:
* <[1] protocol version>
* <[1] software major version>
* <[1] software minor version>
* <[2] software revision>
- * <[8] timestamp (ms since epoch)>
+ * <[8] timestamp for determining latency>
* <[...] binary serialized identity (see Identity)>
- * <[1] destination address type>
- * [<[...] destination address>]
- * <[8] 64-bit world ID of current world>
- * <[8] 64-bit timestamp of current world>
- *
- * This is the only message that ever must be sent in the clear, since it
- * is used to push an identity to a new peer.
+ * <[...] physical destination address of packet>
+ * <[8] 64-bit world ID of current planet>
+ * <[8] 64-bit timestamp of current planet>
+ * [... remainder if packet is encrypted using cryptField() ...]
+ * <[2] 16-bit number of moons>
+ * [<[1] 8-bit type ID of moon>]
+ * [<[8] 64-bit world ID of moon>]
+ * [<[8] 64-bit timestamp of moon>]
+ * [... additional moon type/ID/timestamp tuples ...]
+ *
+ * HELLO is sent in the clear as it is how peers share their identity
+ * public keys. A few additional fields are sent in the clear too, but
+ * these are things that are public info or are easy to determine. As
+ * of 1.2.0 we have added a few more fields, but since these could have
+ * the potential to be sensitive we introduced the encryption of the
+ * remainder of the packet. See cryptField(). Packet MAC is still
+ * performed of course, so authentication occurs as normal.
+ *
+ * Destination address is the actual wire address to which the packet
+ * was sent. See InetAddress::serialize() for format.
*
- * The destination address is the wire address to which this packet is
- * being sent, and in OK is *also* the destination address of the OK
- * packet. This can be used by the receiver to detect NAT, learn its real
- * external address if behind NAT, and detect changes to its external
- * address that require re-establishing connectivity.
+ * OK payload:
+ * <[8] HELLO timestamp field echo>
+ * <[1] protocol version>
+ * <[1] software major version>
+ * <[1] software minor version>
+ * <[2] software revision>
+ * <[...] physical destination address of packet>
+ * <[2] 16-bit length of world update(s) or 0 if none>
+ * [[...] updates to planets and/or moons]
*
- * Destination address types and formats (not all of these are used now):
- * 0x00 - None -- no destination address data present
- * 0x01 - Ethernet address -- format: <[6] Ethernet MAC>
- * 0x04 - 6-byte IPv4 UDP address/port -- format: <[4] IP>, <[2] port>
- * 0x06 - 18-byte IPv6 UDP address/port -- format: <[16] IP>, <[2] port>
+ * With the exception of the timestamp, the other fields pertain to the
+ * respondent who is sending OK and are not echoes.
*
- * OK payload:
- * <[8] timestamp (echoed from original HELLO)>
- * <[1] protocol version (of responder)>
- * <[1] software major version (of responder)>
- * <[1] software minor version (of responder)>
- * <[2] software revision (of responder)>
- * <[1] destination address type (for this OK, not copied from HELLO)>
- * [<[...] destination address>]
- * <[2] 16-bit length of world update or 0 if none>
- * [[...] world update]
+ * Note that OK is fully encrypted so no selective cryptField() of
+ * potentially sensitive fields is needed.
*
* ERROR has no payload.
*/
- VERB_HELLO = 1,
+ VERB_HELLO = 0x01,
/**
* Error response:
@@ -575,7 +587,7 @@ public:
* <[1] error code>
* <[...] error-dependent payload>
*/
- VERB_ERROR = 2,
+ VERB_ERROR = 0x02,
/**
* Success response:
@@ -583,50 +595,43 @@ public:
* <[8] in-re packet ID>
* <[...] request-specific payload>
*/
- VERB_OK = 3,
+ VERB_OK = 0x03,
/**
* Query an identity by address:
* <[5] address to look up>
+ * [<[...] additional addresses to look up>
*
* OK response payload:
* <[...] binary serialized identity>
+ * [<[...] additional binary serialized identities>]
*
* If querying a cluster, duplicate OK responses may occasionally occur.
- * These should be discarded.
+ * These must be tolerated, which is easy since they'll have info you
+ * already have.
*
- * If the address is not found, no response is generated. WHOIS requests
- * will time out much like ARP requests and similar do in L2.
+ * If the address is not found, no response is generated. The semantics
+ * of WHOIS is similar to ARP and NDP in that persistent retrying can
+ * be performed.
*/
- VERB_WHOIS = 4,
+ VERB_WHOIS = 0x04,
/**
- * Meet another node at a given protocol address:
+ * Relay-mediated NAT traversal or firewall punching initiation:
* <[1] flags (unused, currently 0)>
* <[5] ZeroTier address of peer that might be found at this address>
* <[2] 16-bit protocol address port>
* <[1] protocol address length (4 for IPv4, 16 for IPv6)>
* <[...] protocol address (network byte order)>
*
- * This is sent by a relaying node to initiate NAT traversal between two
- * peers that are communicating by way of indirect relay. The relay will
- * send this to both peers at the same time on a periodic basis, telling
- * each where it might find the other on the network.
+ * An upstream node can send this to inform both sides of a relay of
+ * information they might use to establish a direct connection.
*
* Upon receipt a peer sends HELLO to establish a direct link.
*
- * Nodes should implement rate control, limiting the rate at which they
- * respond to these packets to prevent their use in DDOS attacks. Nodes
- * may also ignore these messages if a peer is not known or is not being
- * actively communicated with.
- *
- * Unfortunately the physical address format in this message pre-dates
- * InetAddress's serialization format. :( ZeroTier is four years old and
- * yes we've accumulated a tiny bit of cruft here and there.
- *
* No OK or ERROR is generated.
*/
- VERB_RENDEZVOUS = 5,
+ VERB_RENDEZVOUS = 0x05,
/**
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
@@ -642,31 +647,44 @@ public:
* ERROR may be generated if a membership certificate is needed for a
* closed network. Payload will be network ID.
*/
- VERB_FRAME = 6,
+ VERB_FRAME = 0x06,
/**
* Full Ethernet frame with MAC addressing and optional fields:
* <[8] 64-bit network ID>
* <[1] flags>
- * [<[...] certificate of network membership>]
* <[6] destination MAC or all zero for destination node>
* <[6] source MAC or all zero for node of origin>
* <[2] 16-bit ethertype>
* <[...] ethernet payload>
*
* Flags:
- * 0x01 - Certificate of network membership is attached
- *
- * An extended frame carries full MAC addressing, making them a
- * superset of VERB_FRAME. They're used for bridging or when we
- * want to attach a certificate since FRAME does not support that.
- *
- * Multicast frames may not be sent as EXT_FRAME.
- *
- * ERROR may be generated if a membership certificate is needed for a
- * closed network. Payload will be network ID.
+ * 0x01 - Certificate of network membership attached (DEPRECATED)
+ * 0x02 - Most significant bit of subtype (see below)
+ * 0x04 - Middle bit of subtype (see below)
+ * 0x08 - Least significant bit of subtype (see below)
+ * 0x10 - ACK requested in the form of OK(EXT_FRAME)
+ *
+ * Subtypes (0..7):
+ * 0x0 - Normal frame (bridging can be determined by checking MAC)
+ * 0x1 - TEEd outbound frame
+ * 0x2 - REDIRECTed outbound frame
+ * 0x3 - WATCHed outbound frame (TEE with ACK, ACK bit also set)
+ * 0x4 - TEEd inbound frame
+ * 0x5 - REDIRECTed inbound frame
+ * 0x6 - WATCHed inbound frame
+ * 0x7 - (reserved for future use)
+ *
+ * An extended frame carries full MAC addressing, making it a
+ * superset of VERB_FRAME. It is used for bridged traffic,
+ * redirected or observed traffic via rules, and can in theory
+ * be used for multicast though MULTICAST_FRAME exists for that
+ * purpose and has additional options and capabilities.
+ *
+ * OK payload (if ACK flag is set):
+ * <[8] 64-bit network ID>
*/
- VERB_EXT_FRAME = 7,
+ VERB_EXT_FRAME = 0x07,
/**
* ECHO request (a.k.a. ping):
@@ -676,7 +694,7 @@ public:
* is generated. Response to ECHO requests is optional and ECHO may be
* ignored if a node detects a possible flood.
*/
- VERB_ECHO = 8,
+ VERB_ECHO = 0x08,
/**
* Announce interest in multicast group(s):
@@ -690,77 +708,117 @@ public:
* controllers and root servers. In the current network, root servers
* will provide the service of final multicast cache.
*
- * It is recommended that NETWORK_MEMBERSHIP_CERTIFICATE pushes be sent
- * along with MULTICAST_LIKE when pushing LIKEs to peers that do not
- * share a network membership (such as root servers), since this can be
- * used to authenticate GATHER requests and limit responses to peers
- * authorized to talk on a network. (Should be an optional field here,
- * but saving one or two packets every five minutes is not worth an
- * ugly hack or protocol rev.)
+ * VERB_NETWORK_CREDENTIALS should be pushed along with this, especially
+ * if using upstream (e.g. root) nodes as multicast databases. This allows
+ * GATHERs to be authenticated.
*
* OK/ERROR are not generated.
*/
- VERB_MULTICAST_LIKE = 9,
+ VERB_MULTICAST_LIKE = 0x09,
/**
- * Network member certificate replication/push:
- * <[...] serialized certificate of membership>
- * [ ... additional certificates may follow ...]
- *
- * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
- * be pushed at any other time to keep exchanged certificates up to date.
+ * Network credentials push:
+ * [<[...] one or more certificates of membership>]
+ * <[1] 0x00, null byte marking end of COM array>
+ * <[2] 16-bit number of capabilities>
+ * <[...] one or more serialized Capability>
+ * <[2] 16-bit number of tags>
+ * <[...] one or more serialized Tags>
+ * <[2] 16-bit number of revocations>
+ * <[...] one or more serialized Revocations>
+ * <[2] 16-bit number of certificates of ownership>
+ * <[...] one or more serialized CertificateOfOwnership>
+ *
+ * This can be sent by anyone at any time to push network credentials.
+ * These will of course only be accepted if they are properly signed.
+ * Credentials can be for any number of networks.
+ *
+ * The use of a zero byte to terminate the COM section is for legacy
+ * backward compatiblity. Newer fields are prefixed with a length.
*
* OK/ERROR are not generated.
*/
- VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
+ VERB_NETWORK_CREDENTIALS = 0x0a,
/**
* Network configuration request:
* <[8] 64-bit network ID>
* <[2] 16-bit length of request meta-data dictionary>
* <[...] string-serialized request meta-data>
- * [<[8] 64-bit revision of netconf we currently have>]
+ * <[8] 64-bit revision of netconf we currently have>
+ * <[8] 64-bit timestamp of netconf we currently have>
*
* This message requests network configuration from a node capable of
- * providing it. If the optional revision is included, a response is
- * only generated if there is a newer network configuration available.
+ * providing it.
*
- * OK response payload:
- * <[8] 64-bit network ID>
- * <[2] 16-bit length of network configuration dictionary>
- * <[...] network configuration dictionary>
+ * Respones to this are always whole configs intended for the recipient.
+ * For patches and other updates a NETWORK_CONFIG is sent instead.
*
- * OK returns a Dictionary (string serialized) containing the network's
- * configuration and IP address assignment information for the querying
- * node. It also contains a membership certificate that the querying
- * node can push to other peers to demonstrate its right to speak on
- * a given network.
+ * It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always,
+ * but OK(NTEWORK_CONFIG_REQUEST) should be sent for compatibility.
*
- * When a new network configuration is received, another config request
- * should be sent with the new netconf's revision. This confirms receipt
- * and also causes any subsequent changes to rapidly propagate as this
- * cycle will repeat until there are no changes. This is optional but
- * recommended behavior.
+ * OK response payload:
+ * <[8] 64-bit network ID>
+ * <[2] 16-bit length of network configuration dictionary chunk>
+ * <[...] network configuration dictionary (may be incomplete)>
+ * [ ... end of legacy single chunk response ... ]
+ * <[1] 8-bit flags>
+ * <[8] 64-bit config update ID (should never be 0)>
+ * <[4] 32-bit total length of assembled dictionary>
+ * <[4] 32-bit index of chunk>
+ * [ ... end signed portion ... ]
+ * <[1] 8-bit chunk signature type>
+ * <[2] 16-bit length of chunk signature>
+ * <[...] chunk signature>
+ *
+ * The chunk signature signs the entire payload of the OK response.
+ * Currently only one signature type is supported: ed25519 (1).
+ *
+ * Each config chunk is signed to prevent memory exhaustion or
+ * traffic crowding DOS attacks against config fragment assembly.
+ *
+ * If the packet is from the network controller it is permitted to end
+ * before the config update ID or other chunking related or signature
+ * fields. This is to support older controllers that don't include
+ * these fields and may be removed in the future.
*
* ERROR response payload:
* <[8] 64-bit network ID>
- *
- * UNSUPPORTED_OPERATION is returned if this service is not supported,
- * and OBJ_NOT_FOUND if the queried network ID was not found.
*/
- VERB_NETWORK_CONFIG_REQUEST = 11,
+ VERB_NETWORK_CONFIG_REQUEST = 0x0b,
/**
- * Network configuration refresh request:
- * <[...] array of 64-bit network IDs>
+ * Network configuration data push:
+ * <[8] 64-bit network ID>
+ * <[2] 16-bit length of network configuration dictionary chunk>
+ * <[...] network configuration dictionary (may be incomplete)>
+ * <[1] 8-bit flags>
+ * <[8] 64-bit config update ID (should never be 0)>
+ * <[4] 32-bit total length of assembled dictionary>
+ * <[4] 32-bit index of chunk>
+ * [ ... end signed portion ... ]
+ * <[1] 8-bit chunk signature type>
+ * <[2] 16-bit length of chunk signature>
+ * <[...] chunk signature>
+ *
+ * This is a direct push variant for network config updates. It otherwise
+ * carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same
+ * semantics.
+ *
+ * The legacy mode missing the additional chunking fields is not supported
+ * here.
+ *
+ * Flags:
+ * 0x01 - Use fast propagation
*
- * This can be sent by the network controller to inform a node that it
- * should now make a NETWORK_CONFIG_REQUEST.
+ * An OK should be sent if the config is successfully received and
+ * accepted.
*
- * It does not generate an OK or ERROR message, and is treated only as
- * a hint to refresh now.
+ * OK payload:
+ * <[8] 64-bit network ID>
+ * <[8] 64-bit config update ID>
*/
- VERB_NETWORK_CONFIG_REFRESH = 12,
+ VERB_NETWORK_CONFIG = 0x0c,
/**
* Request endpoints for multicast distribution:
@@ -769,10 +827,10 @@ public:
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
* <[4] 32-bit requested max number of multicast peers>
- * [<[...] network certificate of membership>]
+ * [<[...] network certificate of membership>]
*
* Flags:
- * 0x01 - Network certificate of membership is attached
+ * 0x01 - COM is attached
*
* This message asks a peer for additional known endpoints that have
* LIKEd a given multicast group. It's sent when the sender wishes
@@ -782,6 +840,9 @@ public:
* More than one OK response can occur if the response is broken up across
* multiple packets or if querying a clustered node.
*
+ * The COM should be included so that upstream nodes that are not
+ * members of our network can validate our request.
+ *
* OK response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
@@ -793,13 +854,12 @@ public:
*
* ERROR is not generated; queries that return no response are dropped.
*/
- VERB_MULTICAST_GATHER = 13,
+ VERB_MULTICAST_GATHER = 0x0d,
/**
* Multicast frame:
* <[8] 64-bit network ID>
* <[1] flags>
- * [<[...] network certificate of membership>]
* [<[4] 32-bit implicit gather limit>]
* [<[6] source MAC>]
* <[6] destination MAC (multicast address)>
@@ -808,9 +868,10 @@ public:
* <[...] ethernet payload>
*
* Flags:
- * 0x01 - Network certificate of membership is attached
+ * 0x01 - Network certificate of membership attached (DEPRECATED)
* 0x02 - Implicit gather limit field is present
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
+ * 0x08 - Please replicate (sent to multicast replicators)
*
* OK and ERROR responses are optional. OK may be generated if there are
* implicit gather results or if the recipient wants to send its own
@@ -823,11 +884,11 @@ public:
* <[6] MAC address of multicast group>
* <[4] 32-bit ADI for multicast group>
* <[1] flags>
- * [<[...] network certficate of membership>]
+ * [<[...] network certficate of membership (DEPRECATED)>]
* [<[...] implicit gather results if flag 0x01 is set>]
*
* OK flags (same bits as request flags):
- * 0x01 - OK includes certificate of network membership
+ * 0x01 - OK includes certificate of network membership (DEPRECATED)
* 0x02 - OK includes implicit gather results
*
* ERROR response payload:
@@ -835,7 +896,7 @@ public:
* <[6] multicast group MAC>
* <[4] 32-bit multicast group ADI>
*/
- VERB_MULTICAST_FRAME = 14,
+ VERB_MULTICAST_FRAME = 0x0e,
/**
* Push of potential endpoints for direct communication:
@@ -865,172 +926,42 @@ public:
* be used unless they are blacklisted explicitly or unless flag 0x01
* is set.
*
- * Only a subset of this functionality is currently implemented: basic
- * path pushing and learning. Blacklisting and trust are not fully
- * implemented yet (encryption is still always used).
- *
* OK and ERROR are not generated.
*/
- VERB_PUSH_DIRECT_PATHS = 16,
+ VERB_PUSH_DIRECT_PATHS = 0x10,
- /**
- * Source-routed circuit test message:
- * <[5] address of originator of circuit test>
- * <[2] 16-bit flags>
- * <[8] 64-bit timestamp>
- * <[8] 64-bit test ID (arbitrary, set by tester)>
- * <[2] 16-bit originator credential length (includes type)>
- * [[1] originator credential type (for authorizing test)]
- * [[...] originator credential]
- * <[2] 16-bit length of additional fields>
- * [[...] additional fields]
- * [ ... end of signed portion of request ... ]
- * <[2] 16-bit length of signature of request>
- * <[...] signature of request by originator>
- * <[2] 16-bit previous hop credential length (including type)>
- * [[1] previous hop credential type]
- * [[...] previous hop credential]
- * <[...] next hop(s) in path>
- *
- * Flags:
- * 0x01 - Report back to originator at middle hops
- * 0x02 - Report back to originator at last hop
- *
- * Originator credential types:
- * 0x01 - 64-bit network ID for which originator is controller
- *
- * Previous hop credential types:
- * 0x01 - Certificate of network membership
- *
- * Path record format:
- * <[1] 8-bit flags (unused, must be zero)>
- * <[1] 8-bit breadth (number of next hops)>
- * <[...] one or more ZeroTier addresses of next hops>
- *
- * The circuit test allows a device to send a message that will traverse
- * the network along a specified path, with each hop optionally reporting
- * back to the tester via VERB_CIRCUIT_TEST_REPORT.
- *
- * Each circuit test packet includes a digital signature by the originator
- * of the request, as well as a credential by which that originator claims
- * authorization to perform the test. Currently this signature is ed25519,
- * but in the future flags might be used to indicate an alternative
- * algorithm. For example, the originator might be a network controller.
- * In this case the test might be authorized if the recipient is a member
- * of a network controlled by it, and if the previous hop(s) are also
- * members. Each hop may include its certificate of network membership.
- *
- * Circuit test paths consist of a series of records. When a node receives
- * an authorized circuit test, it:
- *
- * (1) Reports back to circuit tester as flags indicate
- * (2) Reads and removes the next hop from the packet's path
- * (3) Sends the packet along to next hop(s), if any.
- *
- * It is perfectly legal for a path to contain the same hop more than
- * once. In fact, this can be a very useful test to determine if a hop
- * can be reached bidirectionally and if so what that connectivity looks
- * like.
- *
- * The breadth field in source-routed path records allows a hop to forward
- * to more than one recipient, allowing the tester to specify different
- * forms of graph traversal in a test.
- *
- * There is no hard limit to the number of hops in a test, but it is
- * practically limited by the maximum size of a (possibly fragmented)
- * ZeroTier packet.
- *
- * Support for circuit tests is optional. If they are not supported, the
- * node should respond with an UNSUPPORTED_OPERATION error. If a circuit
- * test request is not authorized, it may be ignored or reported as
- * an INVALID_REQUEST. No OK messages are generated, but TEST_REPORT
- * messages may be sent (see below).
- *
- * ERROR packet format:
- * <[8] 64-bit timestamp (echoed from original>
- * <[8] 64-bit test ID (echoed from original)>
- */
- VERB_CIRCUIT_TEST = 17,
+ // 0x11, 0x12 -- deprecated
/**
- * Circuit test hop report:
- * <[8] 64-bit timestamp (from original test)>
- * <[8] 64-bit test ID (from original test)>
- * <[8] 64-bit reserved field (set to 0, currently unused)>
- * <[1] 8-bit vendor ID (set to 0, currently unused)>
- * <[1] 8-bit reporter protocol version>
- * <[1] 8-bit reporter major version>
- * <[1] 8-bit reporter minor version>
- * <[2] 16-bit reporter revision>
- * <[2] 16-bit reporter OS/platform>
- * <[2] 16-bit reporter architecture>
- * <[2] 16-bit error code (set to 0, currently unused)>
- * <[8] 64-bit report flags (set to 0, currently unused)>
- * <[8] 64-bit source packet ID>
- * <[5] upstream ZeroTier address from which test was received>
- * <[1] 8-bit source packet hop count (ZeroTier hop count)>
- * <[...] local wire address on which packet was received>
- * <[...] remote wire address from which packet was received>
- * <[2] 16-bit length of additional fields>
- * <[...] additional fields>
- * <[1] 8-bit number of next hops (breadth)>
- * <[...] next hop information>
- *
- * Next hop information record format:
- * <[5] ZeroTier address of next hop>
- * <[...] current best direct path address, if any, 0 if none>
+ * A message with arbitrary user-definable content:
+ * <[8] 64-bit arbitrary message type ID>
+ * [<[...] message payload>]
*
- * Circuit test reports can be sent by hops in a circuit test to report
- * back results. They should include information about the sender as well
- * as about the paths to which next hops are being sent.
+ * This can be used to send arbitrary messages over VL1. It generates no
+ * OK or ERROR and has no special semantics outside of whatever the user
+ * (via the ZeroTier core API) chooses to give it.
*
- * If a test report is received and no circuit test was sent, it should be
- * ignored. This message generates no OK or ERROR response.
+ * Message type IDs less than or equal to 65535 are reserved for use by
+ * ZeroTier, Inc. itself. We recommend making up random ones for your own
+ * implementations.
*/
- VERB_CIRCUIT_TEST_REPORT = 18,
+ VERB_USER_MESSAGE = 0x14,
/**
- * Request proof of work:
- * <[1] 8-bit proof of work type>
- * <[1] 8-bit proof of work difficulty>
- * <[2] 16-bit length of proof of work challenge>
- * <[...] proof of work challenge>
- *
- * This requests that a peer perform a proof of work calucation. It can be
- * sent by highly trusted peers (e.g. root servers, network controllers)
- * under suspected denial of service conditions in an attempt to filter
- * out "non-serious" peers and remain responsive to those proving their
- * intent to actually communicate.
- *
- * If the peer obliges to perform the work, it does so and responds with
- * an OK containing the result. Otherwise it may ignore the message or
- * response with an ERROR_INVALID_REQUEST or ERROR_UNSUPPORTED_OPERATION.
- *
- * Proof of work type IDs:
- * 0x01 - Salsa20/12+SHA512 hashcash function
- *
- * Salsa20/12+SHA512 is based on the following composite hash function:
- *
- * (1) Compute SHA512(candidate)
- * (2) Use the first 256 bits of the result of #1 as a key to encrypt
- * 131072 zero bytes with Salsa20/12 (with a zero IV).
- * (3) Compute SHA512(the result of step #2)
- * (4) Accept this candiate if the first [difficulty] bits of the result
- * from step #3 are zero. Otherwise generate a new candidate and try
- * again.
- *
- * This is performed repeatedly on candidates generated by appending the
- * supplied challenge to an arbitrary nonce until a valid candidate
- * is found. This chosen prepended nonce is then returned as the result
- * in OK.
- *
- * OK payload:
- * <[2] 16-bit length of result>
- * <[...] computed proof of work>
- *
- * ERROR has no payload.
+ * A trace for remote debugging or diagnostics:
+ * <[...] null-terminated dictionary containing trace information>
+ * [<[...] additional null-terminated dictionaries>]
+ *
+ * This message contains a remote trace event. Remote trace events can
+ * be sent to observers configured at the network level for those that
+ * pertain directly to actiity on a network, or to global observers if
+ * locally configured.
+ *
+ * The instance ID is a random 64-bit value generated by each ZeroTier
+ * node on startup. This is helpful in identifying traces from different
+ * members of a cluster.
*/
- VERB_REQUEST_PROOF_OF_WORK = 19
+ VERB_REMOTE_TRACE = 0x15
};
/**
@@ -1039,40 +970,33 @@ public:
enum ErrorCode
{
/* No error, not actually used in transit */
- ERROR_NONE = 0,
+ ERROR_NONE = 0x00,
/* Invalid request */
- ERROR_INVALID_REQUEST = 1,
+ ERROR_INVALID_REQUEST = 0x01,
/* Bad/unsupported protocol version */
- ERROR_BAD_PROTOCOL_VERSION = 2,
+ ERROR_BAD_PROTOCOL_VERSION = 0x02,
/* Unknown object queried */
- ERROR_OBJ_NOT_FOUND = 3,
+ ERROR_OBJ_NOT_FOUND = 0x03,
/* HELLO pushed an identity whose address is already claimed */
- ERROR_IDENTITY_COLLISION = 4,
+ ERROR_IDENTITY_COLLISION = 0x04,
/* Verb or use case not supported/enabled by this node */
- ERROR_UNSUPPORTED_OPERATION = 5,
+ ERROR_UNSUPPORTED_OPERATION = 0x05,
- /* Message to private network rejected -- no unexpired certificate on file */
- ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6,
+ /* Network membership certificate update needed */
+ ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06,
/* Tried to join network, but you're not a member */
- ERROR_NETWORK_ACCESS_DENIED_ = 7, /* extra _ to avoid Windows name conflict */
+ ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
/* Multicasts to this group are not wanted */
- ERROR_UNWANTED_MULTICAST = 8
+ ERROR_UNWANTED_MULTICAST = 0x08
};
-//#ifdef ZT_TRACE
- static const char *verbString(Verb v)
- throw();
- static const char *errorString(ErrorCode e)
- throw();
-//#endif
-
template<unsigned int C2>
Packet(const Buffer<C2> &b) :
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
@@ -1268,6 +1192,12 @@ public:
/**
* Get this packet's unique ID (the IV field interpreted as uint64_t)
*
+ * Note that the least significant 3 bits of this ID will change when armor()
+ * is called to armor the packet for transport. This is because armor() will
+ * mask the last 3 bits against the send counter for QoS monitoring use prior
+ * to actually using the IV to encrypt and MAC the packet. Be aware of this
+ * when grabbing the packetId of a new packet prior to armor/send.
+ *
* @return Packet ID
*/
inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
@@ -1318,6 +1248,21 @@ public:
bool dearmor(const void *key);
/**
+ * Encrypt/decrypt a separately armored portion of a packet
+ *
+ * This is currently only used to mask portions of HELLO as an extra
+ * security precation since most of that message is sent in the clear.
+ *
+ * This must NEVER be used more than once in the same packet, as doing
+ * so will result in re-use of the same key stream.
+ *
+ * @param key 32-byte key
+ * @param start Start of encrypted portion
+ * @param len Length of encrypted portion
+ */
+ void cryptField(const void *key,unsigned int start,unsigned int len);
+
+ /**
* Attempt to compress payload if not already (must be unencrypted)
*
* This requires that the payload at least contain the verb byte already
diff --git a/node/Path.cpp b/node/Path.cpp
index 5692af66..b1b3dd06 100644
--- a/node/Path.cpp
+++ b/node/Path.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Path.hpp"
@@ -22,10 +30,10 @@
namespace ZeroTier {
-bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now)
{
- if (RR->node->putPacket(_localAddress,address(),data,len)) {
- sent(now);
+ if (RR->node->putPacket(tPtr,_localSocket,_addr,data,len)) {
+ _lastOut = now;
return true;
}
return false;
diff --git a/node/Path.hpp b/node/Path.hpp
index ecf4be24..e12328ff 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PATH_HPP
@@ -21,33 +29,16 @@
#include <stdint.h>
#include <string.h>
+#include <stdlib.h>
#include <stdexcept>
#include <algorithm>
#include "Constants.hpp"
#include "InetAddress.hpp"
-
-// Note: if you change these flags check the logic below. Some of it depends
-// on these bits being what they are.
-
-/**
- * Flag indicating that this path is suboptimal
- *
- * Clusters set this flag on remote paths if GeoIP or other routing decisions
- * indicate that a peer should be handed off to another cluster member.
- */
-#define ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL 0x0001
-
-/**
- * Flag indicating that this path is optimal
- *
- * Peers set this flag on paths that are pushed by a cluster and indicated as
- * optimal. A second flag is needed since we want to prioritize cluster optimal
- * paths and de-prioritize sub-optimal paths and for new paths we don't know
- * which one they are. So we want a trinary state: optimal, suboptimal, unknown.
- */
-#define ZT_PATH_FLAG_CLUSTER_OPTIMAL 0x0002
+#include "SharedPtr.hpp"
+#include "AtomicCounter.hpp"
+#include "Utils.hpp"
/**
* Maximum return value of preferenceRank()
@@ -59,210 +50,142 @@ namespace ZeroTier {
class RuntimeEnvironment;
/**
- * Base class for paths
- *
- * The base Path class is an immutable value.
+ * A path across the physical network
*/
class Path
{
+ friend class SharedPtr<Path>;
+
public:
+ /**
+ * Efficient unique key for paths in a Hashtable
+ */
+ class HashKey
+ {
+ public:
+ HashKey() {}
+
+ HashKey(const int64_t l,const InetAddress &r)
+ {
+ if (r.ss_family == AF_INET) {
+ _k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
+ _k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
+ _k[2] = (uint64_t)l;
+ } else if (r.ss_family == AF_INET6) {
+ ZT_FAST_MEMCPY(_k,reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+ _k[2] = ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port << 32) ^ (uint64_t)l;
+ } else {
+ ZT_FAST_MEMCPY(_k,&r,std::min(sizeof(_k),sizeof(InetAddress)));
+ _k[2] += (uint64_t)l;
+ }
+ }
+
+ inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2]); }
+
+ inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) ); }
+ inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
+
+ private:
+ uint64_t _k[3];
+ };
+
Path() :
- _lastSend(0),
- _lastPing(0),
- _lastKeepalive(0),
- _lastReceived(0),
+ _lastOut(0),
+ _lastIn(0),
+ _lastTrustEstablishedPacketReceived(0),
+ _localSocket(-1),
+ _latency(0xffff),
_addr(),
- _localAddress(),
- _flags(0),
_ipScope(InetAddress::IP_SCOPE_NONE)
{
}
- Path(const InetAddress &localAddress,const InetAddress &addr) :
- _lastSend(0),
- _lastPing(0),
- _lastKeepalive(0),
- _lastReceived(0),
+ Path(const int64_t localSocket,const InetAddress &addr) :
+ _lastOut(0),
+ _lastIn(0),
+ _lastTrustEstablishedPacketReceived(0),
+ _localSocket(localSocket),
+ _latency(0xffff),
_addr(addr),
- _localAddress(localAddress),
- _flags(0),
_ipScope(addr.ipScope())
{
}
- inline Path &operator=(const Path &p)
- {
- if (this != &p)
- memcpy(this,&p,sizeof(Path));
- return *this;
- }
-
/**
- * Called when a packet is sent to this remote path
- *
- * This is called automatically by Path::send().
- *
- * @param t Time of send
- */
- inline void sent(uint64_t t) { _lastSend = t; }
-
- /**
- * Called when we've sent a ping or echo
- *
- * @param t Time of send
- */
- inline void pinged(uint64_t t) { _lastPing = t; }
-
- /**
- * Called when we send a NAT keepalive
- *
- * @param t Time of send
- */
- inline void sentKeepalive(uint64_t t) { _lastKeepalive = t; }
-
- /**
- * Called when a packet is received from this remote path
+ * Called when a packet is received from this remote path, regardless of content
*
* @param t Time of receive
*/
- inline void received(uint64_t t)
- {
- _lastReceived = t;
- _probation = 0;
- }
+ inline void received(const uint64_t t) { _lastIn = t; }
/**
- * @param now Current time
- * @return True if this path appears active
+ * Set time last trusted packet was received (done in Peer::received())
*/
- inline bool active(uint64_t now) const
- {
- return ( ((now - _lastReceived) < ZT_PATH_ACTIVITY_TIMEOUT) && (_probation < ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION) );
- }
+ inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
/**
- * Send a packet via this path
+ * Send a packet via this path (last out time is also updated)
*
* @param RR Runtime environment
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param data Packet data
* @param len Packet length
* @param now Current time
* @return True if transport reported success
*/
- bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
-
- /**
- * @return Address of local side of this path or NULL if unspecified
- */
- inline const InetAddress &localAddress() const throw() { return _localAddress; }
+ bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now);
/**
- * @return Time of last send to this path
- */
- inline uint64_t lastSend() const throw() { return _lastSend; }
-
- /**
- * @return Time we last pinged or dead path checked this link
+ * Manually update last sent time
+ *
+ * @param t Time of send
*/
- inline uint64_t lastPing() const throw() { return _lastPing; }
+ inline void sent(const int64_t t) { _lastOut = t; }
/**
- * @return Time of last keepalive
+ * Update path latency with a new measurement
+ *
+ * @param l Measured latency
*/
- inline uint64_t lastKeepalive() const throw() { return _lastKeepalive; }
+ inline void updateLatency(const unsigned int l)
+ {
+ unsigned int pl = _latency;
+ if (pl < 0xffff)
+ _latency = (pl + l) / 2;
+ else _latency = l;
+ }
/**
- * @return Time of last receive from this path
+ * @return Local socket as specified by external code
*/
- inline uint64_t lastReceived() const throw() { return _lastReceived; }
+ inline int64_t localSocket() const { return _localSocket; }
/**
* @return Physical address
*/
- inline const InetAddress &address() const throw() { return _addr; }
+ inline const InetAddress &address() const { return _addr; }
/**
* @return IP scope -- faster shortcut for address().ipScope()
*/
- inline InetAddress::IpScope ipScope() const throw() { return _ipScope; }
+ inline InetAddress::IpScope ipScope() const { return _ipScope; }
/**
- * @param f Valuve of ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL and inverse of ZT_PATH_FLAG_CLUSTER_OPTIMAL (both are changed)
+ * @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
*/
- inline void setClusterSuboptimal(bool f)
- {
- if (f) {
- _flags = (_flags | ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_OPTIMAL;
- } else {
- _flags = (_flags | ZT_PATH_FLAG_CLUSTER_OPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL;
- }
- }
-
- /**
- * @return True if ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL is set
- */
- inline bool isClusterSuboptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) != 0); }
+ inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
/**
- * @return True if ZT_PATH_FLAG_CLUSTER_OPTIMAL is set
+ * @return Preference rank, higher == better
*/
- inline bool isClusterOptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) != 0); }
-
- /**
- * @return Preference rank, higher == better (will be less than 255)
- */
- inline unsigned int preferenceRank() const throw()
+ inline unsigned int preferenceRank() const
{
- /* First, since the scope enum values in InetAddress.hpp are in order of
- * use preference rank, we take that. Then we multiple by two, yielding
- * a sequence like 0, 2, 4, 6, etc. Then if it's IPv6 we add one. This
- * makes IPv6 addresses of a given scope outrank IPv4 addresses of the
- * same scope -- e.g. 1 outranks 0. This makes us prefer IPv6, but not
- * if the address scope/class is of a fundamentally lower rank. */
+ // This causes us to rank paths in order of IP scope rank (see InetAdddress.hpp) but
+ // within each IP scope class to prefer IPv6 over IPv4.
return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) );
}
/**
- * @return This path's overall quality score (higher is better)
- */
- inline uint64_t score() const throw()
- {
- // This is a little bit convoluted because we try to be branch-free, using multiplication instead of branches for boolean flags
-
- // Start with the last time this path was active, and add a fudge factor to prevent integer underflow if _lastReceived is 0
- uint64_t score = _lastReceived + (ZT_PEER_DIRECT_PING_DELAY * (ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION + 1));
-
- // Increase score based on path preference rank, which is based on IP scope and address family
- score += preferenceRank() * (ZT_PEER_DIRECT_PING_DELAY / ZT_PATH_MAX_PREFERENCE_RANK);
-
- // Increase score if this is known to be an optimal path to a cluster
- score += (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) * (ZT_PEER_DIRECT_PING_DELAY / 2); // /2 because CLUSTER_OPTIMAL is flag 0x0002
-
- // Decrease score if this is known to be a sub-optimal path to a cluster
- score -= (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) * ZT_PEER_DIRECT_PING_DELAY;
-
- // Penalize for missed ECHO tests in dead path detection
- score -= (uint64_t)((ZT_PEER_DIRECT_PING_DELAY / 2) * _probation);
-
- return score;
- }
-
- /**
- * @return True if path is considered reliable (no NAT keepalives etc. are needed)
- */
- inline bool reliable() const throw()
- {
- if ((_addr.ss_family == AF_INET)||(_addr.ss_family == AF_INET6))
- return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE));
- return true;
- }
-
- /**
- * @return True if address is non-NULL
- */
- inline operator bool() const throw() { return (_addr); }
-
- /**
* Check whether this address is valid for a ZeroTier path
*
* This checks the address type and scope against address types and scopes
@@ -272,7 +195,6 @@ public:
* @return True if address is good for ZeroTier path use
*/
static inline bool isAddressValidForPath(const InetAddress &a)
- throw()
{
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
switch(a.ipScope()) {
@@ -304,60 +226,54 @@ public:
}
/**
- * @return Current path probation count (for dead path detect)
+ * @return Latency or 0xffff if unknown
*/
- inline unsigned int probation() const { return _probation; }
+ inline unsigned int latency() const { return _latency; }
/**
- * Increase this path's probation violation count (for dead path detect)
+ * @return Path quality -- lower is better
*/
- inline void increaseProbation() { ++_probation; }
-
- template<unsigned int C>
- inline void serialize(Buffer<C> &b) const
+ inline long quality(const int64_t now) const
{
- b.append((uint8_t)2); // version
- b.append((uint64_t)_lastSend);
- b.append((uint64_t)_lastPing);
- b.append((uint64_t)_lastKeepalive);
- b.append((uint64_t)_lastReceived);
- _addr.serialize(b);
- _localAddress.serialize(b);
- b.append((uint16_t)_flags);
- b.append((uint16_t)_probation);
+ const int l = (long)_latency;
+ const int age = (long)std::min((now - _lastIn),(int64_t)(ZT_PATH_HEARTBEAT_PERIOD * 10)); // set an upper sanity limit to avoid overflow
+ return (((age < (ZT_PATH_HEARTBEAT_PERIOD + 5000)) ? l : (l + 0xffff + age)) * (long)((ZT_INETADDRESS_MAX_SCOPE - _ipScope) + 1));
}
- template<unsigned int C>
- inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
- {
- unsigned int p = startAt;
- if (b[p++] != 2)
- throw std::invalid_argument("invalid serialized Path");
- _lastSend = b.template at<uint64_t>(p); p += 8;
- _lastPing = b.template at<uint64_t>(p); p += 8;
- _lastKeepalive = b.template at<uint64_t>(p); p += 8;
- _lastReceived = b.template at<uint64_t>(p); p += 8;
- p += _addr.deserialize(b,p);
- p += _localAddress.deserialize(b,p);
- _flags = b.template at<uint16_t>(p); p += 2;
- _probation = b.template at<uint16_t>(p); p += 2;
- _ipScope = _addr.ipScope();
- return (p - startAt);
- }
+ /**
+ * @return True if this path is alive (receiving heartbeats)
+ */
+ inline bool alive(const int64_t now) const { return ((now - _lastIn) < (ZT_PATH_HEARTBEAT_PERIOD + 5000)); }
+
+ /**
+ * @return True if this path needs a heartbeat
+ */
+ inline bool needsHeartbeat(const int64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
+
+ /**
+ * @return Last time we sent something
+ */
+ inline int64_t lastOut() const { return _lastOut; }
+
+ /**
+ * @return Last time we received anything
+ */
+ inline int64_t lastIn() const { return _lastIn; }
- inline bool operator==(const Path &p) const { return ((p._addr == _addr)&&(p._localAddress == _localAddress)); }
- inline bool operator!=(const Path &p) const { return ((p._addr != _addr)||(p._localAddress != _localAddress)); }
+ /**
+ * @return Time last trust-established packet was received
+ */
+ inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; }
private:
- uint64_t _lastSend;
- uint64_t _lastPing;
- uint64_t _lastKeepalive;
- uint64_t _lastReceived;
+ volatile int64_t _lastOut;
+ volatile int64_t _lastIn;
+ volatile int64_t _lastTrustEstablishedPacketReceived;
+ int64_t _localSocket;
+ volatile unsigned int _latency;
InetAddress _addr;
- InetAddress _localAddress;
- unsigned int _flags;
- unsigned int _probation;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
+ AtomicCounter __refCount;
};
} // namespace ZeroTier
diff --git a/node/Peer.cpp b/node/Peer.cpp
index cc581004..71afd852 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "../version.h"
@@ -24,535 +32,522 @@
#include "Switch.hpp"
#include "Network.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
#include "Packet.hpp"
-
-#include <algorithm>
-
-#define ZT_PEER_PATH_SORT_INTERVAL 5000
+#include "Trace.hpp"
+#include "InetAddress.hpp"
namespace ZeroTier {
-// Used to send varying values for NAT keepalive
-static uint32_t _natKeepaliveBuf = 0;
-
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
RR(renv),
- _lastUsed(0),
_lastReceive(0),
- _lastUnicastFrame(0),
- _lastMulticastFrame(0),
- _lastAnnouncedTo(0),
+ _lastNontrivialReceive(0),
+ _lastTriedMemorizedPath(0),
_lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
- _lastPathSort(0),
+ _lastCredentialRequestSent(0),
+ _lastWhoisRequestReceived(0),
+ _lastEchoRequestReceived(0),
+ _lastComRequestReceived(0),
+ _lastComRequestSent(0),
+ _lastCredentialsReceived(0),
+ _lastTrustEstablishedPacketReceived(0),
+ _lastSentFullHello(0),
_vProto(0),
_vMajor(0),
_vMinor(0),
_vRevision(0),
_id(peerIdentity),
- _numPaths(0),
- _latency(0),
_directPathPushCutoffCount(0),
- _networkComs(4),
- _lastPushedComs(4)
+ _credentialsCutoffCount(0)
{
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
- throw std::runtime_error("new peer identity key agreement failed");
+ throw ZT_EXCEPTION_INVALID_ARGUMENT;
}
void Peer::received(
- const InetAddress &localAddr,
- const InetAddress &remoteAddr,
- unsigned int hops,
- uint64_t packetId,
- Packet::Verb verb,
- uint64_t inRePacketId,
- Packet::Verb inReVerb)
+ void *tPtr,
+ const SharedPtr<Path> &path,
+ const unsigned int hops,
+ const uint64_t packetId,
+ const Packet::Verb verb,
+ const uint64_t inRePacketId,
+ const Packet::Verb inReVerb,
+ const bool trustEstablished,
+ const uint64_t networkId)
{
-#ifdef ZT_ENABLE_CLUSTER
- bool suboptimalPath = false;
- if ((RR->cluster)&&(hops == 0)) {
- // Note: findBetterEndpoint() is first since we still want to check
- // for a better endpoint even if we don't actually send a redirect.
- InetAddress redirectTo;
- if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) ) {
- if (_vProto >= 5) {
- // For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
- outp.append((uint16_t)1); // count == 1
- outp.append((uint8_t)ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT); // flags: cluster redirect
- outp.append((uint16_t)0); // no extensions
- if (redirectTo.ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append((uint8_t)6);
- outp.append(redirectTo.rawIpData(),4);
- } else {
- outp.append((uint8_t)6);
- outp.append((uint8_t)18);
- outp.append(redirectTo.rawIpData(),16);
- }
- outp.append((uint16_t)redirectTo.port());
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
- } else {
- // For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((uint8_t)0); // no flags
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)redirectTo.port());
- if (redirectTo.ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append(redirectTo.rawIpData(),4);
- } else {
- outp.append((uint8_t)16);
- outp.append(redirectTo.rawIpData(),16);
- }
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
- }
- suboptimalPath = true;
- }
- }
-#endif
+ const int64_t now = RR->node->now();
- const uint64_t now = RR->node->now();
_lastReceive = now;
- if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
- _lastUnicastFrame = now;
- else if (verb == Packet::VERB_MULTICAST_FRAME)
- _lastMulticastFrame = now;
+ switch (verb) {
+ case Packet::VERB_FRAME:
+ case Packet::VERB_EXT_FRAME:
+ case Packet::VERB_NETWORK_CONFIG_REQUEST:
+ case Packet::VERB_NETWORK_CONFIG:
+ case Packet::VERB_MULTICAST_FRAME:
+ _lastNontrivialReceive = now;
+ break;
+ default: break;
+ }
+
+ if (trustEstablished) {
+ _lastTrustEstablishedPacketReceived = now;
+ path->trustedPacketReceived(now);
+ }
if (hops == 0) {
- bool pathIsConfirmed = false;
- unsigned int np = _numPaths;
- for(unsigned int p=0;p<np;++p) {
- if ((_paths[p].address() == remoteAddr)&&(_paths[p].localAddress() == localAddr)) {
- _paths[p].received(now);
-#ifdef ZT_ENABLE_CLUSTER
- _paths[p].setClusterSuboptimal(suboptimalPath);
-#endif
- pathIsConfirmed = true;
- break;
+ // If this is a direct packet (no hops), update existing paths or learn new ones
+
+ bool havePath = false;
+ {
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (_paths[i].p == path) {
+ _paths[i].lr = now;
+ havePath = true;
+ break;
+ }
+ } else break;
}
}
- if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
- if (verb == Packet::VERB_OK) {
+ bool attemptToContact = false;
+ if ((!havePath)&&(RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) {
+ Mutex::Lock _l(_paths_m);
+
+ // Paths are redunant if they duplicate an alive path to the same IP or
+ // with the same local socket and address family.
+ bool redundant = false;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ( (_paths[i].p->alive(now)) && ( ((_paths[i].p->localSocket() == path->localSocket())&&(_paths[i].p->address().ss_family == path->address().ss_family)) || (_paths[i].p->address().ipsEqual2(path->address())) ) ) {
+ redundant = true;
+ break;
+ }
+ } else break;
+ }
- Path *slot = (Path *)0;
- if (np < ZT_MAX_PEER_NETWORK_PATHS) {
- slot = &(_paths[np++]);
- } else {
- uint64_t slotWorstScore = 0xffffffffffffffffULL;
- for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
- if (!_paths[p].active(now)) {
- slot = &(_paths[p]);
- break;
- } else {
- const uint64_t score = _paths[p].score();
- if (score <= slotWorstScore) {
- slotWorstScore = score;
- slot = &(_paths[p]);
- }
+ if (!redundant) {
+ unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
+ int replacePathQuality = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ const int q = _paths[i].p->quality(now);
+ if (q > replacePathQuality) {
+ replacePathQuality = q;
+ replacePath = i;
}
+ } else {
+ replacePath = i;
+ break;
}
}
- if (slot) {
- *slot = Path(localAddr,remoteAddr);
- slot->received(now);
-#ifdef ZT_ENABLE_CLUSTER
- slot->setClusterSuboptimal(suboptimalPath);
-#endif
- _numPaths = np;
- }
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster)
- RR->cluster->broadcastHavePeer(_id);
-#endif
-
- } else {
-
- TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
-
- if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
- } else {
- sendHELLO(localAddr,remoteAddr,now);
+ if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
+ if (verb == Packet::VERB_OK) {
+ RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId);
+ _paths[replacePath].lr = now;
+ _paths[replacePath].p = path;
+ _paths[replacePath].priority = 1;
+ } else {
+ attemptToContact = true;
+ }
}
-
}
}
- }
- if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
- _lastAnnouncedTo = now;
- const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n)
- (*n)->tryAnnounceMulticastGroupsTo(SharedPtr<Peer>(this));
+ if (attemptToContact) {
+ attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true);
+ path->sent(now);
+ RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb);
+ }
}
-}
-void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
-{
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.append(now);
- RR->identity.serialize(outp,false);
- atAddress.serialize(outp);
- outp.append((uint64_t)RR->topology->worldId());
- outp.append((uint64_t)RR->topology->worldTimestamp());
+ // If we have a trust relationship periodically push a message enumerating
+ // all known external addresses for ourselves. We now do this even if we
+ // have a current path since we'll want to use new ones too.
+ if (this->trustEstablished(now)) {
+ if ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) {
+ _lastDirectPathPushSent = now;
+
+ std::vector<InetAddress> pathsToPush;
+
+ std::vector<InetAddress> dps(RR->node->directPaths());
+ for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
+ pathsToPush.push_back(*i);
+
+ // Do symmetric NAT prediction if we are communicating indirectly.
+ if (hops > 0) {
+ std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
+ for(unsigned long i=0,added=0;i<sym.size();++i) {
+ InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
+ if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
+ pathsToPush.push_back(tmp);
+ if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
+ break;
+ }
+ }
+ }
- outp.armor(_key,false); // HELLO is sent in the clear
- RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
-}
+ if (pathsToPush.size() > 0) {
+ std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
+ while (p != pathsToPush.end()) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
+ outp.addSize(2); // leave room for count
+
+ unsigned int count = 0;
+ while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
+ uint8_t addressType = 4;
+ switch(p->ss_family) {
+ case AF_INET:
+ break;
+ case AF_INET6:
+ addressType = 6;
+ break;
+ default: // we currently only push IP addresses
+ ++p;
+ continue;
+ }
-bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
-{
- Path *p = (Path *)0;
+ outp.append((uint8_t)0); // no flags
+ outp.append((uint16_t)0); // no extensions
+ outp.append(addressType);
+ outp.append((uint8_t)((addressType == 4) ? 6 : 18));
+ outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
+ outp.append((uint16_t)p->port());
- if (inetAddressFamily != 0) {
- p = _getBestPath(now,inetAddressFamily);
- } else {
- p = _getBestPath(now);
- }
+ ++count;
+ ++p;
+ }
- if (p) {
- if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
- //TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
- sendHELLO(p->localAddress(),p->address(),now);
- p->sent(now);
- p->pinged(now);
- } else if ( ((now - std::max(p->lastSend(),p->lastKeepalive())) >= ZT_NAT_KEEPALIVE_DELAY) && (!p->reliable()) ) {
- //TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
- _natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
- RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
- p->sentKeepalive(now);
- } else {
- //TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived());
+ if (count) {
+ outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
+ outp.armor(_key,true);
+ path->send(RR,tPtr,outp.data(),outp.size(),now);
+ }
+ }
+ }
}
- return true;
}
-
- return false;
}
-bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths)
+SharedPtr<Path> Peer::getBestPath(int64_t now,bool includeExpired) const
{
-#ifdef ZT_ENABLE_CLUSTER
- // Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
- if (RR->cluster)
- return false;
-#endif
-
- if (!force) {
- if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
- return false;
- else _lastDirectPathPushSent = now;
+ Mutex::Lock _l(_paths_m);
+
+ unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS;
+ long bestPathQuality = 2147483647;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((includeExpired)||((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)) {
+ const long q = _paths[i].p->quality(now) / _paths[i].priority;
+ if (q <= bestPathQuality) {
+ bestPathQuality = q;
+ bestPath = i;
+ }
+ }
+ } else break;
}
- std::vector<InetAddress> pathsToPush;
+ if (bestPath != ZT_MAX_PEER_NETWORK_PATHS)
+ return _paths[bestPath].p;
+ return SharedPtr<Path>();
+}
- std::vector<InetAddress> dps(RR->node->directPaths());
- for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i) {
- if ((includePrivatePaths)||(i->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
- pathsToPush.push_back(*i);
+void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const
+{
+ unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1];
+ for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) {
+ myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ myBestV4QualityByScope[i] = 2147483647;
+ myBestV6QualityByScope[i] = 2147483647;
+ theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS;
+ theirBestV4QualityByScope[i] = 2147483647;
+ theirBestV6QualityByScope[i] = 2147483647;
}
- std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
- for(unsigned long i=0,added=0;i<sym.size();++i) {
- InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
- if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
- pathsToPush.push_back(tmp);
- if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
- break;
- }
- }
- if (pathsToPush.empty())
- return false;
+ Mutex::Lock _l1(_paths_m);
-#ifdef ZT_TRACE
- {
- std::string ps;
- for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
- if (ps.length() > 0)
- ps.push_back(',');
- ps.append(p->toString());
- }
- TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ const long q = _paths[i].p->quality(now) / _paths[i].priority;
+ const unsigned int s = (unsigned int)_paths[i].p->ipScope();
+ switch(_paths[i].p->address().ss_family) {
+ case AF_INET:
+ if (q <= myBestV4QualityByScope[s]) {
+ myBestV4QualityByScope[s] = q;
+ myBestV4ByScope[s] = i;
+ }
+ break;
+ case AF_INET6:
+ if (q <= myBestV6QualityByScope[s]) {
+ myBestV6QualityByScope[s] = q;
+ myBestV6ByScope[s] = i;
+ }
+ break;
+ }
+ } else break;
}
-#endif
- std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
- while (p != pathsToPush.end()) {
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
- outp.addSize(2); // leave room for count
+ Mutex::Lock _l2(other->_paths_m);
- unsigned int count = 0;
- while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
- uint8_t addressType = 4;
- switch(p->ss_family) {
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (other->_paths[i].p) {
+ const long q = other->_paths[i].p->quality(now) / other->_paths[i].priority;
+ const unsigned int s = (unsigned int)other->_paths[i].p->ipScope();
+ switch(other->_paths[i].p->address().ss_family) {
case AF_INET:
+ if (q <= theirBestV4QualityByScope[s]) {
+ theirBestV4QualityByScope[s] = q;
+ theirBestV4ByScope[s] = i;
+ }
break;
case AF_INET6:
- addressType = 6;
+ if (q <= theirBestV6QualityByScope[s]) {
+ theirBestV6QualityByScope[s] = q;
+ theirBestV6ByScope[s] = i;
+ }
break;
- default: // we currently only push IP addresses
- ++p;
- continue;
}
-
- outp.append((uint8_t)0); // no flags
- outp.append((uint16_t)0); // no extensions
- outp.append(addressType);
- outp.append((uint8_t)((addressType == 4) ? 6 : 18));
- outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
- outp.append((uint16_t)p->port());
-
- ++count;
- ++p;
- }
-
- if (count) {
- outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
- outp.armor(_key,true);
- RR->node->putPacket(localAddr,toAddress,outp.data(),outp.size(),0);
- }
+ } else break;
}
- return true;
-}
+ unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS;
+ unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS;
-bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
-{
- unsigned int np = _numPaths;
- unsigned int x = 0;
- unsigned int y = 0;
- while (x < np) {
- if (_paths[x].address().ipScope() == scope) {
- // Resetting a path means sending a HELLO and then forgetting it. If we
- // get OK(HELLO) then it will be re-learned.
- sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
- } else {
- _paths[y++] = _paths[x];
+ for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) {
+ if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
+ mine = myBestV6ByScope[s];
+ theirs = theirBestV6ByScope[s];
+ break;
+ }
+ if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) {
+ mine = myBestV4ByScope[s];
+ theirs = theirBestV4ByScope[s];
+ break;
}
- ++x;
}
- _numPaths = y;
- return (y < np);
-}
-void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
-{
- uint64_t bestV4 = 0,bestV6 = 0;
- for(unsigned int p=0,np=_numPaths;p<np;++p) {
- if (_paths[p].active(now)) {
- uint64_t lr = _paths[p].lastReceived();
- if (lr) {
- if (_paths[p].address().isV4()) {
- if (lr >= bestV4) {
- bestV4 = lr;
- v4 = _paths[p].address();
- }
- } else if (_paths[p].address().isV6()) {
- if (lr >= bestV6) {
- bestV6 = lr;
- v6 = _paths[p].address();
- }
+ if (mine != ZT_MAX_PEER_NETWORK_PATHS) {
+ unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for black magickal NAT-t reasons
+ const unsigned int completed = alt + 2;
+ while (alt != completed) {
+ if ((alt & 1) == 0) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append((uint8_t)0);
+ other->_id.address().appendTo(outp);
+ outp.append((uint16_t)other->_paths[theirs].p->address().port());
+ if (other->_paths[theirs].p->address().ss_family == AF_INET6) {
+ outp.append((uint8_t)16);
+ outp.append(other->_paths[theirs].p->address().rawIpData(),16);
+ } else {
+ outp.append((uint8_t)4);
+ outp.append(other->_paths[theirs].p->address().rawIpData(),4);
}
+ outp.armor(_key,true);
+ _paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
+ } else {
+ Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append((uint8_t)0);
+ _id.address().appendTo(outp);
+ outp.append((uint16_t)_paths[mine].p->address().port());
+ if (_paths[mine].p->address().ss_family == AF_INET6) {
+ outp.append((uint8_t)16);
+ outp.append(_paths[mine].p->address().rawIpData(),16);
+ } else {
+ outp.append((uint8_t)4);
+ outp.append(_paths[mine].p->address().rawIpData(),4);
+ }
+ outp.armor(other->_key,true);
+ other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
}
+ ++alt;
}
}
}
-bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const
+void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
{
- Mutex::Lock _l(_networkComs_m);
- const _NetworkCom *ourCom = _networkComs.get(nwid);
- if (ourCom)
- return ourCom->com.agreesWith(com);
- return false;
-}
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
-bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
-{
- // Sanity checks
- if ((!com)||(com.issuedTo() != _id.address()))
- return false;
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+ outp.append(now);
+ RR->identity.serialize(outp,false);
+ atAddress.serialize(outp);
- // Return true if we already have this *exact* COM
- {
- Mutex::Lock _l(_networkComs_m);
- _NetworkCom *ourCom = _networkComs.get(nwid);
- if ((ourCom)&&(ourCom->com == com))
- return true;
- }
+ outp.append((uint64_t)RR->topology->planetWorldId());
+ outp.append((uint64_t)RR->topology->planetWorldTimestamp());
+
+ const unsigned int startCryptedPortionAt = outp.size();
- // Check signature, log and return if cert is invalid
- if (com.signedBy() != Network::controllerFor(nwid)) {
- TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)nwid,com.signedBy().toString().c_str());
- return false; // invalid signer
+ std::vector<World> moons(RR->topology->moons());
+ std::vector<uint64_t> moonsWanted(RR->topology->moonsWanted());
+ outp.append((uint16_t)(moons.size() + moonsWanted.size()));
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ outp.append((uint8_t)m->type());
+ outp.append((uint64_t)m->id());
+ outp.append((uint64_t)m->timestamp());
+ }
+ for(std::vector<uint64_t>::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) {
+ outp.append((uint8_t)World::TYPE_MOON);
+ outp.append(*m);
+ outp.append((uint64_t)0);
}
- if (com.signedBy() == RR->identity.address()) {
+ outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
- // We are the controller: RR->identity.address() == controller() == cert.signedBy()
- // So, verify that we signed th cert ourself
- if (!com.verify(RR->identity)) {
- TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
- return false; // invalid signature
- }
+ RR->node->expectReplyTo(outp.packetId());
+ if (atAddress) {
+ outp.armor(_key,false); // false == don't encrypt full payload, but add MAC
+ RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
-
- SharedPtr<Peer> signer(RR->topology->getPeer(com.signedBy()));
-
- if (!signer) {
- // This would be rather odd, since this is our controller... could happen
- // if we get packets before we've gotten config.
- RR->sw->requestWhois(com.signedBy());
- return false; // signer unknown
- }
-
- if (!com.verify(signer->identity())) {
- TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
- return false; // invalid signature
- }
+ RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
}
+}
- // If we made it past all those checks, add or update cert in our cert info store
- {
- Mutex::Lock _l(_networkComs_m);
- _networkComs.set(nwid,_NetworkCom(RR->node->now(),com));
+void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello)
+{
+ if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
+ RR->node->expectReplyTo(outp.packetId());
+ outp.armor(_key,true);
+ RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
+ } else {
+ sendHELLO(tPtr,localSocket,atAddress,now);
}
-
- return true;
}
-bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime)
+void Peer::tryMemorizedPath(void *tPtr,int64_t now)
{
- Mutex::Lock _l(_networkComs_m);
- uint64_t &lastPushed = _lastPushedComs[nwid];
- const uint64_t tmp = lastPushed;
- if (updateLastPushedTime)
- lastPushed = now;
- return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 3));
+ if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
+ _lastTriedMemorizedPath = now;
+ InetAddress mp;
+ if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp))
+ attemptToContactAt(tPtr,-1,mp,now,true);
+ }
}
-void Peer::clean(uint64_t now)
+unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
{
- {
- unsigned int np = _numPaths;
- unsigned int x = 0;
- unsigned int y = 0;
- while (x < np) {
- if (_paths[x].active(now))
- _paths[y++] = _paths[x];
- ++x;
- }
- _numPaths = y;
+ unsigned int sent = 0;
+
+ Mutex::Lock _l(_paths_m);
+
+ const bool sendFullHello = ((now - _lastSentFullHello) >= ZT_PEER_PING_PERIOD);
+ _lastSentFullHello = now;
+
+ // Right now we only keep pinging links that have the maximum priority. The
+ // priority is used to track cluster redirections, meaning that when a cluster
+ // redirects us its redirect target links override all other links and we
+ // let those old links expire.
+ long maxPriority = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p)
+ maxPriority = std::max(_paths[i].priority,maxPriority);
+ else break;
}
- {
- Mutex::Lock _l(_networkComs_m);
- {
- uint64_t *k = (uint64_t *)0;
- _NetworkCom *v = (_NetworkCom *)0;
- Hashtable< uint64_t,_NetworkCom >::Iterator i(_networkComs);
- while (i.next(k,v)) {
- if ( (!RR->node->belongsToNetwork(*k)) && ((now - v->ts) >= ZT_PEER_NETWORK_COM_EXPIRATION) )
- _networkComs.erase(*k);
- }
- }
- {
- uint64_t *k = (uint64_t *)0;
- uint64_t *v = (uint64_t *)0;
- Hashtable< uint64_t,uint64_t >::Iterator i(_lastPushedComs);
- while (i.next(k,v)) {
- if ((now - *v) > (ZT_NETWORK_AUTOCONF_DELAY * 2))
- _lastPushedComs.erase(*k);
+ unsigned int j = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ // Clean expired and reduced priority paths
+ if ( ((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION) && (_paths[i].priority == maxPriority) ) {
+ if ((sendFullHello)||(_paths[i].p->needsHeartbeat(now))) {
+ attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,sendFullHello);
+ _paths[i].p->sent(now);
+ sent |= (_paths[i].p->address().ss_family == AF_INET) ? 0x1 : 0x2;
+ }
+ if (i != j)
+ _paths[j] = _paths[i];
+ ++j;
}
- }
+ } else break;
}
+ while(j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = 0;
+ _paths[j].p.zero();
+ _paths[j].priority = 1;
+ ++j;
+ }
+
+ return sent;
}
-void Peer::_doDeadPathDetection(Path &p,const uint64_t now)
+void Peer::clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now)
{
- /* Dead path detection: if we have sent something to this peer and have not
- * yet received a reply, double check this path. The majority of outbound
- * packets including Ethernet frames do generate some kind of reply either
- * immediately or at some point in the near future. This will occasionally
- * (every NO_ANSWER_TIMEOUT ms) check paths unnecessarily if traffic that
- * does not generate a response is being sent such as multicast announcements
- * or frames belonging to unidirectional UDP protocols, but the cost is very
- * tiny and the benefit in reliability is very large. This takes care of many
- * failure modes including crap NATs that forget links and spurious changes
- * to physical network topology that cannot be otherwise detected.
- *
- * Each time we do this we increment a probation counter in the path. This
- * counter is reset on any packet receive over this path. If it reaches the
- * MAX_PROBATION threshold the path is considred dead. */
-
- if (
- (p.lastSend() > p.lastReceived()) &&
- ((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
- ((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
- (!p.isClusterSuboptimal()) &&
- (!RR->topology->amRoot())
- ) {
- TRACE("%s(%s) does not seem to be answering in a timely manner, checking if dead (probation == %u)",_id.address().toString().c_str(),p.address().toString().c_str(),p.probation());
-
- if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
- Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
- outp.armor(_key,true);
- p.send(RR,outp.data(),outp.size(),now);
- p.pinged(now);
- } else {
- sendHELLO(p.localAddress(),p.address(),now);
- p.sent(now);
- p.pinged(now);
- }
+ SharedPtr<Path> np(RR->topology->getPath(originatingPath->localSocket(),remoteAddress));
+ RR->t->peerRedirected(tPtr,0,*this,np);
- p.increaseProbation();
- }
-}
+ attemptToContactAt(tPtr,originatingPath->localSocket(),remoteAddress,now,true);
-Path *Peer::_getBestPath(const uint64_t now)
-{
- Path *bestPath = (Path *)0;
- uint64_t bestPathScore = 0;
- for(unsigned int i=0;i<_numPaths;++i) {
- const uint64_t score = _paths[i].score();
- if ((score >= bestPathScore)&&(_paths[i].active(now))) {
- bestPathScore = score;
- bestPath = &(_paths[i]);
+ {
+ Mutex::Lock _l(_paths_m);
+
+ // New priority is higher than the priority of the originating path (if known)
+ long newPriority = 1;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (_paths[i].p == originatingPath) {
+ newPriority = _paths[i].priority;
+ break;
+ }
+ } else break;
+ }
+ newPriority += 2;
+
+ // Erase any paths with lower priority than this one or that are duplicate
+ // IPs and add this path.
+ unsigned int j = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((_paths[i].priority >= newPriority)&&(!_paths[i].p->address().ipsEqual2(remoteAddress))) {
+ if (i != j)
+ _paths[j] = _paths[i];
+ ++j;
+ }
+ }
+ }
+ if (j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = now;
+ _paths[j].p = np;
+ _paths[j].priority = newPriority;
+ ++j;
+ while (j < ZT_MAX_PEER_NETWORK_PATHS) {
+ _paths[j].lr = 0;
+ _paths[j].p.zero();
+ _paths[j].priority = 1;
+ ++j;
+ }
}
}
- if (bestPath)
- _doDeadPathDetection(*bestPath,now);
- return bestPath;
}
-Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
+void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now)
{
- Path *bestPath = (Path *)0;
- uint64_t bestPathScore = 0;
- for(unsigned int i=0;i<_numPaths;++i) {
- const uint64_t score = _paths[i].score();
- if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
- bestPathScore = score;
- bestPath = &(_paths[i]);
- }
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if ((_paths[i].p->address().ss_family == inetAddressFamily)&&(_paths[i].p->ipScope() == scope)) {
+ attemptToContactAt(tPtr,_paths[i].p->localSocket(),_paths[i].p->address(),now,false);
+ _paths[i].p->sent(now);
+ _paths[i].lr = 0; // path will not be used unless it speaks again
+ }
+ } else break;
}
- if (bestPath)
- _doDeadPathDetection(*bestPath,now);
- return bestPath;
}
} // namespace ZeroTier
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 445535c8..b6f3c695 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PEER_HPP
@@ -31,7 +39,6 @@
#include "../include/ZeroTierOne.h"
#include "RuntimeEnvironment.hpp"
-#include "CertificateOfMembership.hpp"
#include "Path.hpp"
#include "Address.hpp"
#include "Utils.hpp"
@@ -42,18 +49,15 @@
#include "AtomicCounter.hpp"
#include "Hashtable.hpp"
#include "Mutex.hpp"
-#include "NonCopyable.hpp"
-// Very rough computed estimate: (8 + 256 + 80 + (16 * 64) + (128 * 256) + (128 * 16))
-// 1048576 provides tons of headroom -- overflow would just cause peer not to be persisted
-#define ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE 1048576
+#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
namespace ZeroTier {
/**
* Peer on P2P Network (virtual layer 1)
*/
-class Peer : NonCopyable
+class Peer
{
friend class SharedPtr<Peer>;
@@ -74,26 +78,14 @@ public:
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
/**
- * @return Time peer record was last used in any way
- */
- inline uint64_t lastUsed() const throw() { return _lastUsed; }
-
- /**
- * Log a use of this peer record (done by Topology when peers are looked up)
- *
- * @param now New time of last use
- */
- inline void use(uint64_t now) throw() { _lastUsed = now; }
-
- /**
* @return This peer's ZT address (short for identity().address())
*/
- inline const Address &address() const throw() { return _id.address(); }
+ inline const Address &address() const { return _id.address(); }
/**
* @return This peer's identity
*/
- inline const Identity &identity() const throw() { return _id; }
+ inline const Identity &identity() const { return _id; }
/**
* Log receipt of an authenticated packet
@@ -101,154 +93,190 @@ public:
* This is called by the decode pipe when a packet is proven to be authentic
* and appears to be valid.
*
- * @param RR Runtime environment
- * @param localAddr Local address
- * @param remoteAddr Internet address of sender
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param path Path over which packet was received
* @param hops ZeroTier (not IP) hops
* @param packetId Packet ID
* @param verb Packet verb
* @param inRePacketId Packet ID in reply to (default: none)
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
+ * @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
+ * @param networkId Network ID if this pertains to a network, or 0 otherwise
*/
void received(
- const InetAddress &localAddr,
- const InetAddress &remoteAddr,
- unsigned int hops,
- uint64_t packetId,
- Packet::Verb verb,
- uint64_t inRePacketId = 0,
- Packet::Verb inReVerb = Packet::VERB_NOP);
-
- /**
- * Get the current best direct path to this peer
+ void *tPtr,
+ const SharedPtr<Path> &path,
+ const unsigned int hops,
+ const uint64_t packetId,
+ const Packet::Verb verb,
+ const uint64_t inRePacketId,
+ const Packet::Verb inReVerb,
+ const bool trustEstablished,
+ const uint64_t networkId);
+
+ /**
+ * Check whether we have an active path to this peer via the given address
*
* @param now Current time
- * @return Best path or NULL if there are no active direct paths
- */
- inline Path *getBestPath(uint64_t now) { return _getBestPath(now); }
-
- /**
- * @param now Current time
* @param addr Remote address
* @return True if we have an active path to this destination
*/
- inline bool hasActivePathTo(uint64_t now,const InetAddress &addr) const
+ inline bool hasActivePathTo(int64_t now,const InetAddress &addr) const
{
- for(unsigned int p=0;p<_numPaths;++p) {
- if ((_paths[p].active(now))&&(_paths[p].address() == addr))
- return true;
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p) {
+ if (((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)&&(_paths[i].p->address() == addr))
+ return true;
+ } else break;
}
return false;
}
/**
- * Set all paths in the same ss_family that are not this one to cluster suboptimal
- *
- * Addresses in other families are not affected.
+ * Send via best direct path
*
- * @param addr Address to make exclusive
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param data Packet data
+ * @param len Packet length
+ * @param now Current time
+ * @param force If true, send even if path is not alive
+ * @return True if we actually sent something
*/
- inline void setClusterOptimalPathForAddressFamily(const InetAddress &addr)
+ inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
{
- for(unsigned int p=0;p<_numPaths;++p) {
- if (_paths[p].address().ss_family == addr.ss_family) {
- _paths[p].setClusterSuboptimal(_paths[p].address() != addr);
- }
- }
+ SharedPtr<Path> bp(getBestPath(now,force));
+ if (bp)
+ return bp->send(RR,tPtr,data,len,now);
+ return false;
}
/**
- * Send via best path
+ * Get the best current direct path
*
- * @param data Packet data
- * @param len Packet length
* @param now Current time
- * @return Path used on success or NULL on failure
+ * @param includeExpired If true, include even expired paths
+ * @return Best current path or NULL if none
*/
- inline Path *send(const void *data,unsigned int len,uint64_t now)
- {
- Path *const bestPath = getBestPath(now);
- if (bestPath) {
- if (bestPath->send(RR,data,len,now))
- return bestPath;
- }
- return (Path *)0;
- }
+ SharedPtr<Path> getBestPath(int64_t now,bool includeExpired) const;
+
+ /**
+ * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path
+ */
+ void introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const;
/**
* Send a HELLO to this peer at a specified physical address
*
- * This does not update any statistics. It's used to send initial HELLOs
- * for NAT traversal and path verification.
+ * No statistics or sent times are updated here.
*
- * @param localAddr Local address
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param localSocket Local source socket
* @param atAddress Destination address
* @param now Current time
- * @param ttl Desired IP TTL (default: 0 to leave alone)
*/
- void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
+ void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now);
/**
- * Send pings or keepalives depending on configured timeouts
+ * Send ECHO (or HELLO for older peers) to this peer at the given address
*
+ * No statistics or sent times are updated here.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param localSocket Local source socket
+ * @param atAddress Destination address
* @param now Current time
- * @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family
- * @return True if at least one direct path seems alive
+ * @param sendFullHello If true, always send a full HELLO instead of just an ECHO
*/
- bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
+ void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now,bool sendFullHello);
/**
- * Push direct paths back to self if we haven't done so in the configured timeout
+ * Try a memorized or statically defined path if any are known
+ *
+ * Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL.
*
- * @param localAddr Local address
- * @param toAddress Remote address to send push to (usually from path)
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
- * @param force If true, push regardless of rate limit
- * @param includePrivatePaths If true, include local interface address paths (should only be done to peers with a trust relationship)
- * @return True if something was actually sent
*/
- bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths);
+ void tryMemorizedPath(void *tPtr,int64_t now);
/**
- * @return All known direct paths to this peer (active or inactive)
+ * Send pings or keepalives depending on configured timeouts
+ *
+ * This also cleans up some internal data structures. It's called periodically from Node.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
+ * @param inetAddressFamily Keep this address family alive, or -1 for any
+ * @return 0 if nothing sent or bit mask: bit 0x1 if IPv4 sent, bit 0x2 if IPv6 sent (0x3 means both sent)
*/
- inline std::vector<Path> paths() const
- {
- std::vector<Path> pp;
- for(unsigned int p=0,np=_numPaths;p<np;++p)
- pp.push_back(_paths[p]);
- return pp;
- }
+ unsigned int doPingAndKeepalive(void *tPtr,int64_t now);
/**
- * @return Time of last receive of anything, whether direct or relayed
+ * Process a cluster redirect sent by this peer
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param originatingPath Path from which redirect originated
+ * @param remoteAddress Remote address
+ * @param now Current time
*/
- inline uint64_t lastReceive() const throw() { return _lastReceive; }
+ void clusterRedirect(void *tPtr,const SharedPtr<Path> &originatingPath,const InetAddress &remoteAddress,const int64_t now);
/**
- * @return Time of most recent unicast frame received
+ * Reset paths within a given IP scope and address family
+ *
+ * Resetting a path involves sending an ECHO to it and then deactivating
+ * it until or unless it responds. This is done when we detect a change
+ * to our external IP or another system change that might invalidate
+ * many or all current paths.
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param scope IP scope
+ * @param inetAddressFamily Family e.g. AF_INET
+ * @param now Current time
*/
- inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; }
+ void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now);
/**
- * @return Time of most recent multicast frame received
+ * @param now Current time
+ * @return All known paths to this peer
*/
- inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; }
+ inline std::vector< SharedPtr<Path> > paths(const int64_t now) const
+ {
+ std::vector< SharedPtr<Path> > pp;
+ Mutex::Lock _l(_paths_m);
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (!_paths[i].p) break;
+ pp.push_back(_paths[i].p);
+ }
+ return pp;
+ }
/**
- * @return Time of most recent frame of any kind (unicast or multicast)
+ * @return Time of last receive of anything, whether direct or relayed
*/
- inline uint64_t lastFrame() const throw() { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
+ inline int64_t lastReceive() const { return _lastReceive; }
+
+ /**
+ * @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
+ */
+ inline bool isAlive(const int64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
* @return True if this peer has sent us real network traffic recently
*/
- inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
+ inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
- * @return Latency in milliseconds or 0 if unknown
+ * @return Latency in milliseconds of best path or 0xffff if unknown / no paths
*/
- inline unsigned int latency() const { return _latency; }
+ inline unsigned int latency(const int64_t now) const
+ {
+ SharedPtr<Path> bp(getBestPath(now,false));
+ if (bp)
+ return bp->latency();
+ return 0xffff;
+ }
/**
* This computes a quality score for relays and root servers
@@ -261,71 +289,21 @@ public:
*
* @return Relay quality score computed from latency and other factors, lower is better
*/
- inline unsigned int relayQuality(const uint64_t now) const
+ inline unsigned int relayQuality(const int64_t now) const
{
const uint64_t tsr = now - _lastReceive;
if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
return (~(unsigned int)0);
- unsigned int l = _latency;
+ unsigned int l = latency(now);
if (!l)
l = 0xffff;
- return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
+ return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1));
}
/**
- * Update latency with a new direct measurment
- *
- * @param l Direct latency measurment in ms
- */
- inline void addDirectLatencyMeasurment(unsigned int l)
- {
- unsigned int ol = _latency;
- if ((ol > 0)&&(ol < 10000))
- _latency = (ol + std::min(l,(unsigned int)65535)) / 2;
- else _latency = std::min(l,(unsigned int)65535);
- }
-
- /**
- * @param now Current time
- * @return True if this peer has at least one active direct path
- */
- inline bool hasActiveDirectPath(uint64_t now) const
- {
- for(unsigned int p=0;p<_numPaths;++p) {
- if (_paths[p].active(now))
- return true;
- }
- return false;
- }
-
-#ifdef ZT_ENABLE_CLUSTER
- /**
- * @param now Current time
- * @return True if this peer has at least one active direct path that is not cluster-suboptimal
- */
- inline bool hasClusterOptimalPath(uint64_t now) const
- {
- for(unsigned int p=0,np=_numPaths;p<np;++p) {
- if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal()))
- return true;
- }
- return false;
- }
-#endif
-
- /**
- * Reset paths within a given scope
- *
- * @param scope IP scope of paths to reset
- * @param now Current time
- * @return True if at least one path was forgotten
- */
- bool resetWithinScope(InetAddress::IpScope scope,uint64_t now);
-
- /**
* @return 256-bit secret symmetric encryption key
*/
- inline const unsigned char *key() const throw() { return _key; }
+ inline const unsigned char *key() const { return _key; }
/**
* Set the currently known remote version of this peer's client
@@ -343,259 +321,213 @@ public:
_vRevision = (uint16_t)vrev;
}
- inline unsigned int remoteVersionProtocol() const throw() { return _vProto; }
- inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
- inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
- inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
+ inline unsigned int remoteVersionProtocol() const { return _vProto; }
+ inline unsigned int remoteVersionMajor() const { return _vMajor; }
+ inline unsigned int remoteVersionMinor() const { return _vMinor; }
+ inline unsigned int remoteVersionRevision() const { return _vRevision; }
- inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
+ inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
/**
- * Get most recently active path addresses for IPv4 and/or IPv6
- *
- * Note that v4 and v6 are not modified if they are not found, so
- * initialize these to a NULL address to be able to check.
- *
- * @param now Current time
- * @param v4 Result parameter to receive active IPv4 address, if any
- * @param v6 Result parameter to receive active IPv6 address, if any
+ * @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
*/
- void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
+ inline bool trustEstablished(const int64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
/**
- * Check network COM agreement with this peer
- *
- * @param nwid Network ID
- * @param com Another certificate of membership
- * @return True if supplied COM agrees with ours, false if not or if we don't have one
+ * Rate limit gate for VERB_PUSH_DIRECT_PATHS
*/
- bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const;
+ inline bool rateGatePushDirectPaths(const int64_t now)
+ {
+ if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
+ ++_directPathPushCutoffCount;
+ else _directPathPushCutoffCount = 0;
+ _lastDirectPathPushReceive = now;
+ return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
+ }
/**
- * Check the validity of the COM and add/update if valid and new
- *
- * @param nwid Network ID
- * @param com Externally supplied COM
+ * Rate limit gate for VERB_NETWORK_CREDENTIALS
*/
- bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com);
+ inline bool rateGateCredentialsReceived(const int64_t now)
+ {
+ if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
+ ++_credentialsCutoffCount;
+ else _credentialsCutoffCount = 0;
+ _lastCredentialsReceived = now;
+ return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
+ }
/**
- * @param nwid Network ID
- * @param now Current time
- * @param updateLastPushedTime If true, go ahead and update the last pushed time regardless of return value
- * @return Whether or not this peer needs another COM push from us
+ * Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
*/
- bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime);
+ inline bool rateGateRequestCredentials(const int64_t now)
+ {
+ if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastCredentialRequestSent = now;
+ return true;
+ }
+ return false;
+ }
/**
- * Perform periodic cleaning operations
- *
- * @param now Current time
+ * Rate limit gate for inbound WHOIS requests
*/
- void clean(uint64_t now);
+ inline bool rateGateInboundWhoisRequest(const int64_t now)
+ {
+ if ((now - _lastWhoisRequestReceived) >= ZT_PEER_WHOIS_RATE_LIMIT) {
+ _lastWhoisRequestReceived = now;
+ return true;
+ }
+ return false;
+ }
/**
- * Update direct path push stats and return true if we should respond
- *
- * This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly
- * useful as a DDOS amplification attack vector. Otherwise a malicious peer
- * could send loads of these and cause others to bombard arbitrary IPs with
- * traffic.
- *
- * @param now Current time
- * @return True if we should respond
+ * Rate limit gate for inbound ECHO requests
*/
- inline bool shouldRespondToDirectPathPush(const uint64_t now)
+ inline bool rateGateEchoRequest(const int64_t now)
{
- if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
- ++_directPathPushCutoffCount;
- else _directPathPushCutoffCount = 0;
- _lastDirectPathPushReceive = now;
- return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
+ if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastEchoRequestReceived = now;
+ return true;
+ }
+ return false;
}
/**
- * Find a common set of addresses by which two peers can link, if any
- *
- * @param a Peer A
- * @param b Peer B
- * @param now Current time
- * @return Pair: B's address (to send to A), A's address (to send to B)
+ * Rate gate incoming requests for network COM
*/
- static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now)
+ inline bool rateGateIncomingComRequest(const int64_t now)
{
- std::pair<InetAddress,InetAddress> v4,v6;
- b.getBestActiveAddresses(now,v4.first,v6.first);
- a.getBestActiveAddresses(now,v4.second,v6.second);
- if ((v6.first)&&(v6.second)) // prefer IPv6 if both have it since NAT-t is (almost) unnecessary
- return v6;
- else if ((v4.first)&&(v4.second))
- return v4;
- else return std::pair<InetAddress,InetAddress>();
+ if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastComRequestReceived = now;
+ return true;
+ }
+ return false;
}
- template<unsigned int C>
- inline void serialize(Buffer<C> &b) const
+ /**
+ * Rate gate outgoing requests for network COM
+ */
+ inline bool rateGateOutgoingComRequest(const int64_t now)
{
- Mutex::Lock _l(_networkComs_m);
-
- const unsigned int recSizePos = b.size();
- b.addSize(4); // space for uint32_t field length
+ if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
+ _lastComRequestSent = now;
+ return true;
+ }
+ return false;
+ }
- b.append((uint16_t)1); // version of serialized Peer data
+ /**
+ * Serialize a peer for storage in local cache
+ *
+ * This does not serialize everything, just non-ephemeral information.
+ */
+ template<unsigned int C>
+ inline void serializeForCache(Buffer<C> &b) const
+ {
+ b.append((uint8_t)1);
- _id.serialize(b,false);
+ _id.serialize(b);
- b.append((uint64_t)_lastUsed);
- b.append((uint64_t)_lastReceive);
- b.append((uint64_t)_lastUnicastFrame);
- b.append((uint64_t)_lastMulticastFrame);
- b.append((uint64_t)_lastAnnouncedTo);
- b.append((uint64_t)_lastDirectPathPushSent);
- b.append((uint64_t)_lastDirectPathPushReceive);
- b.append((uint64_t)_lastPathSort);
b.append((uint16_t)_vProto);
b.append((uint16_t)_vMajor);
b.append((uint16_t)_vMinor);
b.append((uint16_t)_vRevision);
- b.append((uint32_t)_latency);
- b.append((uint16_t)_directPathPushCutoffCount);
-
- b.append((uint16_t)_numPaths);
- for(unsigned int i=0;i<_numPaths;++i)
- _paths[i].serialize(b);
-
- b.append((uint32_t)_networkComs.size());
- {
- uint64_t *k = (uint64_t *)0;
- _NetworkCom *v = (_NetworkCom *)0;
- Hashtable<uint64_t,_NetworkCom>::Iterator i(const_cast<Peer *>(this)->_networkComs);
- while (i.next(k,v)) {
- b.append((uint64_t)*k);
- b.append((uint64_t)v->ts);
- v->com.serialize(b);
- }
- }
- b.append((uint32_t)_lastPushedComs.size());
{
- uint64_t *k = (uint64_t *)0;
- uint64_t *v = (uint64_t *)0;
- Hashtable<uint64_t,uint64_t>::Iterator i(const_cast<Peer *>(this)->_lastPushedComs);
- while (i.next(k,v)) {
- b.append((uint64_t)*k);
- b.append((uint64_t)*v);
+ Mutex::Lock _l(_paths_m);
+ unsigned int pc = 0;
+ for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+ if (_paths[i].p)
+ ++pc;
+ else break;
}
+ b.append((uint16_t)pc);
+ for(unsigned int i=0;i<pc;++i)
+ _paths[i].p->address().serialize(b);
}
-
- b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size
}
- /**
- * Create a new Peer from a serialized instance
- *
- * @param renv Runtime environment
- * @param myIdentity This node's identity
- * @param b Buffer containing serialized Peer data
- * @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result)
- * @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer)
- */
template<unsigned int C>
- static inline SharedPtr<Peer> deserializeNew(const RuntimeEnvironment *renv,const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
+ inline static SharedPtr<Peer> deserializeFromCache(int64_t now,void *tPtr,Buffer<C> &b,const RuntimeEnvironment *renv)
{
- const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
- if ((p + recSize) > b.size())
- return SharedPtr<Peer>(); // size invalid
- if (b.template at<uint16_t>(p) != 1)
- return SharedPtr<Peer>(); // version mismatch
- p += 2;
-
- Identity npid;
- p += npid.deserialize(b,p);
- if (!npid)
- return SharedPtr<Peer>();
-
- SharedPtr<Peer> np(new Peer(renv,myIdentity,npid));
-
- np->_lastUsed = b.template at<uint64_t>(p); p += 8;
- np->_lastReceive = b.template at<uint64_t>(p); p += 8;
- np->_lastUnicastFrame = b.template at<uint64_t>(p); p += 8;
- np->_lastMulticastFrame = b.template at<uint64_t>(p); p += 8;
- np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
- np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8;
- np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8;
- np->_lastPathSort = b.template at<uint64_t>(p); p += 8;
- np->_vProto = b.template at<uint16_t>(p); p += 2;
- np->_vMajor = b.template at<uint16_t>(p); p += 2;
- np->_vMinor = b.template at<uint16_t>(p); p += 2;
- np->_vRevision = b.template at<uint16_t>(p); p += 2;
- np->_latency = b.template at<uint32_t>(p); p += 4;
- np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2;
-
- const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
- for(unsigned int i=0;i<numPaths;++i) {
- if (i < ZT_MAX_PEER_NETWORK_PATHS) {
- p += np->_paths[np->_numPaths++].deserialize(b,p);
- } else {
- // Skip any paths beyond max, but still read stream
- Path foo;
- p += foo.deserialize(b,p);
+ try {
+ unsigned int ptr = 0;
+ if (b[ptr++] != 1)
+ return SharedPtr<Peer>();
+
+ Identity id;
+ ptr += id.deserialize(b,ptr);
+ if (!id)
+ return SharedPtr<Peer>();
+
+ SharedPtr<Peer> p(new Peer(renv,renv->identity,id));
+
+ p->_vProto = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vMajor = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vMinor = b.template at<uint16_t>(ptr); ptr += 2;
+ p->_vRevision = b.template at<uint16_t>(ptr); ptr += 2;
+
+ // When we deserialize from the cache we don't actually restore paths. We
+ // just try them and then re-learn them if they happen to still be up.
+ // Paths are fairly ephemeral in the real world in most cases.
+ const unsigned int tryPathCount = b.template at<uint16_t>(ptr); ptr += 2;
+ for(unsigned int i=0;i<tryPathCount;++i) {
+ InetAddress inaddr;
+ try {
+ ptr += inaddr.deserialize(b,ptr);
+ if (inaddr)
+ p->attemptToContactAt(tPtr,-1,inaddr,now,true);
+ } catch ( ... ) {
+ break;
+ }
}
- }
-
- const unsigned int numNetworkComs = b.template at<uint32_t>(p); p += 4;
- for(unsigned int i=0;i<numNetworkComs;++i) {
- _NetworkCom &c = np->_networkComs[b.template at<uint64_t>(p)]; p += 8;
- c.ts = b.template at<uint64_t>(p); p += 8;
- p += c.com.deserialize(b,p);
- }
- const unsigned int numLastPushed = b.template at<uint32_t>(p); p += 4;
- for(unsigned int i=0;i<numLastPushed;++i) {
- const uint64_t nwid = b.template at<uint64_t>(p); p += 8;
- const uint64_t ts = b.template at<uint64_t>(p); p += 8;
- np->_lastPushedComs.set(nwid,ts);
+ return p;
+ } catch ( ... ) {
+ return SharedPtr<Peer>();
}
-
- return np;
}
private:
- void _doDeadPathDetection(Path &p,const uint64_t now);
- Path *_getBestPath(const uint64_t now);
- Path *_getBestPath(const uint64_t now,int inetAddressFamily);
+ struct _PeerPath
+ {
+ _PeerPath() : lr(0),p(),priority(1) {}
+ int64_t lr; // time of last valid ZeroTier packet
+ SharedPtr<Path> p;
+ long priority; // >= 1, higher is better
+ };
- unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized
+ uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
const RuntimeEnvironment *RR;
- uint64_t _lastUsed;
- uint64_t _lastReceive; // direct or indirect
- uint64_t _lastUnicastFrame;
- uint64_t _lastMulticastFrame;
- uint64_t _lastAnnouncedTo;
- uint64_t _lastDirectPathPushSent;
- uint64_t _lastDirectPathPushReceive;
- uint64_t _lastPathSort;
+
+ int64_t _lastReceive; // direct or indirect
+ int64_t _lastNontrivialReceive; // frames, things like netconf, etc.
+ int64_t _lastTriedMemorizedPath;
+ int64_t _lastDirectPathPushSent;
+ int64_t _lastDirectPathPushReceive;
+ int64_t _lastCredentialRequestSent;
+ int64_t _lastWhoisRequestReceived;
+ int64_t _lastEchoRequestReceived;
+ int64_t _lastComRequestReceived;
+ int64_t _lastComRequestSent;
+ int64_t _lastCredentialsReceived;
+ int64_t _lastTrustEstablishedPacketReceived;
+ int64_t _lastSentFullHello;
+
uint16_t _vProto;
uint16_t _vMajor;
uint16_t _vMinor;
uint16_t _vRevision;
+
+ _PeerPath _paths[ZT_MAX_PEER_NETWORK_PATHS];
+ Mutex _paths_m;
+
Identity _id;
- Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
- unsigned int _numPaths;
- unsigned int _latency;
- unsigned int _directPathPushCutoffCount;
- struct _NetworkCom
- {
- _NetworkCom() {}
- _NetworkCom(uint64_t t,const CertificateOfMembership &c) : ts(t),com(c) {}
- uint64_t ts;
- CertificateOfMembership com;
- };
- Hashtable<uint64_t,_NetworkCom> _networkComs;
- Hashtable<uint64_t,uint64_t> _lastPushedComs;
- Mutex _networkComs_m;
+ unsigned int _directPathPushCutoffCount;
+ unsigned int _credentialsCutoffCount;
AtomicCounter __refCount;
};
diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp
index b78071f6..eceb57b3 100644
--- a/node/Poly1305.cpp
+++ b/node/Poly1305.cpp
@@ -121,7 +121,6 @@ static inline int crypto_onetimeauth(unsigned char *out,const unsigned char *in,
}
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
- throw()
{
crypto_onetimeauth((unsigned char *)auth,(const unsigned char *)data,len,(const unsigned char *)key);
}
@@ -135,7 +134,7 @@ typedef struct poly1305_context {
unsigned char opaque[136];
} poly1305_context;
-#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__))
+#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
//////////////////////////////////////////////////////////////////////////////
// 128-bit implementation for MSC and GCC from Poly1305-donna
@@ -183,9 +182,9 @@ typedef struct poly1305_state_internal_t {
unsigned char final;
} poly1305_state_internal_t;
-/* interpret eight 8 bit unsigned integers as a 64 bit unsigned integer in little endian */
-static inline unsigned long long
-U8TO64(const unsigned char *p) {
+#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
+static inline unsigned long long U8TO64(const unsigned char *p)
+{
return
(((unsigned long long)(p[0] & 0xff) ) |
((unsigned long long)(p[1] & 0xff) << 8) |
@@ -196,10 +195,13 @@ U8TO64(const unsigned char *p) {
((unsigned long long)(p[6] & 0xff) << 48) |
((unsigned long long)(p[7] & 0xff) << 56));
}
+#else
+#define U8TO64(p) (*reinterpret_cast<const unsigned long long *>(p))
+#endif
-/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little endian */
-static inline void
-U64TO8(unsigned char *p, unsigned long long v) {
+#if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN)
+static inline void U64TO8(unsigned char *p, unsigned long long v)
+{
p[0] = (v ) & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
@@ -209,6 +211,9 @@ U64TO8(unsigned char *p, unsigned long long v) {
p[6] = (v >> 48) & 0xff;
p[7] = (v >> 56) & 0xff;
}
+#else
+#define U64TO8(p,v) ((*reinterpret_cast<unsigned long long *>(p)) = (v))
+#endif
static inline void
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
@@ -617,7 +622,6 @@ poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
} // anonymous namespace
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)
- throw()
{
poly1305_context ctx;
poly1305_init(&ctx,reinterpret_cast<const unsigned char *>(key));
diff --git a/node/Poly1305.hpp b/node/Poly1305.hpp
index 62d57546..adcc2410 100644
--- a/node/Poly1305.hpp
+++ b/node/Poly1305.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_POLY1305_HPP
@@ -46,8 +54,7 @@ public:
* @param len Length of data to authenticate in bytes
* @param key 32-byte one-time use key to authenticate data (must not be reused)
*/
- static void compute(void *auth,const void *data,unsigned int len,const void *key)
- throw();
+ static void compute(void *auth,const void *data,unsigned int len,const void *key);
};
} // namespace ZeroTier
diff --git a/node/README.md b/node/README.md
index 01378c75..1728400c 100644
--- a/node/README.md
+++ b/node/README.md
@@ -1,4 +1,4 @@
-ZeroTier Virtual Switch Core
+ZeroTier Network Hypervisor Core
======
This directory contains the *real* ZeroTier: a completely OS-independent global virtual Ethernet switch engine. This is where the magic happens.
diff --git a/node/Revocation.cpp b/node/Revocation.cpp
new file mode 100644
index 00000000..78098f8c
--- /dev/null
+++ b/node/Revocation.cpp
@@ -0,0 +1,55 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Revocation.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<sizeof(Revocation) + 64> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
new file mode 100644
index 00000000..eaf01915
--- /dev/null
+++ b/node/Revocation.hpp
@@ -0,0 +1,197 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_REVOCATION_HPP
+#define ZT_REVOCATION_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
+#include "Credential.hpp"
+#include "Address.hpp"
+#include "C25519.hpp"
+#include "Utils.hpp"
+#include "Buffer.hpp"
+#include "Identity.hpp"
+
+/**
+ * Flag: fast propagation via rumor mill algorithm
+ */
+#define ZT_REVOCATION_FLAG_FAST_PROPAGATE 0x1ULL
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Revocation certificate to instantaneously revoke a COM, capability, or tag
+ */
+class Revocation : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; }
+
+ Revocation()
+ {
+ memset(this,0,sizeof(Revocation));
+ }
+
+ /**
+ * @param i ID (arbitrary for revocations, currently random)
+ * @param nwid Network ID
+ * @param cid Credential ID being revoked (0 for all or for COMs, which lack IDs)
+ * @param thr Revocation time threshold before which credentials will be revoked
+ * @param fl Flags
+ * @param tgt Target node whose credential(s) are being revoked
+ * @param ct Credential type being revoked
+ */
+ Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) :
+ _id(i),
+ _credentialId(cid),
+ _networkId(nwid),
+ _threshold(thr),
+ _flags(fl),
+ _target(tgt),
+ _signedBy(),
+ _type(ct) {}
+
+ inline uint32_t id() const { return _id; }
+ inline uint32_t credentialId() const { return _credentialId; }
+ inline uint64_t networkId() const { return _networkId; }
+ inline int64_t threshold() const { return _threshold; }
+ inline const Address &target() const { return _target; }
+ inline const Address &signer() const { return _signedBy; }
+ inline Credential::Type type() const { return _type; }
+
+ inline bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
+
+ /**
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(Revocation) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Verify this revocation's signature
+ *
+ * @param RR Runtime environment to provide for peer lookup, etc.
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append((uint32_t)0); // 4 unused bytes, currently set to 0
+ b.append(_id);
+ b.append(_networkId);
+ b.append((uint32_t)0); // 4 unused bytes, currently set to 0
+ b.append(_credentialId);
+ b.append(_threshold);
+ b.append(_flags);
+ _target.appendTo(b);
+ _signedBy.appendTo(b);
+ b.append((uint8_t)_type);
+
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519 signature
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ // This is the size of any additional fields, currently 0.
+ b.append((uint16_t)0);
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ memset(this,0,sizeof(Revocation));
+
+ unsigned int p = startAt;
+
+ p += 4; // 4 bytes, currently unused
+ _id = b.template at<uint32_t>(p); p += 4;
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ p += 4; // 4 bytes, currently unused
+ _credentialId = b.template at<uint32_t>(p); p += 4;
+ _threshold = b.template at<uint64_t>(p); p += 8;
+ _flags = b.template at<uint64_t>(p); p += 8;
+ _target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _type = (Credential::Type)b[p++];
+
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
+ p += 2;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
+ p += ZT_C25519_SIGNATURE_LEN;
+ } else throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+private:
+ uint32_t _id;
+ uint32_t _credentialId;
+ uint64_t _networkId;
+ int64_t _threshold;
+ uint64_t _flags;
+ Address _target;
+ Address _signedBy;
+ Credential::Type _type;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
index 1f527733..46350b4a 100644
--- a/node/RuntimeEnvironment.hpp
+++ b/node/RuntimeEnvironment.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,16 +14,24 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_RUNTIMEENVIRONMENT_HPP
#define ZT_RUNTIMEENVIRONMENT_HPP
-#include <string>
+#include <string.h>
#include "Constants.hpp"
+#include "Utils.hpp"
#include "Identity.hpp"
-#include "Mutex.hpp"
namespace ZeroTier {
@@ -34,8 +42,7 @@ class Node;
class Multicaster;
class NetworkController;
class SelfAwareness;
-class Cluster;
-class DeferredPackets;
+class Trace;
/**
* Holds global state for an instance of ZeroTier::Node
@@ -45,52 +52,47 @@ class RuntimeEnvironment
public:
RuntimeEnvironment(Node *n) :
node(n)
- ,identity()
,localNetworkController((NetworkController *)0)
+ ,rtmem((void *)0)
,sw((Switch *)0)
,mc((Multicaster *)0)
,topology((Topology *)0)
,sa((SelfAwareness *)0)
- ,dp((DeferredPackets *)0)
-#ifdef ZT_ENABLE_CLUSTER
- ,cluster((Cluster *)0)
-#endif
- ,dpEnabled(0)
{
+ publicIdentityStr[0] = (char)0;
+ secretIdentityStr[0] = (char)0;
+ }
+
+ ~RuntimeEnvironment()
+ {
+ Utils::burn(secretIdentityStr,sizeof(secretIdentityStr));
}
// Node instance that owns this RuntimeEnvironment
Node *const node;
- // This node's identity
- Identity identity;
- std::string publicIdentityStr;
- std::string secretIdentityStr;
-
// This is set externally to an instance of this base class
NetworkController *localNetworkController;
- /*
- * Order matters a bit here. These are constructed in this order
+ // Memory actually occupied by Trace, Switch, etc.
+ void *rtmem;
+
+ /* Order matters a bit here. These are constructed in this order
* and then deleted in the opposite order on Node exit. The order ensures
* that things that are needed are there before they're needed.
*
- * These are constant and never null after startup unless indicated.
- */
+ * These are constant and never null after startup unless indicated. */
+ Trace *t;
Switch *sw;
Multicaster *mc;
Topology *topology;
SelfAwareness *sa;
- DeferredPackets *dp;
-#ifdef ZT_ENABLE_CLUSTER
- Cluster *cluster;
-#endif
-
- // This is set to >0 if background threads are waiting on deferred
- // packets, otherwise 'dp' should not be used.
- volatile int dpEnabled;
+ // This node's identity and string representations thereof
+ Identity identity;
+ char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+ char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
};
} // namespace ZeroTier
diff --git a/node/SHA512.cpp b/node/SHA512.cpp
index 76737d37..d3c938af 100644
--- a/node/SHA512.cpp
+++ b/node/SHA512.cpp
@@ -1,20 +1,11 @@
+// Code taken from NaCl by D. J. Bernstein and others
+// Public domain
+
/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+20080913
+D. J. Bernstein
+Public domain.
+*/
#include <stdint.h>
#include <stdlib.h>
@@ -23,19 +14,37 @@
#include "SHA512.hpp"
#include "Utils.hpp"
+#ifdef __APPLE__
+#include <CommonCrypto/CommonDigest.h>
+#define ZT_HAVE_NATIVE_SHA512
namespace ZeroTier {
+void SHA512::hash(void *digest,const void *data,unsigned int len)
+{
+ CC_SHA512_CTX ctx;
+ CC_SHA512_Init(&ctx);
+ CC_SHA512_Update(&ctx,data,len);
+ CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+}
+#endif
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
+#ifdef ZT_USE_LIBCRYPTO
+#include <openssl/sha.h>
+#define ZT_HAVE_NATIVE_SHA512
+namespace ZeroTier {
+void SHA512::hash(void *digest,const void *data,unsigned int len)
+{
+ SHA512_CTX ctx;
+ SHA512_Init(&ctx);
+ SHA512_Update(&ctx,data,len);
+ SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
+}
+#endif
-// Code taken from NaCl by D. J. Bernstein and others
-// Public domain
+#ifndef ZT_HAVE_NATIVE_SHA512
-/*
-20080913
-D. J. Bernstein
-Public domain.
-*/
+namespace ZeroTier {
#define uint64 uint64_t
@@ -43,28 +52,28 @@ Public domain.
static uint64 load_bigendian(const unsigned char *x)
{
- return
- (uint64) (x[7]) \
- | (((uint64) (x[6])) << 8) \
- | (((uint64) (x[5])) << 16) \
- | (((uint64) (x[4])) << 24) \
- | (((uint64) (x[3])) << 32) \
- | (((uint64) (x[2])) << 40) \
- | (((uint64) (x[1])) << 48) \
- | (((uint64) (x[0])) << 56)
- ;
+ return
+ (uint64) (x[7]) \
+ | (((uint64) (x[6])) << 8) \
+ | (((uint64) (x[5])) << 16) \
+ | (((uint64) (x[4])) << 24) \
+ | (((uint64) (x[3])) << 32) \
+ | (((uint64) (x[2])) << 40) \
+ | (((uint64) (x[1])) << 48) \
+ | (((uint64) (x[0])) << 56)
+ ;
}
static void store_bigendian(unsigned char *x,uint64 u)
{
- x[7] = u; u >>= 8;
- x[6] = u; u >>= 8;
- x[5] = u; u >>= 8;
- x[4] = u; u >>= 8;
- x[3] = u; u >>= 8;
- x[2] = u; u >>= 8;
- x[1] = u; u >>= 8;
- x[0] = u;
+ x[7] = u; u >>= 8;
+ x[6] = u; u >>= 8;
+ x[5] = u; u >>= 8;
+ x[4] = u; u >>= 8;
+ x[3] = u; u >>= 8;
+ x[2] = u; u >>= 8;
+ x[1] = u; u >>= 8;
+ x[0] = u;
}
#else // !ZT_NO_TYPE_PUNNING
@@ -87,266 +96,272 @@ static void store_bigendian(unsigned char *x,uint64 u)
#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0;
#define EXPAND \
- M(w0 ,w14,w9 ,w1 ) \
- M(w1 ,w15,w10,w2 ) \
- M(w2 ,w0 ,w11,w3 ) \
- M(w3 ,w1 ,w12,w4 ) \
- M(w4 ,w2 ,w13,w5 ) \
- M(w5 ,w3 ,w14,w6 ) \
- M(w6 ,w4 ,w15,w7 ) \
- M(w7 ,w5 ,w0 ,w8 ) \
- M(w8 ,w6 ,w1 ,w9 ) \
- M(w9 ,w7 ,w2 ,w10) \
- M(w10,w8 ,w3 ,w11) \
- M(w11,w9 ,w4 ,w12) \
- M(w12,w10,w5 ,w13) \
- M(w13,w11,w6 ,w14) \
- M(w14,w12,w7 ,w15) \
- M(w15,w13,w8 ,w0 )
+ M(w0 ,w14,w9 ,w1 ) \
+ M(w1 ,w15,w10,w2 ) \
+ M(w2 ,w0 ,w11,w3 ) \
+ M(w3 ,w1 ,w12,w4 ) \
+ M(w4 ,w2 ,w13,w5 ) \
+ M(w5 ,w3 ,w14,w6 ) \
+ M(w6 ,w4 ,w15,w7 ) \
+ M(w7 ,w5 ,w0 ,w8 ) \
+ M(w8 ,w6 ,w1 ,w9 ) \
+ M(w9 ,w7 ,w2 ,w10) \
+ M(w10,w8 ,w3 ,w11) \
+ M(w11,w9 ,w4 ,w12) \
+ M(w12,w10,w5 ,w13) \
+ M(w13,w11,w6 ,w14) \
+ M(w14,w12,w7 ,w15) \
+ M(w15,w13,w8 ,w0 )
#define F(w,k) \
- T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
- T2 = Sigma0(a) + Maj(a,b,c); \
- h = g; \
- g = f; \
- f = e; \
- e = d + T1; \
- d = c; \
- c = b; \
- b = a; \
- a = T1 + T2;
+ T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
+ T2 = Sigma0(a) + Maj(a,b,c); \
+ h = g; \
+ g = f; \
+ f = e; \
+ e = d + T1; \
+ d = c; \
+ c = b; \
+ b = a; \
+ a = T1 + T2;
static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
{
- uint64 state[8];
- uint64 a;
- uint64 b;
- uint64 c;
- uint64 d;
- uint64 e;
- uint64 f;
- uint64 g;
- uint64 h;
- uint64 T1;
- uint64 T2;
-
- a = load_bigendian(statebytes + 0); state[0] = a;
- b = load_bigendian(statebytes + 8); state[1] = b;
- c = load_bigendian(statebytes + 16); state[2] = c;
- d = load_bigendian(statebytes + 24); state[3] = d;
- e = load_bigendian(statebytes + 32); state[4] = e;
- f = load_bigendian(statebytes + 40); state[5] = f;
- g = load_bigendian(statebytes + 48); state[6] = g;
- h = load_bigendian(statebytes + 56); state[7] = h;
-
- while (inlen >= 128) {
- uint64 w0 = load_bigendian(in + 0);
- uint64 w1 = load_bigendian(in + 8);
- uint64 w2 = load_bigendian(in + 16);
- uint64 w3 = load_bigendian(in + 24);
- uint64 w4 = load_bigendian(in + 32);
- uint64 w5 = load_bigendian(in + 40);
- uint64 w6 = load_bigendian(in + 48);
- uint64 w7 = load_bigendian(in + 56);
- uint64 w8 = load_bigendian(in + 64);
- uint64 w9 = load_bigendian(in + 72);
- uint64 w10 = load_bigendian(in + 80);
- uint64 w11 = load_bigendian(in + 88);
- uint64 w12 = load_bigendian(in + 96);
- uint64 w13 = load_bigendian(in + 104);
- uint64 w14 = load_bigendian(in + 112);
- uint64 w15 = load_bigendian(in + 120);
-
- F(w0 ,0x428a2f98d728ae22ULL)
- F(w1 ,0x7137449123ef65cdULL)
- F(w2 ,0xb5c0fbcfec4d3b2fULL)
- F(w3 ,0xe9b5dba58189dbbcULL)
- F(w4 ,0x3956c25bf348b538ULL)
- F(w5 ,0x59f111f1b605d019ULL)
- F(w6 ,0x923f82a4af194f9bULL)
- F(w7 ,0xab1c5ed5da6d8118ULL)
- F(w8 ,0xd807aa98a3030242ULL)
- F(w9 ,0x12835b0145706fbeULL)
- F(w10,0x243185be4ee4b28cULL)
- F(w11,0x550c7dc3d5ffb4e2ULL)
- F(w12,0x72be5d74f27b896fULL)
- F(w13,0x80deb1fe3b1696b1ULL)
- F(w14,0x9bdc06a725c71235ULL)
- F(w15,0xc19bf174cf692694ULL)
-
- EXPAND
-
- F(w0 ,0xe49b69c19ef14ad2ULL)
- F(w1 ,0xefbe4786384f25e3ULL)
- F(w2 ,0x0fc19dc68b8cd5b5ULL)
- F(w3 ,0x240ca1cc77ac9c65ULL)
- F(w4 ,0x2de92c6f592b0275ULL)
- F(w5 ,0x4a7484aa6ea6e483ULL)
- F(w6 ,0x5cb0a9dcbd41fbd4ULL)
- F(w7 ,0x76f988da831153b5ULL)
- F(w8 ,0x983e5152ee66dfabULL)
- F(w9 ,0xa831c66d2db43210ULL)
- F(w10,0xb00327c898fb213fULL)
- F(w11,0xbf597fc7beef0ee4ULL)
- F(w12,0xc6e00bf33da88fc2ULL)
- F(w13,0xd5a79147930aa725ULL)
- F(w14,0x06ca6351e003826fULL)
- F(w15,0x142929670a0e6e70ULL)
-
- EXPAND
-
- F(w0 ,0x27b70a8546d22ffcULL)
- F(w1 ,0x2e1b21385c26c926ULL)
- F(w2 ,0x4d2c6dfc5ac42aedULL)
- F(w3 ,0x53380d139d95b3dfULL)
- F(w4 ,0x650a73548baf63deULL)
- F(w5 ,0x766a0abb3c77b2a8ULL)
- F(w6 ,0x81c2c92e47edaee6ULL)
- F(w7 ,0x92722c851482353bULL)
- F(w8 ,0xa2bfe8a14cf10364ULL)
- F(w9 ,0xa81a664bbc423001ULL)
- F(w10,0xc24b8b70d0f89791ULL)
- F(w11,0xc76c51a30654be30ULL)
- F(w12,0xd192e819d6ef5218ULL)
- F(w13,0xd69906245565a910ULL)
- F(w14,0xf40e35855771202aULL)
- F(w15,0x106aa07032bbd1b8ULL)
-
- EXPAND
-
- F(w0 ,0x19a4c116b8d2d0c8ULL)
- F(w1 ,0x1e376c085141ab53ULL)
- F(w2 ,0x2748774cdf8eeb99ULL)
- F(w3 ,0x34b0bcb5e19b48a8ULL)
- F(w4 ,0x391c0cb3c5c95a63ULL)
- F(w5 ,0x4ed8aa4ae3418acbULL)
- F(w6 ,0x5b9cca4f7763e373ULL)
- F(w7 ,0x682e6ff3d6b2b8a3ULL)
- F(w8 ,0x748f82ee5defb2fcULL)
- F(w9 ,0x78a5636f43172f60ULL)
- F(w10,0x84c87814a1f0ab72ULL)
- F(w11,0x8cc702081a6439ecULL)
- F(w12,0x90befffa23631e28ULL)
- F(w13,0xa4506cebde82bde9ULL)
- F(w14,0xbef9a3f7b2c67915ULL)
- F(w15,0xc67178f2e372532bULL)
-
- EXPAND
-
- F(w0 ,0xca273eceea26619cULL)
- F(w1 ,0xd186b8c721c0c207ULL)
- F(w2 ,0xeada7dd6cde0eb1eULL)
- F(w3 ,0xf57d4f7fee6ed178ULL)
- F(w4 ,0x06f067aa72176fbaULL)
- F(w5 ,0x0a637dc5a2c898a6ULL)
- F(w6 ,0x113f9804bef90daeULL)
- F(w7 ,0x1b710b35131c471bULL)
- F(w8 ,0x28db77f523047d84ULL)
- F(w9 ,0x32caab7b40c72493ULL)
- F(w10,0x3c9ebe0a15c9bebcULL)
- F(w11,0x431d67c49c100d4cULL)
- F(w12,0x4cc5d4becb3e42b6ULL)
- F(w13,0x597f299cfc657e2aULL)
- F(w14,0x5fcb6fab3ad6faecULL)
- F(w15,0x6c44198c4a475817ULL)
-
- a += state[0];
- b += state[1];
- c += state[2];
- d += state[3];
- e += state[4];
- f += state[5];
- g += state[6];
- h += state[7];
-
- state[0] = a;
- state[1] = b;
- state[2] = c;
- state[3] = d;
- state[4] = e;
- state[5] = f;
- state[6] = g;
- state[7] = h;
-
- in += 128;
- inlen -= 128;
- }
-
- store_bigendian(statebytes + 0,state[0]);
- store_bigendian(statebytes + 8,state[1]);
- store_bigendian(statebytes + 16,state[2]);
- store_bigendian(statebytes + 24,state[3]);
- store_bigendian(statebytes + 32,state[4]);
- store_bigendian(statebytes + 40,state[5]);
- store_bigendian(statebytes + 48,state[6]);
- store_bigendian(statebytes + 56,state[7]);
-
- return 0;
+ uint64 state[8];
+ uint64 a;
+ uint64 b;
+ uint64 c;
+ uint64 d;
+ uint64 e;
+ uint64 f;
+ uint64 g;
+ uint64 h;
+ uint64 T1;
+ uint64 T2;
+
+ a = load_bigendian(statebytes + 0); state[0] = a;
+ b = load_bigendian(statebytes + 8); state[1] = b;
+ c = load_bigendian(statebytes + 16); state[2] = c;
+ d = load_bigendian(statebytes + 24); state[3] = d;
+ e = load_bigendian(statebytes + 32); state[4] = e;
+ f = load_bigendian(statebytes + 40); state[5] = f;
+ g = load_bigendian(statebytes + 48); state[6] = g;
+ h = load_bigendian(statebytes + 56); state[7] = h;
+
+ while (inlen >= 128) {
+ uint64 w0 = load_bigendian(in + 0);
+ uint64 w1 = load_bigendian(in + 8);
+ uint64 w2 = load_bigendian(in + 16);
+ uint64 w3 = load_bigendian(in + 24);
+ uint64 w4 = load_bigendian(in + 32);
+ uint64 w5 = load_bigendian(in + 40);
+ uint64 w6 = load_bigendian(in + 48);
+ uint64 w7 = load_bigendian(in + 56);
+ uint64 w8 = load_bigendian(in + 64);
+ uint64 w9 = load_bigendian(in + 72);
+ uint64 w10 = load_bigendian(in + 80);
+ uint64 w11 = load_bigendian(in + 88);
+ uint64 w12 = load_bigendian(in + 96);
+ uint64 w13 = load_bigendian(in + 104);
+ uint64 w14 = load_bigendian(in + 112);
+ uint64 w15 = load_bigendian(in + 120);
+
+ F(w0 ,0x428a2f98d728ae22ULL)
+ F(w1 ,0x7137449123ef65cdULL)
+ F(w2 ,0xb5c0fbcfec4d3b2fULL)
+ F(w3 ,0xe9b5dba58189dbbcULL)
+ F(w4 ,0x3956c25bf348b538ULL)
+ F(w5 ,0x59f111f1b605d019ULL)
+ F(w6 ,0x923f82a4af194f9bULL)
+ F(w7 ,0xab1c5ed5da6d8118ULL)
+ F(w8 ,0xd807aa98a3030242ULL)
+ F(w9 ,0x12835b0145706fbeULL)
+ F(w10,0x243185be4ee4b28cULL)
+ F(w11,0x550c7dc3d5ffb4e2ULL)
+ F(w12,0x72be5d74f27b896fULL)
+ F(w13,0x80deb1fe3b1696b1ULL)
+ F(w14,0x9bdc06a725c71235ULL)
+ F(w15,0xc19bf174cf692694ULL)
+
+ EXPAND
+
+ F(w0 ,0xe49b69c19ef14ad2ULL)
+ F(w1 ,0xefbe4786384f25e3ULL)
+ F(w2 ,0x0fc19dc68b8cd5b5ULL)
+ F(w3 ,0x240ca1cc77ac9c65ULL)
+ F(w4 ,0x2de92c6f592b0275ULL)
+ F(w5 ,0x4a7484aa6ea6e483ULL)
+ F(w6 ,0x5cb0a9dcbd41fbd4ULL)
+ F(w7 ,0x76f988da831153b5ULL)
+ F(w8 ,0x983e5152ee66dfabULL)
+ F(w9 ,0xa831c66d2db43210ULL)
+ F(w10,0xb00327c898fb213fULL)
+ F(w11,0xbf597fc7beef0ee4ULL)
+ F(w12,0xc6e00bf33da88fc2ULL)
+ F(w13,0xd5a79147930aa725ULL)
+ F(w14,0x06ca6351e003826fULL)
+ F(w15,0x142929670a0e6e70ULL)
+
+ EXPAND
+
+ F(w0 ,0x27b70a8546d22ffcULL)
+ F(w1 ,0x2e1b21385c26c926ULL)
+ F(w2 ,0x4d2c6dfc5ac42aedULL)
+ F(w3 ,0x53380d139d95b3dfULL)
+ F(w4 ,0x650a73548baf63deULL)
+ F(w5 ,0x766a0abb3c77b2a8ULL)
+ F(w6 ,0x81c2c92e47edaee6ULL)
+ F(w7 ,0x92722c851482353bULL)
+ F(w8 ,0xa2bfe8a14cf10364ULL)
+ F(w9 ,0xa81a664bbc423001ULL)
+ F(w10,0xc24b8b70d0f89791ULL)
+ F(w11,0xc76c51a30654be30ULL)
+ F(w12,0xd192e819d6ef5218ULL)
+ F(w13,0xd69906245565a910ULL)
+ F(w14,0xf40e35855771202aULL)
+ F(w15,0x106aa07032bbd1b8ULL)
+
+ EXPAND
+
+ F(w0 ,0x19a4c116b8d2d0c8ULL)
+ F(w1 ,0x1e376c085141ab53ULL)
+ F(w2 ,0x2748774cdf8eeb99ULL)
+ F(w3 ,0x34b0bcb5e19b48a8ULL)
+ F(w4 ,0x391c0cb3c5c95a63ULL)
+ F(w5 ,0x4ed8aa4ae3418acbULL)
+ F(w6 ,0x5b9cca4f7763e373ULL)
+ F(w7 ,0x682e6ff3d6b2b8a3ULL)
+ F(w8 ,0x748f82ee5defb2fcULL)
+ F(w9 ,0x78a5636f43172f60ULL)
+ F(w10,0x84c87814a1f0ab72ULL)
+ F(w11,0x8cc702081a6439ecULL)
+ F(w12,0x90befffa23631e28ULL)
+ F(w13,0xa4506cebde82bde9ULL)
+ F(w14,0xbef9a3f7b2c67915ULL)
+ F(w15,0xc67178f2e372532bULL)
+
+ EXPAND
+
+ F(w0 ,0xca273eceea26619cULL)
+ F(w1 ,0xd186b8c721c0c207ULL)
+ F(w2 ,0xeada7dd6cde0eb1eULL)
+ F(w3 ,0xf57d4f7fee6ed178ULL)
+ F(w4 ,0x06f067aa72176fbaULL)
+ F(w5 ,0x0a637dc5a2c898a6ULL)
+ F(w6 ,0x113f9804bef90daeULL)
+ F(w7 ,0x1b710b35131c471bULL)
+ F(w8 ,0x28db77f523047d84ULL)
+ F(w9 ,0x32caab7b40c72493ULL)
+ F(w10,0x3c9ebe0a15c9bebcULL)
+ F(w11,0x431d67c49c100d4cULL)
+ F(w12,0x4cc5d4becb3e42b6ULL)
+ F(w13,0x597f299cfc657e2aULL)
+ F(w14,0x5fcb6fab3ad6faecULL)
+ F(w15,0x6c44198c4a475817ULL)
+
+ a += state[0];
+ b += state[1];
+ c += state[2];
+ d += state[3];
+ e += state[4];
+ f += state[5];
+ g += state[6];
+ h += state[7];
+
+ state[0] = a;
+ state[1] = b;
+ state[2] = c;
+ state[3] = d;
+ state[4] = e;
+ state[5] = f;
+ state[6] = g;
+ state[7] = h;
+
+ in += 128;
+ inlen -= 128;
+ }
+
+ store_bigendian(statebytes + 0,state[0]);
+ store_bigendian(statebytes + 8,state[1]);
+ store_bigendian(statebytes + 16,state[2]);
+ store_bigendian(statebytes + 24,state[3]);
+ store_bigendian(statebytes + 32,state[4]);
+ store_bigendian(statebytes + 40,state[5]);
+ store_bigendian(statebytes + 48,state[6]);
+ store_bigendian(statebytes + 56,state[7]);
+
+ return 0;
}
#define blocks crypto_hashblocks
static const unsigned char iv[64] = {
- 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
- 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
- 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
- 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
- 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
- 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
- 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
- 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
+ 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
+ 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
+ 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
+ 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
+ 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
+ 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
+ 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
+ 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
};
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-
void SHA512::hash(void *digest,const void *data,unsigned int len)
{
- unsigned char h[64];
- unsigned char padded[256];
- int i;
- uint64_t bytes = len;
-
- const unsigned char *in = (const unsigned char *)data;
- unsigned int inlen = len;
-
- for (i = 0;i < 64;++i) h[i] = iv[i];
-
- blocks(h,in,inlen);
- in += inlen;
- inlen &= 127;
- in -= inlen;
-
- for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
- padded[inlen] = 0x80;
-
- if (inlen < 112) {
- for (i = inlen + 1;i < 119;++i) padded[i] = 0;
- padded[119] = (unsigned char)((bytes >> 61) & 0xff);
- padded[120] = (unsigned char)((bytes >> 53) & 0xff);
- padded[121] = (unsigned char)((bytes >> 45) & 0xff);
- padded[122] = (unsigned char)((bytes >> 37) & 0xff);
- padded[123] = (unsigned char)((bytes >> 29) & 0xff);
- padded[124] = (unsigned char)((bytes >> 21) & 0xff);
- padded[125] = (unsigned char)((bytes >> 13) & 0xff);
- padded[126] = (unsigned char)((bytes >> 5) & 0xff);
- padded[127] = (unsigned char)((bytes << 3) & 0xff);
- blocks(h,padded,128);
- } else {
- for (i = inlen + 1;i < 247;++i) padded[i] = 0;
- padded[247] = (unsigned char)((bytes >> 61) & 0xff);
- padded[248] = (unsigned char)((bytes >> 53) & 0xff);
- padded[249] = (unsigned char)((bytes >> 45) & 0xff);
- padded[250] = (unsigned char)((bytes >> 37) & 0xff);
- padded[251] = (unsigned char)((bytes >> 29) & 0xff);
- padded[252] = (unsigned char)((bytes >> 21) & 0xff);
- padded[253] = (unsigned char)((bytes >> 13) & 0xff);
- padded[254] = (unsigned char)((bytes >> 5) & 0xff);
- padded[255] = (unsigned char)((bytes << 3) & 0xff);
- blocks(h,padded,256);
- }
-
- for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
+ unsigned char h[64];
+ unsigned char padded[256];
+ int i;
+ uint64_t bytes = len;
+
+ const unsigned char *in = (const unsigned char *)data;
+ unsigned int inlen = len;
+
+ for (i = 0;i < 64;++i) h[i] = iv[i];
+
+ blocks(h,in,inlen);
+ in += inlen;
+ inlen &= 127;
+ in -= inlen;
+
+ for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
+ padded[inlen] = 0x80;
+
+ if (inlen < 112) {
+ for (i = inlen + 1;i < 119;++i) padded[i] = 0;
+ padded[119] = (unsigned char)((bytes >> 61) & 0xff);
+ padded[120] = (unsigned char)((bytes >> 53) & 0xff);
+ padded[121] = (unsigned char)((bytes >> 45) & 0xff);
+ padded[122] = (unsigned char)((bytes >> 37) & 0xff);
+ padded[123] = (unsigned char)((bytes >> 29) & 0xff);
+ padded[124] = (unsigned char)((bytes >> 21) & 0xff);
+ padded[125] = (unsigned char)((bytes >> 13) & 0xff);
+ padded[126] = (unsigned char)((bytes >> 5) & 0xff);
+ padded[127] = (unsigned char)((bytes << 3) & 0xff);
+ blocks(h,padded,128);
+ } else {
+ for (i = inlen + 1;i < 247;++i) padded[i] = 0;
+ padded[247] = (unsigned char)((bytes >> 61) & 0xff);
+ padded[248] = (unsigned char)((bytes >> 53) & 0xff);
+ padded[249] = (unsigned char)((bytes >> 45) & 0xff);
+ padded[250] = (unsigned char)((bytes >> 37) & 0xff);
+ padded[251] = (unsigned char)((bytes >> 29) & 0xff);
+ padded[252] = (unsigned char)((bytes >> 21) & 0xff);
+ padded[253] = (unsigned char)((bytes >> 13) & 0xff);
+ padded[254] = (unsigned char)((bytes >> 5) & 0xff);
+ padded[255] = (unsigned char)((bytes << 3) & 0xff);
+ blocks(h,padded,256);
+ }
+
+ for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
}
} // namespace ZeroTier
+
+#endif // !ZT_HAVE_NATIVE_SHA512
+
+// Internally re-export to included C code, which includes some fast crypto code ported in on some platforms.
+// This eliminates the need to link against a third party SHA512() from this code
+extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len)
+{
+ ZeroTier::SHA512::hash(digest,data,len);
+}
diff --git a/node/SHA512.hpp b/node/SHA512.hpp
index 639a7dfd..eedc284a 100644
--- a/node/SHA512.hpp
+++ b/node/SHA512.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SHA512_HPP
diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp
index 3aa19ac6..1d4117e3 100644
--- a/node/Salsa20.cpp
+++ b/node/Salsa20.cpp
@@ -66,65 +66,49 @@ static const _s20sseconsts _S20SSECONSTANTS;
namespace ZeroTier {
-void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
- throw()
+void Salsa20::init(const void *key,const void *iv)
{
#ifdef ZT_SALSA20_SSE
- const uint32_t *k = (const uint32_t *)key;
-
+ const uint32_t *const k = (const uint32_t *)key;
_state.i[0] = 0x61707865;
+ _state.i[1] = 0x3320646e;
+ _state.i[2] = 0x79622d32;
_state.i[3] = 0x6b206574;
- _state.i[13] = k[0];
- _state.i[10] = k[1];
- _state.i[7] = k[2];
_state.i[4] = k[3];
- if (kbits == 256) {
- k += 4;
- _state.i[1] = 0x3320646e;
- _state.i[2] = 0x79622d32;
- } else {
- _state.i[1] = 0x3120646e;
- _state.i[2] = 0x79622d36;
- }
- _state.i[15] = k[0];
- _state.i[12] = k[1];
- _state.i[9] = k[2];
- _state.i[6] = k[3];
- _state.i[14] = ((const uint32_t *)iv)[0];
- _state.i[11] = ((const uint32_t *)iv)[1];
_state.i[5] = 0;
+ _state.i[6] = k[7];
+ _state.i[7] = k[2];
_state.i[8] = 0;
+ _state.i[9] = k[6];
+ _state.i[10] = k[1];
+ _state.i[11] = ((const uint32_t *)iv)[1];
+ _state.i[12] = k[5];
+ _state.i[13] = k[0];
+ _state.i[14] = ((const uint32_t *)iv)[0];
+ _state.i[15] = k[4];
#else
- const char *constants;
- const uint8_t *k = (const uint8_t *)key;
-
+ const char *const constants = "expand 32-byte k";
+ const uint8_t *const k = (const uint8_t *)key;
+ _state.i[0] = U8TO32_LITTLE(constants + 0);
_state.i[1] = U8TO32_LITTLE(k + 0);
_state.i[2] = U8TO32_LITTLE(k + 4);
_state.i[3] = U8TO32_LITTLE(k + 8);
_state.i[4] = U8TO32_LITTLE(k + 12);
- if (kbits == 256) { /* recommended */
- k += 16;
- constants = "expand 32-byte k";
- } else { /* kbits == 128 */
- constants = "expand 16-byte k";
- }
_state.i[5] = U8TO32_LITTLE(constants + 4);
_state.i[6] = U8TO32_LITTLE(((const uint8_t *)iv) + 0);
_state.i[7] = U8TO32_LITTLE(((const uint8_t *)iv) + 4);
_state.i[8] = 0;
_state.i[9] = 0;
_state.i[10] = U8TO32_LITTLE(constants + 8);
- _state.i[11] = U8TO32_LITTLE(k + 0);
- _state.i[12] = U8TO32_LITTLE(k + 4);
- _state.i[13] = U8TO32_LITTLE(k + 8);
- _state.i[14] = U8TO32_LITTLE(k + 12);
+ _state.i[11] = U8TO32_LITTLE(k + 16);
+ _state.i[12] = U8TO32_LITTLE(k + 20);
+ _state.i[13] = U8TO32_LITTLE(k + 24);
+ _state.i[14] = U8TO32_LITTLE(k + 28);
_state.i[15] = U8TO32_LITTLE(constants + 12);
- _state.i[0] = U8TO32_LITTLE(constants + 0);
#endif
}
-void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
- throw()
+void Salsa20::crypt12(const void *in,void *out,unsigned int bytes)
{
uint8_t tmp[64];
const uint8_t *m = (const uint8_t *)in;
@@ -623,8 +607,7 @@ void Salsa20::encrypt12(const void *in,void *out,unsigned int bytes)
}
}
-void Salsa20::encrypt20(const void *in,void *out,unsigned int bytes)
- throw()
+void Salsa20::crypt20(const void *in,void *out,unsigned int bytes)
{
uint8_t tmp[64];
const uint8_t *m = (const uint8_t *)in;
diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp
index 7e4c1e53..bfb6d9d9 100644
--- a/node/Salsa20.hpp
+++ b/node/Salsa20.hpp
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include "Constants.hpp"
#include "Utils.hpp"
@@ -30,76 +31,119 @@ namespace ZeroTier {
class Salsa20
{
public:
- Salsa20() throw() {}
-
+ Salsa20() {}
~Salsa20() { Utils::burn(&_state,sizeof(_state)); }
/**
- * @param key Key bits
- * @param kbits Number of key bits: 128 or 256 (recommended)
- * @param iv 64-bit initialization vector
+ * XOR d with s
+ *
+ * This is done efficiently using e.g. SSE if available. It's used when
+ * alternative Salsa20 implementations are used in Packet and is here
+ * since this is where all the SSE stuff is already included.
+ *
+ * @param d Destination to XOR
+ * @param s Source bytes to XOR with destination
+ * @param len Length of s and d
*/
- Salsa20(const void *key,unsigned int kbits,const void *iv)
- throw()
+ static inline void memxor(uint8_t *d,const uint8_t *s,unsigned int len)
{
- init(key,kbits,iv);
+#ifdef ZT_SALSA20_SSE
+ while (len >= 128) {
+ __m128i s0 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s));
+ __m128i s1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 16));
+ __m128i s2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 32));
+ __m128i s3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 48));
+ __m128i s4 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 64));
+ __m128i s5 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 80));
+ __m128i s6 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 96));
+ __m128i s7 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s + 112));
+ __m128i d0 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d));
+ __m128i d1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 16));
+ __m128i d2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 32));
+ __m128i d3 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 48));
+ __m128i d4 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 64));
+ __m128i d5 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 80));
+ __m128i d6 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 96));
+ __m128i d7 = _mm_loadu_si128(reinterpret_cast<__m128i *>(d + 112));
+ d0 = _mm_xor_si128(d0,s0);
+ d1 = _mm_xor_si128(d1,s1);
+ d2 = _mm_xor_si128(d2,s2);
+ d3 = _mm_xor_si128(d3,s3);
+ d4 = _mm_xor_si128(d4,s4);
+ d5 = _mm_xor_si128(d5,s5);
+ d6 = _mm_xor_si128(d6,s6);
+ d7 = _mm_xor_si128(d7,s7);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d),d0);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 16),d1);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 32),d2);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 48),d3);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 64),d4);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 80),d5);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 96),d6);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d + 112),d7);
+ s += 128;
+ d += 128;
+ len -= 128;
+ }
+ while (len >= 16) {
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(d),_mm_xor_si128(_mm_loadu_si128(reinterpret_cast<__m128i *>(d)),_mm_loadu_si128(reinterpret_cast<const __m128i *>(s))));
+ s += 16;
+ d += 16;
+ len -= 16;
+ }
+#else
+#ifndef ZT_NO_TYPE_PUNNING
+ while (len >= 16) {
+ (*reinterpret_cast<uint64_t *>(d)) ^= (*reinterpret_cast<const uint64_t *>(s));
+ s += 8;
+ d += 8;
+ (*reinterpret_cast<uint64_t *>(d)) ^= (*reinterpret_cast<const uint64_t *>(s));
+ s += 8;
+ d += 8;
+ len -= 16;
+ }
+#endif
+#endif
+ while (len) {
+ --len;
+ *(d++) ^= *(s++);
+ }
}
/**
- * Initialize cipher
- *
- * @param key Key bits
- * @param kbits Number of key bits: 128 or 256 (recommended)
+ * @param key 256-bit (32 byte) key
* @param iv 64-bit initialization vector
*/
- void init(const void *key,unsigned int kbits,const void *iv)
- throw();
-
- /**
- * Encrypt data using Salsa20/12
- *
- * @param in Input data
- * @param out Output buffer
- * @param bytes Length of data
- */
- void encrypt12(const void *in,void *out,unsigned int bytes)
- throw();
+ Salsa20(const void *key,const void *iv)
+ {
+ init(key,iv);
+ }
/**
- * Encrypt data using Salsa20/20
+ * Initialize cipher
*
- * @param in Input data
- * @param out Output buffer
- * @param bytes Length of data
+ * @param key Key bits
+ * @param iv 64-bit initialization vector
*/
- void encrypt20(const void *in,void *out,unsigned int bytes)
- throw();
+ void init(const void *key,const void *iv);
/**
- * Decrypt data
+ * Encrypt/decrypt data using Salsa20/12
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
- inline void decrypt12(const void *in,void *out,unsigned int bytes)
- throw()
- {
- encrypt12(in,out,bytes);
- }
+ void crypt12(const void *in,void *out,unsigned int bytes);
/**
- * Decrypt data
+ * Encrypt/decrypt data using Salsa20/20
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
- inline void decrypt20(const void *in,void *out,unsigned int bytes)
- throw()
- {
- encrypt20(in,out,bytes);
- }
+ void crypt20(const void *in,void *out,unsigned int bytes);
private:
union {
diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp
index 8bed0c51..c4f107fb 100644
--- a/node/SelfAwareness.cpp
+++ b/node/SelfAwareness.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -31,43 +39,38 @@
#include "Packet.hpp"
#include "Peer.hpp"
#include "Switch.hpp"
+#include "Trace.hpp"
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
-#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000
+#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
namespace ZeroTier {
class _ResetWithinScope
{
public:
- _ResetWithinScope(uint64_t now,InetAddress::IpScope scope) :
+ _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
_now(now),
+ _tPtr(tPtr),
+ _family(inetAddressFamily),
_scope(scope) {}
- inline void operator()(Topology &t,const SharedPtr<Peer> &p)
- {
- if (p->resetWithinScope(_scope,_now))
- peersReset.push_back(p);
- }
-
- std::vector< SharedPtr<Peer> > peersReset;
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); }
private:
uint64_t _now;
+ void *_tPtr;
+ int _family;
InetAddress::IpScope _scope;
};
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
RR(renv),
- _phy(32)
+ _phy(128)
{
}
-SelfAwareness::~SelfAwareness()
-{
-}
-
-void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
+void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
{
const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
@@ -75,13 +78,15 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
return;
Mutex::Lock _l(_phy_m);
- PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalAddress,reporterPhysicalAddress,scope)];
+ PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
// Changes to external surface reported by trusted peers causes path reset in this scope
+ RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,myPhysicalAddress,scope);
+
entry.mySurface = myPhysicalAddress;
entry.ts = now;
- TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
+ entry.trusted = trusted;
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
// due to multiple reports of endpoint change.
@@ -96,27 +101,18 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
}
}
- // Reset all paths within this scope
- _ResetWithinScope rset(now,(InetAddress::IpScope)scope);
+ // Reset all paths within this scope and address family
+ _ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
RR->topology->eachPeer<_ResetWithinScope &>(rset);
-
- // Send a NOP to all peers for whom we forgot a path. This will cause direct
- // links to be re-established if possible, possibly using a root server or some
- // other relay.
- for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) {
- if ((*p)->activelyTransferringFrames(now)) {
- Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP);
- RR->sw->send(outp,true,0);
- }
- }
} else {
// Otherwise just update DB to use to determine external surface info
entry.mySurface = myPhysicalAddress;
entry.ts = now;
+ entry.trusted = trusted;
}
}
-void SelfAwareness::clean(uint64_t now)
+void SelfAwareness::clean(int64_t now)
{
Mutex::Lock _l(_phy_m);
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
@@ -133,55 +129,82 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
/* This is based on ideas and strategies found here:
* https://tools.ietf.org/html/draft-takeda-symmetric-nat-traversal-00
*
- * In short: a great many symmetric NATs allocate ports sequentially.
- * This is common on enterprise and carrier grade NATs as well as consumer
- * devices. This code generates a list of "you might try this" addresses by
- * extrapolating likely port assignments from currently known external
- * global IPv4 surfaces. These can then be included in a PUSH_DIRECT_PATHS
- * message to another peer, causing it to possibly try these addresses and
- * bust our local symmetric NAT. It works often enough to be worth the
- * extra bit of code and does no harm in cases where it fails. */
-
- // Gather unique surfaces indexed by local received-on address and flag
- // us as behind a symmetric NAT if there is more than one.
- std::map< InetAddress,std::set<InetAddress> > surfaces;
- bool symmetric = false;
+ * For each IP address reported by a trusted (upstream) peer, we find
+ * the external port most recently reported by ANY peer for that IP.
+ *
+ * We only do any of this for global IPv4 addresses since private IPs
+ * and IPv6 are not going to have symmetric NAT.
+ *
+ * SECURITY NOTE:
+ *
+ * We never use IPs reported by non-trusted peers, since this could lead
+ * to a minor vulnerability whereby a peer could poison our cache with
+ * bad external surface reports via OK(HELLO) and then possibly coax us
+ * into suggesting their IP to other peers via PUSH_DIRECT_PATHS. This
+ * in turn could allow them to MITM flows.
+ *
+ * Since flows are encrypted and authenticated they could not actually
+ * read or modify traffic, but they could gather meta-data for forensics
+ * purpsoes or use this as a DOS attack vector. */
+
+ std::map< uint32_t,unsigned int > maxPortByIp;
+ InetAddress theOneTrueSurface;
{
Mutex::Lock _l(_phy_m);
- Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
- PhySurfaceKey *k = (PhySurfaceKey *)0;
- PhySurfaceEntry *e = (PhySurfaceEntry *)0;
- while (i.next(k,e)) {
- if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
- std::set<InetAddress> &s = surfaces[k->receivedOnLocalAddress];
- s.insert(e->mySurface);
- symmetric = symmetric||(s.size() > 1);
+
+ // First check to see if this is a symmetric NAT and enumerate external IPs learned from trusted peers
+ bool symmetric = false;
+ {
+ Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
+ PhySurfaceKey *k = (PhySurfaceKey *)0;
+ PhySurfaceEntry *e = (PhySurfaceEntry *)0;
+ while (i.next(k,e)) {
+ if ((e->trusted)&&(e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
+ if (!theOneTrueSurface)
+ theOneTrueSurface = e->mySurface;
+ else if (theOneTrueSurface != e->mySurface)
+ symmetric = true;
+ maxPortByIp[reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr] = e->mySurface.port();
+ }
}
}
- }
+ if (!symmetric)
+ return std::vector<InetAddress>();
- // If we appear to be symmetrically NATed, generate and return extrapolations
- // of those surfaces. Since PUSH_DIRECT_PATHS is sent multiple times, we
- // probabilistically generate extrapolations of anywhere from +1 to +5 to
- // increase the odds that it will work "eventually".
- if (symmetric) {
- std::vector<InetAddress> r;
- for(std::map< InetAddress,std::set<InetAddress> >::iterator si(surfaces.begin());si!=surfaces.end();++si) {
- for(std::set<InetAddress>::iterator i(si->second.begin());i!=si->second.end();++i) {
- InetAddress ipp(*i);
- unsigned int p = ipp.port() + 1 + ((unsigned int)RR->node->prng() & 3);
- if (p >= 65535)
- p -= 64510; // NATs seldom use ports <=1024 so wrap to 1025
- ipp.setPort(p);
- if ((si->second.count(ipp) == 0)&&(std::find(r.begin(),r.end(),ipp) == r.end())) {
- r.push_back(ipp);
+ { // Then find the highest issued port per IP
+ Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
+ PhySurfaceKey *k = (PhySurfaceKey *)0;
+ PhySurfaceEntry *e = (PhySurfaceEntry *)0;
+ while (i.next(k,e)) {
+ if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
+ const unsigned int port = e->mySurface.port();
+ std::map< uint32_t,unsigned int >::iterator mp(maxPortByIp.find(reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr));
+ if ((mp != maxPortByIp.end())&&(mp->second < port))
+ mp->second = port;
}
}
}
- return r;
}
- return std::vector<InetAddress>();
+ std::vector<InetAddress> r;
+
+ // Try next port up from max for each
+ for(std::map< uint32_t,unsigned int >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
+ unsigned int p = i->second + 1;
+ if (p > 65535) p -= 64511;
+ const InetAddress pred(&(i->first),4,p);
+ if (std::find(r.begin(),r.end(),pred) == r.end())
+ r.push_back(pred);
+ }
+
+ // Try a random port for each -- there are only 65535 so eventually it should work
+ for(std::map< uint32_t,unsigned int >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
+ const InetAddress pred(&(i->first),4,1024 + ((unsigned int)RR->node->prng() % 64511));
+ if (std::find(r.begin(),r.end(),pred) == r.end())
+ r.push_back(pred);
+ }
+
+ return r;
}
} // namespace ZeroTier
diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp
index 06c264a9..ce6e8c76 100644
--- a/node/SelfAwareness.hpp
+++ b/node/SelfAwareness.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SELFAWARENESS_HPP
@@ -36,7 +44,6 @@ class SelfAwareness
{
public:
SelfAwareness(const RuntimeEnvironment *renv);
- ~SelfAwareness();
/**
* Called when a trusted remote peer informs us of our external network address
@@ -48,14 +55,14 @@ public:
* @param trusted True if this peer is trusted as an authority to inform us of external address changes
* @param now Current time
*/
- void iam(const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now);
+ void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now);
/**
* Clean up database periodically
*
* @param now Current time
*/
- void clean(uint64_t now);
+ void clean(int64_t now);
/**
* If we appear to be behind a symmetric NAT, get predictions for possible external endpoints
@@ -68,23 +75,24 @@ private:
struct PhySurfaceKey
{
Address reporter;
- InetAddress receivedOnLocalAddress;
+ int64_t receivedOnLocalSocket;
InetAddress reporterPhysicalAddress;
InetAddress::IpScope scope;
PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
- PhySurfaceKey(const Address &r,const InetAddress &rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalAddress(rol),reporterPhysicalAddress(ra),scope(s) {}
+ PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
- inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
- inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(receivedOnLocalAddress == k.receivedOnLocalAddress)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
+ inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
+ inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
};
struct PhySurfaceEntry
{
InetAddress mySurface;
uint64_t ts;
+ bool trusted;
- PhySurfaceEntry() : mySurface(),ts(0) {}
- PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t) {}
+ PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
+ PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
};
const RuntimeEnvironment *RR;
diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp
index 3ff5ed18..2f0e50c9 100644
--- a/node/SharedPtr.hpp
+++ b/node/SharedPtr.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_SHAREDPTR_HPP
@@ -25,41 +33,19 @@
namespace ZeroTier {
/**
- * Simple reference counted pointer
+ * Simple zero-overhead introspective reference counted pointer
*
* This is an introspective shared pointer. Classes that need to be reference
* counted must list this as a 'friend' and must have a private instance of
- * AtomicCounter called __refCount. They should also have private destructors,
- * since only this class should delete them.
- *
- * Because this is introspective, it is safe to apply to a naked pointer
- * multiple times provided there is always at least one holding SharedPtr.
- *
- * Once C++11 is ubiquitous, this and a few other things like Thread might get
- * torn out for their standard equivalents.
+ * AtomicCounter called __refCount.
*/
template<typename T>
class SharedPtr
{
public:
- SharedPtr()
- throw() :
- _ptr((T *)0)
- {
- }
-
- SharedPtr(T *obj)
- throw() :
- _ptr(obj)
- {
- ++obj->__refCount;
- }
-
- SharedPtr(const SharedPtr &sp)
- throw() :
- _ptr(sp._getAndInc())
- {
- }
+ SharedPtr() : _ptr((T *)0) {}
+ SharedPtr(T *obj) : _ptr(obj) { ++obj->__refCount; }
+ SharedPtr(const SharedPtr &sp) : _ptr(sp._getAndInc()) {}
~SharedPtr()
{
@@ -90,8 +76,9 @@ public:
*
* @param ptr Naked pointer to assign
*/
- inline void setToUnsafe(T *ptr)
+ inline void set(T *ptr)
{
+ zero();
++ptr->__refCount;
_ptr = ptr;
}
@@ -102,50 +89,57 @@ public:
* @param with Pointer to swap with
*/
inline void swap(SharedPtr &with)
- throw()
{
T *tmp = _ptr;
_ptr = with._ptr;
with._ptr = tmp;
}
- inline operator bool() const throw() { return (_ptr != (T *)0); }
- inline T &operator*() const throw() { return *_ptr; }
- inline T *operator->() const throw() { return _ptr; }
+ inline operator bool() const { return (_ptr != (T *)0); }
+ inline T &operator*() const { return *_ptr; }
+ inline T *operator->() const { return _ptr; }
/**
* @return Raw pointer to held object
*/
- inline T *ptr() const throw() { return _ptr; }
+ inline T *ptr() const { return _ptr; }
/**
- * Set this pointer to null
+ * Set this pointer to NULL
*/
inline void zero()
{
if (_ptr) {
if (--_ptr->__refCount <= 0)
delete _ptr;
+ _ptr = (T *)0;
}
- _ptr = (T *)0;
}
- inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
- inline bool operator!=(const SharedPtr &sp) const throw() { return (_ptr != sp._ptr); }
- inline bool operator>(const SharedPtr &sp) const throw() { return (_ptr > sp._ptr); }
- inline bool operator<(const SharedPtr &sp) const throw() { return (_ptr < sp._ptr); }
- inline bool operator>=(const SharedPtr &sp) const throw() { return (_ptr >= sp._ptr); }
- inline bool operator<=(const SharedPtr &sp) const throw() { return (_ptr <= sp._ptr); }
+ /**
+ * @return Number of references according to this object's ref count or 0 if NULL
+ */
+ inline int references()
+ {
+ if (_ptr)
+ return _ptr->__refCount.load();
+ return 0;
+ }
+
+ inline bool operator==(const SharedPtr &sp) const { return (_ptr == sp._ptr); }
+ inline bool operator!=(const SharedPtr &sp) const { return (_ptr != sp._ptr); }
+ inline bool operator>(const SharedPtr &sp) const { return (_ptr > sp._ptr); }
+ inline bool operator<(const SharedPtr &sp) const { return (_ptr < sp._ptr); }
+ inline bool operator>=(const SharedPtr &sp) const { return (_ptr >= sp._ptr); }
+ inline bool operator<=(const SharedPtr &sp) const { return (_ptr <= sp._ptr); }
private:
inline T *_getAndInc() const
- throw()
{
if (_ptr)
++_ptr->__refCount;
return _ptr;
}
-
T *_ptr;
};
diff --git a/node/Switch.cpp b/node/Switch.cpp
index bf3afe33..eb1ebadb 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -35,43 +43,25 @@
#include "Peer.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
-#include "Cluster.hpp"
+#include "Trace.hpp"
namespace ZeroTier {
-#ifdef ZT_TRACE
-static const char *etherTypeName(const unsigned int etherType)
-{
- switch(etherType) {
- case ZT_ETHERTYPE_IPV4: return "IPV4";
- case ZT_ETHERTYPE_ARP: return "ARP";
- case ZT_ETHERTYPE_RARP: return "RARP";
- case ZT_ETHERTYPE_ATALK: return "ATALK";
- case ZT_ETHERTYPE_AARP: return "AARP";
- case ZT_ETHERTYPE_IPX_A: return "IPX_A";
- case ZT_ETHERTYPE_IPX_B: return "IPX_B";
- case ZT_ETHERTYPE_IPV6: return "IPV6";
- }
- return "UNKNOWN";
-}
-#endif // ZT_TRACE
-
Switch::Switch(const RuntimeEnvironment *renv) :
RR(renv),
_lastBeaconResponse(0),
- _outstandingWhoisRequests(32),
+ _lastCheckedQueues(0),
_lastUniteAttempt(8) // only really used on root servers and upstreams, and it'll grow there just fine
{
}
-Switch::~Switch()
-{
-}
-
-void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
+void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len)
{
try {
- const uint64_t now = RR->node->now();
+ const int64_t now = RR->node->now();
+
+ const SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
+ path->received(now);
if (len == 13) {
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
@@ -79,22 +69,22 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
* no longer send these, but we'll listen for them for a while to
* locate peers with versions <1.0.4. */
- Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
+ const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
if (beaconAddr == RR->identity.address())
return;
- if (!RR->node->shouldUsePathForZeroTierTraffic(localAddr,fromAddr))
+ if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localSocket,fromAddr))
return;
- SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
+ const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,beaconAddr));
if (peer) { // we'll only respond to beacons from known peers
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
_lastBeaconResponse = now;
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),true);
- RR->node->putPacket(localAddr,fromAddr,outp.data(),outp.size());
+ path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
- } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // min length check is important!
+ } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below!
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
// Handle fragment ----------------------------------------------------
@@ -102,28 +92,21 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
const Address destination(fragment.destination());
if (destination != RR->identity.address()) {
- // Fragment is not for us, so try to relay it
+ if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) )
+ return;
+
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
fragment.incrementHops();
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
// It wouldn't hurt anything, just redundant and unnecessary.
- SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
- if ((!relayTo)||(!relayTo->send(fragment.data(),fragment.size(),now))) {
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster) {
- RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
- return;
- }
-#endif
-
- // Don't know peer or no direct path -- so relay via root server
- relayTo = RR->topology->getBestRoot();
+ SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+ if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) {
+ // Don't know peer or no direct path -- so relay via someone upstream
+ relayTo = RR->topology->getUpstreamPeer();
if (relayTo)
- relayTo->send(fragment.data(),fragment.size(),now);
+ relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true);
}
- } else {
- TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
}
} else {
// Fragment looks like ours
@@ -137,12 +120,9 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
// Total fragments must be more than 1, otherwise why are we
// seeing a Packet::Fragment?
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *const rq = _findRXQueueEntry(now,fragmentPacketId);
-
- if ((!rq->timestamp)||(rq->packetId != fragmentPacketId)) {
+ RXQueueEntry *const rq = _findRXQueueEntry(fragmentPacketId);
+ if (rq->packetId != fragmentPacketId) {
// No packet found, so we received a fragment without its head.
- //TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
rq->timestamp = now;
rq->packetId = fragmentPacketId;
@@ -152,19 +132,17 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
rq->complete = false;
} else if (!(rq->haveFragments & (1 << fragmentNumber))) {
// We have other fragments and maybe the head, so add this one and check
- //TRACE("fragment (%u/%u) of %.16llx from %s",fragmentNumber + 1,totalFragments,fragmentPacketId,fromAddr.toString().c_str());
rq->frags[fragmentNumber - 1] = fragment;
rq->totalFragments = totalFragments;
if (Utils::countBits(rq->haveFragments |= (1 << fragmentNumber)) == totalFragments) {
// We have all fragments -- assemble and process full Packet
- //TRACE("packet %.16llx is complete, assembling and processing...",fragmentPacketId);
for(unsigned int f=1;f<totalFragments;++f)
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
- if (rq->frag0.tryDecode(RR,false)) {
+ if (rq->frag0.tryDecode(RR,tPtr)) {
rq->timestamp = 0; // packet decoded, free entry
} else {
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
@@ -178,77 +156,59 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { // min length check is important!
// Handle packet head -------------------------------------------------
- // See packet format in Packet.hpp to understand this
- const uint64_t packetId = (
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[2]) << 40) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[3]) << 32) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[4]) << 24) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[5]) << 16) |
- (((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
- ((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
- );
const Address destination(reinterpret_cast<const uint8_t *>(data) + 8,ZT_ADDRESS_LENGTH);
const Address source(reinterpret_cast<const uint8_t *>(data) + 13,ZT_ADDRESS_LENGTH);
- // Catch this and toss it -- it would never work, but it could happen if we somehow
- // mistakenly guessed an address we're bound to as a destination for another peer.
if (source == RR->identity.address())
return;
- //TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
-
if (destination != RR->identity.address()) {
+ if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
+ return;
+
Packet packet(data,len);
- // Packet is not for us, so try to relay it
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
packet.incrementHops();
-
- SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
- if ((relayTo)&&((relayTo->send(packet.data(),packet.size(),now)))) {
- Mutex::Lock _l(_lastUniteAttempt_m);
- uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
- if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
- luts = now;
- unite(source,destination);
+ SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+ if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) {
+ if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) {
+ const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+ if (sourcePeer)
+ relayTo->introduce(tPtr,now,sourcePeer);
}
} else {
-#ifdef ZT_ENABLE_CLUSTER
- if (RR->cluster) {
- bool shouldUnite;
- {
- Mutex::Lock _l(_lastUniteAttempt_m);
- uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
- shouldUnite = ((now - luts) >= ZT_MIN_UNITE_INTERVAL);
- if (shouldUnite)
- luts = now;
+ relayTo = RR->topology->getUpstreamPeer();
+ if ((relayTo)&&(relayTo->address() != source)) {
+ if (relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true)) {
+ const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+ if (sourcePeer)
+ relayTo->introduce(tPtr,now,sourcePeer);
}
- RR->cluster->sendViaCluster(source,destination,packet.data(),packet.size(),shouldUnite);
- return;
}
-#endif
- relayTo = RR->topology->getBestRoot(&source,1,true);
- if (relayTo)
- relayTo->send(packet.data(),packet.size(),now);
}
- } else {
- TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
}
} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
// Packet is the head of a fragmented packet series
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *const rq = _findRXQueueEntry(now,packetId);
-
- if ((!rq->timestamp)||(rq->packetId != packetId)) {
+ const uint64_t packetId = (
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[0]) << 56) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[1]) << 48) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[2]) << 40) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[3]) << 32) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[4]) << 24) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[5]) << 16) |
+ (((uint64_t)reinterpret_cast<const uint8_t *>(data)[6]) << 8) |
+ ((uint64_t)reinterpret_cast<const uint8_t *>(data)[7])
+ );
+
+ RXQueueEntry *const rq = _findRXQueueEntry(packetId);
+ if (rq->packetId != packetId) {
// If we have no other fragments yet, create an entry and save the head
- //TRACE("fragment (0/?) of %.16llx from %s",pid,fromAddr.toString().c_str());
rq->timestamp = now;
rq->packetId = packetId;
- rq->frag0.init(data,len,localAddr,fromAddr,now);
+ rq->frag0.init(data,len,path,now);
rq->totalFragments = 0;
rq->haveFragments = 1;
rq->complete = false;
@@ -257,36 +217,28 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) {
// We have all fragments -- assemble and process full Packet
- //TRACE("packet %.16llx is complete, assembling and processing...",pid);
- rq->frag0.init(data,len,localAddr,fromAddr,now);
+ rq->frag0.init(data,len,path,now);
for(unsigned int f=1;f<rq->totalFragments;++f)
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
- if (rq->frag0.tryDecode(RR,false)) {
+ if (rq->frag0.tryDecode(RR,tPtr)) {
rq->timestamp = 0; // packet decoded, free entry
} else {
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
}
} else {
// Still waiting on more fragments, but keep the head
- rq->frag0.init(data,len,localAddr,fromAddr,now);
+ rq->frag0.init(data,len,path,now);
}
} // else this is a duplicate head, ignore
} else {
// Packet is unfragmented, so just process it
- IncomingPacket packet(data,len,localAddr,fromAddr,now);
- if (!packet.tryDecode(RR,false)) {
- Mutex::Lock _l(_rxQueue_m);
- RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
- unsigned long i = ZT_RX_QUEUE_SIZE - 1;
- while ((i)&&(rq->timestamp)) {
- RXQueueEntry *tmp = &(_rxQueue[--i]);
- if (tmp->timestamp < rq->timestamp)
- rq = tmp;
- }
+ IncomingPacket packet(data,len,path,now);
+ if (!packet.tryDecode(RR,tPtr)) {
+ RXQueueEntry *const rq = _nextRXQueueEntry();
rq->timestamp = now;
- rq->packetId = packetId;
+ rq->packetId = packet.packetId();
rq->frag0 = packet;
rq->totalFragments = 1;
rq->haveFragments = 1;
@@ -297,41 +249,25 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
// --------------------------------------------------------------------
}
}
- } catch (std::exception &ex) {
- TRACE("dropped packet from %s: unexpected exception: %s",fromAddr.toString().c_str(),ex.what());
- } catch ( ... ) {
- TRACE("dropped packet from %s: unexpected exception: (unknown)",fromAddr.toString().c_str());
- }
+ } catch ( ... ) {} // sanity check, should be caught elsewhere
}
-void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
if (!network->hasConfig())
return;
- // Sanity check -- bridge loop? OS problem?
- if (to == network->mac())
- return;
-
- // Check to make sure this protocol is allowed on this network
- if (!network->config().permitsEtherType(etherType)) {
- TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
- return;
- }
-
// Check if this packet is from someone other than the tap -- i.e. bridged in
- bool fromBridged = false;
- if (from != network->mac()) {
+ bool fromBridged;
+ if ((fromBridged = (from != network->mac()))) {
if (!network->config().permitsBridging(RR->identity.address())) {
- TRACE("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"not a bridge");
return;
}
- fromBridged = true;
}
if (to.isMulticast()) {
- // Destination is a multicast address (including broadcast)
- MulticastGroup mg(to,0);
+ MulticastGroup multicastGroup(to,0);
if (to.isBroadcast()) {
if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
@@ -344,10 +280,10 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
* them into multicasts by stuffing the IP address being queried into
* the 32-bit ADI field. In practice this uses our multicast pub/sub
* system to implement a kind of extended/distributed ARP table. */
- mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
+ multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
} else if (!network->config().enableBroadcast()) {
// Don't transmit broadcasts if this network doesn't want them
- TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"broadcast disabled");
return;
}
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
@@ -400,7 +336,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
const MAC peerMac(v6EmbeddedAddress,network->id());
- TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
uint8_t adv[72];
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
@@ -428,74 +363,87 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
adv[42] = (checksum >> 8) & 0xff;
adv[43] = checksum & 0xff;
- RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
+ RR->node->putFrame(tPtr,network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query.
} // else no NDP emulation
} // else no NDP emulation
}
+ // Check this after NDP emulation, since that has to be allowed in exactly this case
+ if (network->config().multicastLimit == 0) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"multicast disabled");
+ return;
+ }
+
/* Learn multicast groups for bridged-in hosts.
* Note that some OSes, most notably Linux, do this for you by learning
* multicast addresses on bridge interfaces and subscribing each slave.
* But in that case this does no harm, as the sets are just merged. */
if (fromBridged)
- network->learnBridgedMulticastGroup(mg,RR->node->now());
+ network->learnBridgedMulticastGroup(tPtr,multicastGroup,RR->node->now());
- //TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len);
+ // First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
+ if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+ return;
+ }
RR->mc->send(
- ((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0,
- network->config().multicastLimit,
+ tPtr,
RR->node->now(),
- network->id(),
- network->config().activeBridges(),
- mg,
+ network,
+ Address(),
+ multicastGroup,
(fromBridged) ? from : MAC(),
etherType,
data,
len);
-
- return;
- }
-
- if (to[0] == MAC::firstOctetForNetwork(network->id())) {
+ } else if (to == network->mac()) {
+ // Destination is this node, so just reinject it
+ RR->node->putFrame(tPtr,network->id(),network->userPtr(),from,to,etherType,vlanId,data,len);
+ } else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
// Destination is another ZeroTier peer on the same network
Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
- SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
- const bool includeCom = ( (network->config().isPrivate()) && (network->config().com) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) );
- if ((fromBridged)||(includeCom)) {
+ SharedPtr<Peer> toPeer(RR->topology->getPeer(tPtr,toZT));
+
+ if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+ return;
+ }
+
+ if (fromBridged) {
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
outp.append(network->id());
- if (includeCom) {
- outp.append((unsigned char)0x01); // 0x01 -- COM included
- network->config().com.serialize(outp);
- } else {
- outp.append((unsigned char)0x00);
- }
+ outp.append((unsigned char)0x00);
to.appendTo(outp);
from.appendTo(outp);
outp.append((uint16_t)etherType);
outp.append(data,len);
- outp.compress();
- send(outp,true,network->id());
+ if (!network->config().disableCompression())
+ outp.compress();
+ send(tPtr,outp,true);
} else {
Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
outp.append(network->id());
outp.append((uint16_t)etherType);
outp.append(data,len);
- outp.compress();
- send(outp,true,network->id());
+ if (!network->config().disableCompression())
+ outp.compress();
+ send(tPtr,outp,true);
}
- //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
-
- return;
- }
-
- {
+ } else {
// Destination is bridged behind a remote peer
+ // We filter with a NULL destination ZeroTier address first. Filtrations
+ // for each ZT destination are also done below. This is the same rationale
+ // and design as for multicast.
+ if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
+ return;
+ }
+
Address bridges[ZT_MAX_BRIDGE_SPAM];
unsigned int numBridges = 0;
@@ -529,245 +477,132 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
}
for(unsigned int b=0;b<numBridges;++b) {
- SharedPtr<Peer> bridgePeer(RR->topology->getPeer(bridges[b]));
- Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
- outp.append(network->id());
- if ( (network->config().isPrivate()) && (network->config().com) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) {
- outp.append((unsigned char)0x01); // 0x01 -- COM included
- network->config().com.serialize(outp);
+ if (network->filterOutgoingPacket(tPtr,true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+ Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
+ outp.append(network->id());
+ outp.append((uint8_t)0x00);
+ to.appendTo(outp);
+ from.appendTo(outp);
+ outp.append((uint16_t)etherType);
+ outp.append(data,len);
+ if (!network->config().disableCompression())
+ outp.compress();
+ send(tPtr,outp,true);
} else {
- outp.append((unsigned char)0);
+ RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked (bridge replication)");
}
- to.appendTo(outp);
- from.appendTo(outp);
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- outp.compress();
- send(outp,true,network->id());
}
}
}
-void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid)
+void Switch::send(void *tPtr,Packet &packet,bool encrypt)
{
- if (packet.destination() == RR->identity.address()) {
- TRACE("BUG: caught attempt to send() to self, ignored");
+ const Address dest(packet.destination());
+ if (dest == RR->identity.address())
return;
- }
-
- //TRACE(">> %s to %s (%u bytes, encrypt==%d, nwid==%.16llx)",Packet::verbString(packet.verb()),packet.destination().toString().c_str(),packet.size(),(int)encrypt,nwid);
-
- if (!_trySend(packet,encrypt,nwid)) {
- Mutex::Lock _l(_txQueue_m);
- _txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt,nwid));
- }
-}
-
-bool Switch::unite(const Address &p1,const Address &p2)
-{
- if ((p1 == RR->identity.address())||(p2 == RR->identity.address()))
- return false;
- SharedPtr<Peer> p1p = RR->topology->getPeer(p1);
- if (!p1p)
- return false;
- SharedPtr<Peer> p2p = RR->topology->getPeer(p2);
- if (!p2p)
- return false;
-
- const uint64_t now = RR->node->now();
-
- std::pair<InetAddress,InetAddress> cg(Peer::findCommonGround(*p1p,*p2p,now));
- if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope()))
- return false;
-
- TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),cg.second.toString().c_str(),p2.toString().c_str(),cg.first.toString().c_str());
-
- /* Tell P1 where to find P2 and vice versa, sending the packets to P1 and
- * P2 in randomized order in terms of which gets sent first. This is done
- * since in a few cases NAT-t can be sensitive to slight timing differences
- * in terms of when the two peers initiate. Normally this is accounted for
- * by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but
- * given that relay are hosted on cloud providers this can in some
- * cases have a few ms of latency between packet departures. By randomizing
- * the order we make each attempted NAT-t favor one or the other going
- * first, meaning if it doesn't succeed the first time it might the second
- * and so forth. */
- unsigned int alt = (unsigned int)RR->node->prng() & 1;
- unsigned int completed = alt + 2;
- while (alt != completed) {
- if ((alt & 1) == 0) {
- // Tell p1 where to find p2.
- Packet outp(p1,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((unsigned char)0);
- p2.appendTo(outp);
- outp.append((uint16_t)cg.first.port());
- if (cg.first.isV6()) {
- outp.append((unsigned char)16);
- outp.append(cg.first.rawIpData(),16);
- } else {
- outp.append((unsigned char)4);
- outp.append(cg.first.rawIpData(),4);
- }
- outp.armor(p1p->key(),true);
- p1p->send(outp.data(),outp.size(),now);
- } else {
- // Tell p2 where to find p1.
- Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((unsigned char)0);
- p1.appendTo(outp);
- outp.append((uint16_t)cg.second.port());
- if (cg.second.isV6()) {
- outp.append((unsigned char)16);
- outp.append(cg.second.rawIpData(),16);
- } else {
- outp.append((unsigned char)4);
- outp.append(cg.second.rawIpData(),4);
- }
- outp.armor(p2p->key(),true);
- p2p->send(outp.data(),outp.size(),now);
+ if (!_trySend(tPtr,packet,encrypt)) {
+ {
+ Mutex::Lock _l(_txQueue_m);
+ _txQueue.push_back(TXQueueEntry(dest,RR->node->now(),packet,encrypt));
}
- ++alt; // counts up and also flips LSB
+ if (!RR->topology->getPeer(tPtr,dest))
+ requestWhois(tPtr,RR->node->now(),dest);
}
-
- return true;
}
-void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr)
+void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr)
{
- TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
- const uint64_t now = RR->node->now();
- peer->sendHELLO(localAddr,atAddr,now,2); // first attempt: send low-TTL packet to 'open' local NAT
+ if (addr == RR->identity.address())
+ return;
+
{
- Mutex::Lock _l(_contactQueue_m);
- _contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,localAddr,atAddr));
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ int64_t &last = _lastSentWhoisRequest[addr];
+ if ((now - last) < ZT_WHOIS_RETRY_DELAY)
+ return;
+ else last = now;
}
-}
-void Switch::requestWhois(const Address &addr)
-{
- bool inserted = false;
- {
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- WhoisRequest &r = _outstandingWhoisRequests[addr];
- if (r.lastSent) {
- r.retries = 0; // reset retry count if entry already existed, but keep waiting and retry again after normal timeout
- } else {
- r.lastSent = RR->node->now();
- inserted = true;
- }
+ const SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer());
+ if (upstream) {
+ Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
+ addr.appendTo(outp);
+ RR->node->expectReplyTo(outp.packetId());
+ send(tPtr,outp,true);
}
- if (inserted)
- _sendWhoisRequest(addr,(const Address *)0,0);
}
-void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
+void Switch::doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer)
{
- { // cancel pending WHOIS since we now know this peer
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- _outstandingWhoisRequests.erase(peer->address());
+ {
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ _lastSentWhoisRequest.erase(peer->address());
}
- { // finish processing any packets waiting on peer's public key / identity
- Mutex::Lock _l(_rxQueue_m);
- unsigned long i = ZT_RX_QUEUE_SIZE;
- while (i) {
- RXQueueEntry *rq = &(_rxQueue[--i]);
- if ((rq->timestamp)&&(rq->complete)) {
- if (rq->frag0.tryDecode(RR,false))
- rq->timestamp = 0;
- }
+ const int64_t now = RR->node->now();
+ for(unsigned int ptr=0;ptr<ZT_RX_QUEUE_SIZE;++ptr) {
+ RXQueueEntry *const rq = &(_rxQueue[ptr]);
+ if ((rq->timestamp)&&(rq->complete)) {
+ if ((rq->frag0.tryDecode(RR,tPtr))||((now - rq->timestamp) > ZT_RECEIVE_QUEUE_TIMEOUT))
+ rq->timestamp = 0;
}
}
- { // finish sending any packets waiting on peer's public key / identity
+ {
Mutex::Lock _l(_txQueue_m);
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
if (txi->dest == peer->address()) {
- if (_trySend(txi->packet,txi->encrypt,txi->nwid))
+ if (_trySend(tPtr,txi->packet,txi->encrypt)) {
_txQueue.erase(txi++);
- else ++txi;
- } else ++txi;
- }
- }
-}
-
-unsigned long Switch::doTimerTasks(uint64_t now)
-{
- unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
-
- { // Iterate through NAT traversal strategies for entries in contact queue
- Mutex::Lock _l(_contactQueue_m);
- for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
- if (now >= qi->fireAtTime) {
- if (!qi->peer->pushDirectPaths(qi->localAddr,qi->inaddr,now,true,false))
- qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
- _contactQueue.erase(qi++);
- continue;
- /* Old symmetric NAT buster code, obsoleted by port prediction alg in SelfAwareness but left around for now in case we revert
- if (qi->strategyIteration == 0) {
- // First strategy: send packet directly to destination
- qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
- } else if (qi->strategyIteration <= 3) {
- // Strategies 1-3: try escalating ports for symmetric NATs that remap sequentially
- InetAddress tmpaddr(qi->inaddr);
- int p = (int)qi->inaddr.port() + qi->strategyIteration;
- if (p > 65535)
- p -= 64511;
- tmpaddr.setPort((unsigned int)p);
- qi->peer->sendHELLO(qi->localAddr,tmpaddr,now);
} else {
- // All strategies tried, expire entry
- _contactQueue.erase(qi++);
- continue;
+ ++txi;
}
- ++qi->strategyIteration;
- qi->fireAtTime = now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY;
- nextDelay = std::min(nextDelay,(unsigned long)ZT_NAT_T_TACTICAL_ESCALATION_DELAY);
- */
} else {
- nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
+ ++txi;
}
- ++qi; // if qi was erased, loop will have continued before here
}
}
+}
- { // Retry outstanding WHOIS requests
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
- Address *a = (Address *)0;
- WhoisRequest *r = (WhoisRequest *)0;
- while (i.next(a,r)) {
- const unsigned long since = (unsigned long)(now - r->lastSent);
- if (since >= ZT_WHOIS_RETRY_DELAY) {
- if (r->retries >= ZT_MAX_WHOIS_RETRIES) {
- TRACE("WHOIS %s timed out",a->toString().c_str());
- _outstandingWhoisRequests.erase(*a);
- } else {
- r->lastSent = now;
- r->peersConsulted[r->retries] = _sendWhoisRequest(*a,r->peersConsulted,r->retries);
- ++r->retries;
- TRACE("WHOIS %s (retry %u)",a->toString().c_str(),r->retries);
- nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
- }
- } else {
- nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
- }
- }
- }
+unsigned long Switch::doTimerTasks(void *tPtr,int64_t now)
+{
+ const uint64_t timeSinceLastCheck = now - _lastCheckedQueues;
+ if (timeSinceLastCheck < ZT_WHOIS_RETRY_DELAY)
+ return (unsigned long)(ZT_WHOIS_RETRY_DELAY - timeSinceLastCheck);
+ _lastCheckedQueues = now;
- { // Time out TX queue packets that never got WHOIS lookups or other info.
+ std::vector<Address> needWhois;
+ {
Mutex::Lock _l(_txQueue_m);
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
- if (_trySend(txi->packet,txi->encrypt,txi->nwid))
+ if (_trySend(tPtr,txi->packet,txi->encrypt)) {
_txQueue.erase(txi++);
- else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
- TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
+ } else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
_txQueue.erase(txi++);
- } else ++txi;
+ } else {
+ if (!RR->topology->getPeer(tPtr,txi->dest))
+ needWhois.push_back(txi->dest);
+ ++txi;
+ }
+ }
+ }
+ for(std::vector<Address>::const_iterator i(needWhois.begin());i!=needWhois.end();++i)
+ requestWhois(tPtr,now,*i);
+
+ for(unsigned int ptr=0;ptr<ZT_RX_QUEUE_SIZE;++ptr) {
+ RXQueueEntry *const rq = &(_rxQueue[ptr]);
+ if ((rq->timestamp)&&(rq->complete)) {
+ if ((rq->frag0.tryDecode(RR,tPtr))||((now - rq->timestamp) > ZT_RECEIVE_QUEUE_TIMEOUT)) {
+ rq->timestamp = 0;
+ } else {
+ const Address src(rq->frag0.source());
+ if (!RR->topology->getPeer(tPtr,src))
+ requestWhois(tPtr,now,src);
+ }
}
}
- { // Remove really old last unite attempt entries to keep table size controlled
+ {
Mutex::Lock _l(_lastUniteAttempt_m);
Hashtable< _LastUniteKey,uint64_t >::Iterator i(_lastUniteAttempt);
_LastUniteKey *k = (_LastUniteKey *)0;
@@ -778,109 +613,86 @@ unsigned long Switch::doTimerTasks(uint64_t now)
}
}
- return nextDelay;
+ {
+ Mutex::Lock _l(_lastSentWhoisRequest_m);
+ Hashtable< Address,int64_t >::Iterator i(_lastSentWhoisRequest);
+ Address *a = (Address *)0;
+ int64_t *ts = (int64_t *)0;
+ while (i.next(a,ts)) {
+ if ((now - *ts) > (ZT_WHOIS_RETRY_DELAY * 2))
+ _lastSentWhoisRequest.erase(*a);
+ }
+ }
+
+ return ZT_WHOIS_RETRY_DELAY;
}
-Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
+bool Switch::_shouldUnite(const int64_t now,const Address &source,const Address &destination)
{
- SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
- if (root) {
- Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
- addr.appendTo(outp);
- outp.armor(root->key(),true);
- if (root->send(outp.data(),outp.size(),RR->node->now()))
- return root->address();
+ Mutex::Lock _l(_lastUniteAttempt_m);
+ uint64_t &ts = _lastUniteAttempt[_LastUniteKey(source,destination)];
+ if ((now - ts) >= ZT_MIN_UNITE_INTERVAL) {
+ ts = now;
+ return true;
}
- return Address();
+ return false;
}
-bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
+bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
{
- SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
+ SharedPtr<Path> viaPath;
+ const int64_t now = RR->node->now();
+ const Address destination(packet.destination());
+ const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,destination));
if (peer) {
- const uint64_t now = RR->node->now();
-
- SharedPtr<Network> network;
- if (nwid) {
- network = RR->node->network(nwid);
- if ((!network)||(!network->hasConfig()))
- return false; // we probably just left this network, let its packets die
- }
-
- Path *viaPath = peer->getBestPath(now);
- SharedPtr<Peer> relay;
-
+ viaPath = peer->getBestPath(now,false);
if (!viaPath) {
- if (network) {
- unsigned int bestq = ~((unsigned int)0); // max unsigned int since quality is lower==better
- unsigned int ptr = 0;
- for(;;) {
- const Address raddr(network->config().nextRelay(ptr));
- if (raddr) {
- SharedPtr<Peer> rp(RR->topology->getPeer(raddr));
- if (rp) {
- const unsigned int q = rp->relayQuality(now);
- if (q < bestq) {
- bestq = q;
- rp.swap(relay);
- }
- }
- } else break;
- }
+ peer->tryMemorizedPath(tPtr,now); // periodically attempt memorized or statically defined paths, if any are known
+ const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
+ if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
+ if (!(viaPath = peer->getBestPath(now,true)))
+ return false;
}
-
- if (!relay)
- relay = RR->topology->getBestRoot();
-
- if ( (!relay) || (!(viaPath = relay->getBestPath(now))) )
- return false;
- }
- // viaPath will not be null if we make it here
-
- // Push possible direct paths to us if we are relaying
- if (relay) {
- peer->pushDirectPaths(viaPath->localAddress(),viaPath->address(),now,false,( (network)&&(network->isAllowed(peer)) ));
- viaPath->sent(now);
}
+ } else {
+ return false;
+ }
- Packet tmp(packet);
+ unsigned int mtu = ZT_DEFAULT_PHYSMTU;
+ uint64_t trustedPathId = 0;
+ RR->topology->getOutboundPathInfo(viaPath->address(),mtu,trustedPathId);
- unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
- tmp.setFragmented(chunkSize < tmp.size());
+ unsigned int chunkSize = std::min(packet.size(),mtu);
+ packet.setFragmented(chunkSize < packet.size());
- const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
- if (trustedPathId) {
- tmp.setTrusted(trustedPathId);
- } else {
- tmp.armor(peer->key(),encrypt);
- }
+ if (trustedPathId) {
+ packet.setTrusted(trustedPathId);
+ } else {
+ packet.armor(peer->key(),encrypt);
+ }
- if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
- if (chunkSize < tmp.size()) {
- // Too big for one packet, fragment the rest
- unsigned int fragStart = chunkSize;
- unsigned int remaining = tmp.size() - chunkSize;
- unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
- if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
- ++fragsRemaining;
- unsigned int totalFragments = fragsRemaining + 1;
-
- for(unsigned int fno=1;fno<totalFragments;++fno) {
- chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
- Packet::Fragment frag(tmp,fragStart,chunkSize,fno,totalFragments);
- viaPath->send(RR,frag.data(),frag.size(),now);
- fragStart += chunkSize;
- remaining -= chunkSize;
- }
+ if (viaPath->send(RR,tPtr,packet.data(),chunkSize,now)) {
+ if (chunkSize < packet.size()) {
+ // Too big for one packet, fragment the rest
+ unsigned int fragStart = chunkSize;
+ unsigned int remaining = packet.size() - chunkSize;
+ unsigned int fragsRemaining = (remaining / (mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ if ((fragsRemaining * (mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
+ ++fragsRemaining;
+ const unsigned int totalFragments = fragsRemaining + 1;
+
+ for(unsigned int fno=1;fno<totalFragments;++fno) {
+ chunkSize = std::min(remaining,(unsigned int)(mtu - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ Packet::Fragment frag(packet,fragStart,chunkSize,fno,totalFragments);
+ viaPath->send(RR,tPtr,frag.data(),frag.size(),now);
+ fragStart += chunkSize;
+ remaining -= chunkSize;
}
-
- return true;
}
- } else {
- requestWhois(packet.destination());
}
- return false;
+
+ return true;
}
} // namespace ZeroTier
diff --git a/node/Switch.hpp b/node/Switch.hpp
index ce4f00a1..906f418e 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_N_SWITCH_HPP
@@ -27,12 +35,10 @@
#include "Constants.hpp"
#include "Mutex.hpp"
#include "MAC.hpp"
-#include "NonCopyable.hpp"
#include "Packet.hpp"
#include "Utils.hpp"
#include "InetAddress.hpp"
#include "Topology.hpp"
-#include "Array.hpp"
#include "Network.hpp"
#include "SharedPtr.hpp"
#include "IncomingPacket.hpp"
@@ -51,25 +57,26 @@ class Peer;
* packets from tap devices, and this sends them where they need to go and
* wraps/unwraps accordingly. It also handles queues and timeouts and such.
*/
-class Switch : NonCopyable
+class Switch
{
public:
Switch(const RuntimeEnvironment *renv);
- ~Switch();
/**
* Called when a packet is received from the real network
*
- * @param localAddr Local interface address
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param localSocket Local I/O socket as supplied by external code
* @param fromAddr Internet IP address of origin
* @param data Packet data
* @param len Packet length
*/
- void onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
+ void onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len);
/**
* Called when a packet comes from a local Ethernet tap
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param network Which network's TAP did this packet come from?
* @param from Originating MAC address
* @param to Destination MAC address
@@ -78,7 +85,7 @@ public:
* @param data Ethernet payload
* @param len Frame length
*/
- void onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
+ void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
/**
* Send a packet to a ZeroTier address (destination in packet)
@@ -92,51 +99,30 @@ public:
* Needless to say, the packet's source must be this node. Otherwise it
* won't be encrypted right. (This is not used for relaying.)
*
- * The network ID should only be specified for frames and other actual
- * network traffic. Other traffic such as controller requests and regular
- * protocol messages should specify zero.
- *
- * @param packet Packet to send
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param packet Packet to send (buffer may be modified)
* @param encrypt Encrypt packet payload? (always true except for HELLO)
- * @param nwid Related network ID or 0 if message is not in-network traffic
- */
- void send(const Packet &packet,bool encrypt,uint64_t nwid);
-
- /**
- * Send RENDEZVOUS to two peers to permit them to directly connect
- *
- * This only works if both peers are known, with known working direct
- * links to this peer. The best link for each peer is sent to the other.
- *
- * @param p1 One of two peers (order doesn't matter)
- * @param p2 Second of pair
- */
- bool unite(const Address &p1,const Address &p2);
-
- /**
- * Attempt NAT traversal to peer at a given physical address
- *
- * @param peer Peer to contact
- * @param localAddr Local interface address
- * @param atAddr Address of peer
*/
- void rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr);
+ void send(void *tPtr,Packet &packet,bool encrypt);
/**
* Request WHOIS on a given address
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param now Current time
* @param addr Address to look up
*/
- void requestWhois(const Address &addr);
+ void requestWhois(void *tPtr,const int64_t now,const Address &addr);
/**
* Run any processes that are waiting for this peer's identity
*
* Called when we learn of a peer's identity from HELLO, OK(WHOIS), etc.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param peer New peer
*/
- void doAnythingWaitingForPeer(const SharedPtr<Peer> &peer);
+ void doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer);
/**
* Perform retries and other periodic timer tasks
@@ -144,77 +130,70 @@ public:
* This can return a very long delay if there are no pending timer
* tasks. The caller should cap this comparatively vs. other values.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param now Current time
* @return Number of milliseconds until doTimerTasks() should be run again
*/
- unsigned long doTimerTasks(uint64_t now);
+ unsigned long doTimerTasks(void *tPtr,int64_t now);
private:
- Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
- bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
+ bool _shouldUnite(const int64_t now,const Address &source,const Address &destination);
+ bool _trySend(void *tPtr,Packet &packet,bool encrypt); // packet is modified if return is true
const RuntimeEnvironment *const RR;
- uint64_t _lastBeaconResponse;
+ int64_t _lastBeaconResponse;
+ volatile int64_t _lastCheckedQueues;
- // Outstanding WHOIS requests and how many retries they've undergone
- struct WhoisRequest
- {
- WhoisRequest() : lastSent(0),retries(0) {}
- uint64_t lastSent;
- Address peersConsulted[ZT_MAX_WHOIS_RETRIES]; // by retry
- unsigned int retries; // 0..ZT_MAX_WHOIS_RETRIES
- };
- Hashtable< Address,WhoisRequest > _outstandingWhoisRequests;
- Mutex _outstandingWhoisRequests_m;
+ // Time we last sent a WHOIS request for each address
+ Hashtable< Address,int64_t > _lastSentWhoisRequest;
+ Mutex _lastSentWhoisRequest_m;
// Packets waiting for WHOIS replies or other decode info or missing fragments
struct RXQueueEntry
{
RXQueueEntry() : timestamp(0) {}
- uint64_t timestamp; // 0 if entry is not in use
- uint64_t packetId;
+ volatile int64_t timestamp; // 0 if entry is not in use
+ volatile uint64_t packetId;
IncomingPacket frag0; // head of packet
Packet::Fragment frags[ZT_MAX_PACKET_FRAGMENTS - 1]; // later fragments (if any)
unsigned int totalFragments; // 0 if only frag0 received, waiting for frags
uint32_t haveFragments; // bit mask, LSB to MSB
- bool complete; // if true, packet is complete
+ volatile bool complete; // if true, packet is complete
};
RXQueueEntry _rxQueue[ZT_RX_QUEUE_SIZE];
- Mutex _rxQueue_m;
+ AtomicCounter _rxQueuePtr;
- /* Returns the matching or oldest entry. Caller must check timestamp and
- * packet ID to determine which. */
- inline RXQueueEntry *_findRXQueueEntry(uint64_t now,uint64_t packetId)
+ // Returns matching or next available RX queue entry
+ inline RXQueueEntry *_findRXQueueEntry(uint64_t packetId)
{
- RXQueueEntry *rq;
- RXQueueEntry *oldest = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
- unsigned long i = ZT_RX_QUEUE_SIZE;
- while (i) {
- rq = &(_rxQueue[--i]);
+ const unsigned int current = static_cast<unsigned int>(_rxQueuePtr.load());
+ for(unsigned int k=1;k<=ZT_RX_QUEUE_SIZE;++k) {
+ RXQueueEntry *rq = &(_rxQueue[(current - k) % ZT_RX_QUEUE_SIZE]);
if ((rq->packetId == packetId)&&(rq->timestamp))
return rq;
- if ((now - rq->timestamp) >= ZT_RX_QUEUE_EXPIRE)
- rq->timestamp = 0;
- if (rq->timestamp < oldest->timestamp)
- oldest = rq;
}
- return oldest;
+ ++_rxQueuePtr;
+ return &(_rxQueue[static_cast<unsigned int>(current) % ZT_RX_QUEUE_SIZE]);
+ }
+
+ // Returns current entry in rx queue ring buffer and increments ring pointer
+ inline RXQueueEntry *_nextRXQueueEntry()
+ {
+ return &(_rxQueue[static_cast<unsigned int>((++_rxQueuePtr) - 1) % ZT_RX_QUEUE_SIZE]);
}
// ZeroTier-layer TX queue entry
struct TXQueueEntry
{
TXQueueEntry() {}
- TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc,uint64_t nw) :
+ TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) :
dest(d),
creationTime(ct),
- nwid(nw),
packet(p),
encrypt(enc) {}
Address dest;
uint64_t creationTime;
- uint64_t nwid;
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
bool encrypt;
};
@@ -235,32 +214,12 @@ private:
y = a2.toInt();
}
}
- inline unsigned long hashCode() const throw() { return ((unsigned long)x ^ (unsigned long)y); }
- inline bool operator==(const _LastUniteKey &k) const throw() { return ((x == k.x)&&(y == k.y)); }
+ inline unsigned long hashCode() const { return ((unsigned long)x ^ (unsigned long)y); }
+ inline bool operator==(const _LastUniteKey &k) const { return ((x == k.x)&&(y == k.y)); }
uint64_t x,y;
};
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
Mutex _lastUniteAttempt_m;
-
- // Active attempts to contact remote peers, including state of multi-phase NAT traversal
- struct ContactQueueEntry
- {
- ContactQueueEntry() {}
- ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &laddr,const InetAddress &a) :
- peer(p),
- fireAtTime(ft),
- inaddr(a),
- localAddr(laddr),
- strategyIteration(0) {}
-
- SharedPtr<Peer> peer;
- uint64_t fireAtTime;
- InetAddress inaddr;
- InetAddress localAddr;
- unsigned int strategyIteration;
- };
- std::list<ContactQueueEntry> _contactQueue;
- Mutex _contactQueue_m;
};
} // namespace ZeroTier
diff --git a/node/Tag.cpp b/node/Tag.cpp
new file mode 100644
index 00000000..62d9cb2e
--- /dev/null
+++ b/node/Tag.cpp
@@ -0,0 +1,55 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "Tag.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Identity.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
+ return -1;
+ const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(tPtr,RR->node->now(),_signedBy);
+ return 1;
+ }
+ try {
+ Buffer<(sizeof(Tag) * 2)> tmp;
+ this->serialize(tmp,true);
+ return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+ } catch ( ... ) {
+ return -1;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Tag.hpp b/node/Tag.hpp
new file mode 100644
index 00000000..d2e932c2
--- /dev/null
+++ b/node/Tag.hpp
@@ -0,0 +1,210 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TAG_HPP
+#define ZT_TAG_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "Credential.hpp"
+#include "C25519.hpp"
+#include "Address.hpp"
+#include "Identity.hpp"
+#include "Buffer.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * A tag that can be associated with members and matched in rules
+ *
+ * Capabilities group rules, while tags group members subject to those
+ * rules. Tag values can be matched in rules, and tags relevant to a
+ * capability are presented along with it.
+ *
+ * E.g. a capability might be "can speak Samba/CIFS within your
+ * department." This cap might have a rule to allow TCP/137 but
+ * only if a given tag ID's value matches between two peers. The
+ * capability is what members can do, while the tag is who they are.
+ * Different departments might have tags with the same ID but different
+ * values.
+ *
+ * Unlike capabilities tags are signed only by the issuer and are never
+ * transferrable.
+ */
+class Tag : public Credential
+{
+public:
+ static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_TAG; }
+
+ Tag()
+ {
+ memset(this,0,sizeof(Tag));
+ }
+
+ /**
+ * @param nwid Network ID
+ * @param ts Timestamp
+ * @param issuedTo Address to which this tag was issued
+ * @param id Tag ID
+ * @param value Tag value
+ */
+ Tag(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
+ _id(id),
+ _value(value),
+ _networkId(nwid),
+ _ts(ts),
+ _issuedTo(issuedTo),
+ _signedBy()
+ {
+ }
+
+ inline uint32_t id() const { return _id; }
+ inline const uint32_t &value() const { return _value; }
+ inline uint64_t networkId() const { return _networkId; }
+ inline int64_t timestamp() const { return _ts; }
+ inline const Address &issuedTo() const { return _issuedTo; }
+ inline const Address &signedBy() const { return _signedBy; }
+
+ /**
+ * Sign this tag
+ *
+ * @param signer Signing identity, must have private key
+ * @return True if signature was successful
+ */
+ inline bool sign(const Identity &signer)
+ {
+ if (signer.hasPrivate()) {
+ Buffer<sizeof(Tag) + 64> tmp;
+ _signedBy = signer.address();
+ this->serialize(tmp,true);
+ _signature = signer.sign(tmp.data(),tmp.size());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check this tag's signature
+ *
+ * @param RR Runtime environment to allow identity lookup for signedBy
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
+ */
+ int verify(const RuntimeEnvironment *RR,void *tPtr) const;
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,const bool forSign = false) const
+ {
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append(_networkId);
+ b.append(_ts);
+ b.append(_id);
+ b.append(_value);
+
+ _issuedTo.appendTo(b);
+ _signedBy.appendTo(b);
+ if (!forSign) {
+ b.append((uint8_t)1); // 1 == Ed25519
+ b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
+ b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+ }
+
+ b.append((uint16_t)0); // length of additional fields, currently 0
+
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ unsigned int p = startAt;
+
+ memset(this,0,sizeof(Tag));
+
+ _networkId = b.template at<uint64_t>(p); p += 8;
+ _ts = b.template at<uint64_t>(p); p += 8;
+ _id = b.template at<uint32_t>(p); p += 4;
+
+ _value = b.template at<uint32_t>(p); p += 4;
+
+ _issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ _signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+ if (b[p++] == 1) {
+ if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+ p += 2;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ } else {
+ p += 2 + b.template at<uint16_t>(p);
+ }
+
+ p += 2 + b.template at<uint16_t>(p);
+ if (p > b.size())
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+ return (p - startAt);
+ }
+
+ // Provides natural sort order by ID
+ inline bool operator<(const Tag &t) const { return (_id < t._id); }
+
+ inline bool operator==(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) == 0); }
+ inline bool operator!=(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) != 0); }
+
+ // For searching sorted arrays or lists of Tags by ID
+ struct IdComparePredicate
+ {
+ inline bool operator()(const Tag &a,const Tag &b) const { return (a.id() < b.id()); }
+ inline bool operator()(const uint32_t a,const Tag &b) const { return (a < b.id()); }
+ inline bool operator()(const Tag &a,const uint32_t b) const { return (a.id() < b); }
+ inline bool operator()(const Tag *a,const Tag *b) const { return (a->id() < b->id()); }
+ inline bool operator()(const Tag *a,const Tag &b) const { return (a->id() < b.id()); }
+ inline bool operator()(const Tag &a,const Tag *b) const { return (a.id() < b->id()); }
+ inline bool operator()(const uint32_t a,const Tag *b) const { return (a < b->id()); }
+ inline bool operator()(const Tag *a,const uint32_t b) const { return (a->id() < b); }
+ inline bool operator()(const uint32_t a,const uint32_t b) const { return (a < b); }
+ };
+
+private:
+ uint32_t _id;
+ uint32_t _value;
+ uint64_t _networkId;
+ int64_t _ts;
+ Address _issuedTo;
+ Address _signedBy;
+ C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Topology.cpp b/node/Topology.cpp
index 6e96f2eb..7c526b41 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "Constants.hpp"
@@ -23,342 +31,410 @@
#include "Network.hpp"
#include "NetworkConfig.hpp"
#include "Buffer.hpp"
+#include "Switch.hpp"
namespace ZeroTier {
-// 2015-11-16 -- The Fabulous Four (should have named them after Beatles!)
-//#define ZT_DEFAULT_WORLD_LENGTH 494
-//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x11,0x70,0xb2,0xfb,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x80,0x31,0xa4,0x65,0x95,0x45,0x06,0x1c,0xfb,0xc2,0x4e,0x5d,0xe7,0x0a,0x40,0x7a,0x97,0xce,0x36,0xa2,0x3d,0x05,0xca,0x87,0xc7,0x59,0x27,0x5c,0x8b,0x0d,0x4c,0xb4,0xbb,0x26,0x2f,0x77,0x17,0x5e,0xb7,0x4d,0xb8,0xd3,0xb4,0xe9,0x23,0x5d,0xcc,0xa2,0x71,0xa8,0xdf,0xf1,0x23,0xa3,0xb2,0x66,0x74,0xea,0xe5,0xdc,0x8d,0xef,0xd3,0x0a,0xa9,0xac,0xcb,0xda,0x93,0xbd,0x6c,0xcd,0x43,0x1d,0xa7,0x98,0x6a,0xde,0x70,0xc0,0xc6,0x1c,0xaf,0xf0,0xfd,0x7f,0x8a,0xb9,0x76,0x13,0xe1,0xde,0x4f,0xf3,0xd6,0x13,0x04,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x01,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x01,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09};
-
-// 2015-11-20 -- Alice and Bob are live, and we're now IPv6 dual-stack!
-//#define ZT_DEFAULT_WORLD_LENGTH 792
-//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x26,0x6f,0x7c,0x8a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0xe8,0x0a,0xf5,0xbc,0xf8,0x3d,0x97,0xcd,0xc3,0xf8,0xe2,0x41,0x16,0x42,0x0f,0xc7,0x76,0x8e,0x07,0xf3,0x7e,0x9e,0x7d,0x1b,0xb3,0x23,0x21,0x79,0xce,0xb9,0xd0,0xcb,0xb5,0x94,0x7b,0x89,0x21,0x57,0x72,0xf6,0x70,0xa1,0xdd,0x67,0x38,0xcf,0x45,0x45,0xc2,0x8d,0x46,0xec,0x00,0x2c,0xe0,0x2a,0x63,0x3f,0x63,0x8d,0x33,0x08,0x51,0x07,0x77,0x81,0x5b,0x32,0x49,0xae,0x87,0x89,0xcf,0x31,0xaa,0x41,0xf1,0x52,0x97,0xdc,0xa2,0x55,0xe1,0x4a,0x6e,0x3c,0x04,0xf0,0x4f,0x8a,0x0e,0xe9,0xca,0xec,0x24,0x30,0x04,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09};
-
-// 2015-12-17 -- Old New York root is dead, old SF still alive
-//#define ZT_DEFAULT_WORLD_LENGTH 732
-//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0xb1,0x7e,0x39,0x9d,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x8a,0xca,0xf2,0x3d,0x71,0x2e,0xc2,0x39,0x45,0x66,0xb3,0xe9,0x39,0x79,0xb1,0x55,0xc4,0xa9,0xfc,0xbc,0xfc,0x55,0xaf,0x8a,0x2f,0x38,0xc8,0xcd,0xe9,0x02,0x5b,0x86,0xa9,0x72,0xf7,0x16,0x00,0x35,0xb7,0x84,0xc9,0xfc,0xe4,0xfa,0x96,0x8b,0xf4,0x1e,0xba,0x60,0x9f,0x85,0x14,0xc2,0x07,0x4b,0xfd,0xd1,0x6c,0x19,0x69,0xd3,0xf9,0x09,0x9c,0x9d,0xe3,0xb9,0x8f,0x11,0x78,0x71,0xa7,0x4a,0x05,0xd8,0xcc,0x60,0xa2,0x06,0x66,0x9f,0x47,0xc2,0x71,0xb8,0x54,0x80,0x9c,0x45,0x16,0x10,0xa9,0xd0,0xbd,0xf7,0x03,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x02,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0xc5,0xf0,0x01,0x27,0x09};
-
-// 2016-01-13 -- Old San Francisco 1.0.1 root is dead, now we're just on Alice and Bob!
+/*
+ * 2016-01-13 ZeroTier planet definition for the third planet of Sol:
+ *
+ * There are two roots, each of which is a cluster spread across multiple
+ * continents and providers. They are named Alice and Bob after the
+ * canonical example names used in cryptography.
+ *
+ * Alice:
+ *
+ * root-alice-ams-01: Amsterdam, Netherlands
+ * root-alice-joh-01: Johannesburg, South Africa
+ * root-alice-nyc-01: New York, New York, USA
+ * root-alice-sao-01: Sao Paolo, Brazil
+ * root-alice-sfo-01: San Francisco, California, USA
+ * root-alice-sgp-01: Singapore
+ *
+ * Bob:
+ *
+ * root-bob-dfw-01: Dallas, Texas, USA
+ * root-bob-fra-01: Frankfurt, Germany
+ * root-bob-par-01: Paris, France
+ * root-bob-syd-01: Sydney, Australia
+ * root-bob-tok-01: Tokyo, Japan
+ * root-bob-tor-01: Toronto, Canada
+ */
#define ZT_DEFAULT_WORLD_LENGTH 634
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x52,0x3c,0x32,0x50,0x1a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x4a,0xf7,0x86,0xa8,0x40,0xd6,0x52,0xea,0xae,0x9e,0x7a,0xbf,0x4c,0x97,0x66,0xab,0x2d,0x6f,0xaf,0xc9,0x2b,0x3a,0xff,0xed,0xd6,0x30,0x3e,0xc4,0x6a,0x65,0xf2,0xbd,0x83,0x52,0xf5,0x40,0xe9,0xcc,0x0d,0x6e,0x89,0x3f,0x9a,0xa0,0xb8,0xdf,0x42,0xd2,0x2f,0x84,0xe6,0x03,0x26,0x0f,0xa8,0xe3,0xcc,0x05,0x05,0x03,0xef,0x12,0x80,0x0d,0xce,0x3e,0xb6,0x58,0x3b,0x1f,0xa8,0xad,0xc7,0x25,0xf9,0x43,0x71,0xa7,0x5c,0x9a,0xc7,0xe1,0xa3,0xb8,0x88,0xd0,0x71,0x6c,0x94,0x99,0x73,0x41,0x0b,0x1b,0x48,0x84,0x02,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09};
-Topology::Topology(const RuntimeEnvironment *renv) :
+Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
RR(renv),
- _trustedPathCount(0),
- _amRoot(false)
+ _numConfiguredPhysicalPaths(0),
+ _amUpstream(false)
{
- std::string alls(RR->node->dataStoreGet("peers.save"));
- const uint8_t *all = reinterpret_cast<const uint8_t *>(alls.data());
- RR->node->dataStoreDelete("peers.save");
-
- Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *deserializeBuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
- unsigned int ptr = 0;
- while ((ptr + 4) < alls.size()) {
+ uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ uint64_t idtmp[2];
+ idtmp[0] = 0; idtmp[1] = 0;
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,idtmp,tmp,sizeof(tmp));
+ if (n > 0) {
try {
- const unsigned int reclen = ( // each Peer serialized record is prefixed by a record length
- ((((unsigned int)all[ptr]) & 0xff) << 24) |
- ((((unsigned int)all[ptr + 1]) & 0xff) << 16) |
- ((((unsigned int)all[ptr + 2]) & 0xff) << 8) |
- (((unsigned int)all[ptr + 3]) & 0xff)
- );
- unsigned int pos = 0;
- deserializeBuf->copyFrom(all + ptr,reclen + 4);
- SharedPtr<Peer> p(Peer::deserializeNew(RR,RR->identity,*deserializeBuf,pos));
- ptr += pos;
- if (!p)
- break; // stop if invalid records
- if (p->address() != RR->identity.address())
- _peers.set(p->address(),p);
- } catch ( ... ) {
- break; // stop if invalid records
- }
+ World cachedPlanet;
+ cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0);
+ addWorld(tPtr,cachedPlanet,false);
+ } catch ( ... ) {} // ignore invalid cached planets
}
- delete deserializeBuf;
-
- clean(RR->node->now());
- std::string dsWorld(RR->node->dataStoreGet("world"));
- World cachedWorld;
- if (dsWorld.length() > 0) {
- try {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(dsWorld.data(),(unsigned int)dsWorld.length());
- cachedWorld.deserialize(dswtmp,0);
- } catch ( ... ) {
- cachedWorld = World(); // clear if cached world is invalid
- }
- }
- World defaultWorld;
+ World defaultPlanet;
{
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
- defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
+ defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
}
- if (cachedWorld.shouldBeReplacedBy(defaultWorld,false)) {
- _setWorld(defaultWorld);
- if (dsWorld.length() > 0)
- RR->node->dataStoreDelete("world");
- } else _setWorld(cachedWorld);
+ addWorld(tPtr,defaultPlanet,false);
}
Topology::~Topology()
{
- Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *pbuf = 0;
- try {
- pbuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
- std::string all;
-
- Address *a = (Address *)0;
- SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
- Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
- while (i.next(a,p)) {
- if (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) {
- pbuf->clear();
- try {
- (*p)->serialize(*pbuf);
- try {
- all.append((const char *)pbuf->data(),pbuf->size());
- } catch ( ... ) {
- return; // out of memory? just skip
- }
- } catch ( ... ) {} // peer too big? shouldn't happen, but it so skip
- }
- }
-
- RR->node->dataStorePut("peers.save",all,true);
-
- delete pbuf;
- } catch ( ... ) {
- delete pbuf;
- }
+ Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
+ Address *a = (Address *)0;
+ SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
+ while (i.next(a,p))
+ _savePeer((void *)0,*p);
}
-SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
+SharedPtr<Peer> Topology::addPeer(void *tPtr,const SharedPtr<Peer> &peer)
{
-#ifdef ZT_TRACE
- if ((!peer)||(peer->address() == RR->identity.address())) {
- if (!peer)
- fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add NULL peer" ZT_EOL_S);
- else fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add peer for self" ZT_EOL_S);
- abort();
- }
-#endif
-
SharedPtr<Peer> np;
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
SharedPtr<Peer> &hp = _peers[peer->address()];
if (!hp)
hp = peer;
np = hp;
}
-
- np->use(RR->node->now());
- saveIdentity(np->identity());
-
return np;
}
-SharedPtr<Peer> Topology::getPeer(const Address &zta)
+SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
{
- if (zta == RR->identity.address()) {
- TRACE("BUG: ignored attempt to getPeer() for self, returned NULL");
+ if (zta == RR->identity.address())
return SharedPtr<Peer>();
- }
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
const SharedPtr<Peer> *const ap = _peers.get(zta);
- if (ap) {
- (*ap)->use(RR->node->now());
+ if (ap)
return *ap;
- }
}
try {
- Identity id(_getIdentity(zta));
- if (id) {
- SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
- {
- Mutex::Lock _l(_lock);
- SharedPtr<Peer> &ap = _peers[zta];
- if (!ap)
- ap.swap(np);
- ap->use(RR->node->now());
+ Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
+ uint64_t idbuf[2]; idbuf[0] = zta.toInt(); idbuf[1] = 0;
+ int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf.unsafeData(),ZT_PEER_MAX_SERIALIZED_STATE_SIZE);
+ if (len > 0) {
+ buf.setSize(len);
+ Mutex::Lock _l(_peers_m);
+ SharedPtr<Peer> &ap = _peers[zta];
+ if (ap)
return ap;
+ ap = Peer::deserializeFromCache(RR->node->now(),tPtr,buf,RR);
+ if (!ap) {
+ _peers.erase(zta);
}
+ return SharedPtr<Peer>();
}
- } catch ( ... ) {
- fprintf(stderr,"EXCEPTION in getPeer() part 2\n");
- abort();
- } // invalid identity on disk?
+ } catch ( ... ) {} // ignore invalid identities or other strage failures
return SharedPtr<Peer>();
}
-Identity Topology::getIdentity(const Address &zta)
+Identity Topology::getIdentity(void *tPtr,const Address &zta)
{
- {
- Mutex::Lock _l(_lock);
+ if (zta == RR->identity.address()) {
+ return RR->identity;
+ } else {
+ Mutex::Lock _l(_peers_m);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return (*ap)->identity();
}
- return _getIdentity(zta);
+ return Identity();
+}
+
+SharedPtr<Peer> Topology::getUpstreamPeer()
+{
+ const int64_t now = RR->node->now();
+ unsigned int bestq = ~((unsigned int)0);
+ const SharedPtr<Peer> *best = (const SharedPtr<Peer> *)0;
+
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
+
+ for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
+ const SharedPtr<Peer> *p = _peers.get(*a);
+ if (p) {
+ const unsigned int q = (*p)->relayQuality(now);
+ if (q <= bestq) {
+ bestq = q;
+ best = p;
+ }
+ }
+ }
+
+ if (!best)
+ return SharedPtr<Peer>();
+ return *best;
+}
+
+bool Topology::isUpstream(const Identity &id) const
+{
+ Mutex::Lock _l(_upstreams_m);
+ return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
+}
+
+bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const
+{
+ Mutex::Lock _l(_upstreams_m);
+ if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end())
+ return true;
+ for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
+ if (s->second == addr)
+ return true;
+ }
+ return false;
}
-void Topology::saveIdentity(const Identity &id)
+ZT_PeerRole Topology::role(const Address &ztaddr) const
{
- if (id) {
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt());
- RR->node->dataStorePut(p,id.toString(false),false);
+ Mutex::Lock _l(_upstreams_m);
+ if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
+ for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
+ if (i->identity.address() == ztaddr)
+ return ZT_PEER_ROLE_PLANET;
+ }
+ return ZT_PEER_ROLE_MOON;
}
+ return ZT_PEER_ROLE_LEAF;
}
-SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
+bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const
{
- const uint64_t now = RR->node->now();
- Mutex::Lock _l(_lock);
-
- if (_amRoot) {
- /* If I am a root server, the "best" root server is the one whose address
- * is numerically greater than mine (with wrap at top of list). This
- * causes packets searching for a route to pretty much literally
- * circumnavigate the globe rather than bouncing between just two. */
-
- for(unsigned long p=0;p<_rootAddresses.size();++p) {
- if (_rootAddresses[p] == RR->identity.address()) {
- for(unsigned long q=1;q<_rootAddresses.size();++q) {
- const SharedPtr<Peer> *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]);
- if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) {
- (*nextsn)->use(now);
- return *nextsn;
+ Mutex::Lock _l(_upstreams_m);
+
+ // For roots the only permitted addresses are those defined. This adds just a little
+ // bit of extra security against spoofing, replaying, etc.
+ if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) {
+ for(std::vector<World::Root>::const_iterator r(_planet.roots().begin());r!=_planet.roots().end();++r) {
+ if (r->identity.address() == ztaddr) {
+ if (r->stableEndpoints.size() == 0)
+ return false; // no stable endpoints specified, so allow dynamic paths
+ for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
+ if (ipaddr.ipsEqual(*e))
+ return false;
+ }
+ }
+ }
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ for(std::vector<World::Root>::const_iterator r(m->roots().begin());r!=m->roots().end();++r) {
+ if (r->identity.address() == ztaddr) {
+ if (r->stableEndpoints.size() == 0)
+ return false; // no stable endpoints specified, so allow dynamic paths
+ for(std::vector<InetAddress>::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) {
+ if (ipaddr.ipsEqual(*e))
+ return false;
}
}
- break;
}
}
+ return true;
+ }
- } else {
- /* If I am not a root server, the best root server is the active one with
- * the lowest quality score. (lower == better) */
-
- unsigned int bestQualityOverall = ~((unsigned int)0);
- unsigned int bestQualityNotAvoid = ~((unsigned int)0);
- const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
- const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
-
- for(std::vector< SharedPtr<Peer> >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) {
- bool avoiding = false;
- for(unsigned int i=0;i<avoidCount;++i) {
- if (avoid[i] == (*r)->address()) {
- avoiding = true;
+ return false;
+}
+
+bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
+{
+ if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON))
+ return false;
+
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
+
+ World *existing = (World *)0;
+ switch(newWorld.type()) {
+ case World::TYPE_PLANET:
+ existing = &_planet;
+ break;
+ case World::TYPE_MOON:
+ for(std::vector< World >::iterator m(_moons.begin());m!=_moons.end();++m) {
+ if (m->id() == newWorld.id()) {
+ existing = &(*m);
break;
}
}
- const unsigned int q = (*r)->relayQuality(now);
- if (q <= bestQualityOverall) {
- bestQualityOverall = q;
- bestOverall = &(*r);
- }
- if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
- bestQualityNotAvoid = q;
- bestNotAvoid = &(*r);
+ break;
+ default:
+ return false;
+ }
+
+ if (existing) {
+ if (existing->shouldBeReplacedBy(newWorld))
+ *existing = newWorld;
+ else return false;
+ } else if (newWorld.type() == World::TYPE_MOON) {
+ if (alwaysAcceptNew) {
+ _moons.push_back(newWorld);
+ existing = &(_moons.back());
+ } else {
+ for(std::vector< std::pair<uint64_t,Address> >::iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
+ if (m->first == newWorld.id()) {
+ for(std::vector<World::Root>::const_iterator r(newWorld.roots().begin());r!=newWorld.roots().end();++r) {
+ if (r->identity.address() == m->second) {
+ _moonSeeds.erase(m);
+ _moons.push_back(newWorld);
+ existing = &(_moons.back());
+ break;
+ }
+ }
+ if (existing)
+ break;
+ }
}
}
+ if (!existing)
+ return false;
+ } else {
+ return false;
+ }
- if (bestNotAvoid) {
- (*bestNotAvoid)->use(now);
- return *bestNotAvoid;
- } else if ((!strictAvoid)&&(bestOverall)) {
- (*bestOverall)->use(now);
- return *bestOverall;
- }
+ try {
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf;
+ existing->serialize(sbuf,false);
+ uint64_t idtmp[2];
+ idtmp[0] = existing->id(); idtmp[1] = 0;
+ RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,idtmp,sbuf.data(),sbuf.size());
+ } catch ( ... ) {}
- }
+ _memoizeUpstreams(tPtr);
- return SharedPtr<Peer>();
+ return true;
}
-bool Topology::isUpstream(const Identity &id) const
+void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
{
- if (isRoot(id))
- return true;
- std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator nw(nws.begin());nw!=nws.end();++nw) {
- if ((*nw)->config().isRelay(id.address())) {
- return true;
- }
+ char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ uint64_t idtmp[2];
+ idtmp[0] = id; idtmp[1] = 0;
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,idtmp,tmp,sizeof(tmp));
+ if (n > 0) {
+ try {
+ World w;
+ w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n));
+ if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
+ addWorld(tPtr,w,true);
+ return;
+ }
+ } catch ( ... ) {}
}
- return false;
-}
-bool Topology::worldUpdateIfValid(const World &newWorld)
-{
- Mutex::Lock _l(_lock);
- if (_world.shouldBeReplacedBy(newWorld,true)) {
- _setWorld(newWorld);
- try {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
- newWorld.serialize(dswtmp,false);
- RR->node->dataStorePut("world",dswtmp.data(),dswtmp.size(),false);
- } catch ( ... ) {
- RR->node->dataStoreDelete("world");
- }
- return true;
+ if (seed) {
+ Mutex::Lock _l(_upstreams_m);
+ if (std::find(_moonSeeds.begin(),_moonSeeds.end(),std::pair<uint64_t,Address>(id,seed)) == _moonSeeds.end())
+ _moonSeeds.push_back(std::pair<uint64_t,Address>(id,seed));
}
- return false;
}
-void Topology::clean(uint64_t now)
+void Topology::removeMoon(void *tPtr,const uint64_t id)
{
- Mutex::Lock _l(_lock);
- Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
- Address *a = (Address *)0;
- SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
- while (i.next(a,p)) {
- if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) {
- _peers.erase(*a);
+ Mutex::Lock _l2(_peers_m);
+ Mutex::Lock _l1(_upstreams_m);
+
+ std::vector<World> nm;
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ if (m->id() != id) {
+ nm.push_back(*m);
} else {
- (*p)->clean(now);
+ uint64_t idtmp[2];
+ idtmp[0] = id; idtmp[1] = 0;
+ RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp);
}
}
+ _moons.swap(nm);
+
+ std::vector< std::pair<uint64_t,Address> > cm;
+ for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) {
+ if (m->first != id)
+ cm.push_back(*m);
+ }
+ _moonSeeds.swap(cm);
+
+ _memoizeUpstreams(tPtr);
}
-Identity Topology::_getIdentity(const Address &zta)
+void Topology::doPeriodicTasks(void *tPtr,int64_t now)
{
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)zta.toInt());
- std::string ids(RR->node->dataStoreGet(p));
- if (ids.length() > 0) {
- try {
- return Identity(ids);
- } catch ( ... ) {} // ignore invalid IDs
+ {
+ Mutex::Lock _l1(_peers_m);
+ Mutex::Lock _l2(_upstreams_m);
+ Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
+ Address *a = (Address *)0;
+ SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
+ while (i.next(a,p)) {
+ if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) {
+ _savePeer(tPtr,*p);
+ _peers.erase(*a);
+ }
+ }
+ }
+
+ {
+ Mutex::Lock _l(_paths_m);
+ Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
+ Path::HashKey *k = (Path::HashKey *)0;
+ SharedPtr<Path> *p = (SharedPtr<Path> *)0;
+ while (i.next(k,p)) {
+ if (p->references() <= 1)
+ _paths.erase(*k);
+ }
}
- return Identity();
}
-void Topology::_setWorld(const World &newWorld)
+void Topology::_memoizeUpstreams(void *tPtr)
{
- // assumed _lock is locked (or in constructor)
- _world = newWorld;
- _amRoot = false;
- _rootAddresses.clear();
- _rootPeers.clear();
- for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
- _rootAddresses.push_back(r->identity.address());
- if (r->identity.address() == RR->identity.address()) {
- _amRoot = true;
- } else {
- SharedPtr<Peer> *rp = _peers.get(r->identity.address());
- if (rp) {
- _rootPeers.push_back(*rp);
- } else {
- SharedPtr<Peer> newrp(new Peer(RR,RR->identity,r->identity));
- _peers.set(r->identity.address(),newrp);
- _rootPeers.push_back(newrp);
+ // assumes _upstreams_m and _peers_m are locked
+ _upstreamAddresses.clear();
+ _amUpstream = false;
+
+ for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
+ if (i->identity == RR->identity) {
+ _amUpstream = true;
+ } else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
+ _upstreamAddresses.push_back(i->identity.address());
+ SharedPtr<Peer> &hp = _peers[i->identity.address()];
+ if (!hp)
+ hp = new Peer(RR,RR->identity,i->identity);
+ }
+ }
+
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
+ if (i->identity == RR->identity) {
+ _amUpstream = true;
+ } else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) {
+ _upstreamAddresses.push_back(i->identity.address());
+ SharedPtr<Peer> &hp = _peers[i->identity.address()];
+ if (!hp)
+ hp = new Peer(RR,RR->identity,i->identity);
}
}
}
+
+ std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end());
+}
+
+void Topology::_savePeer(void *tPtr,const SharedPtr<Peer> &peer)
+{
+ try {
+ Buffer<ZT_PEER_MAX_SERIALIZED_STATE_SIZE> buf;
+ peer->serializeForCache(buf);
+ uint64_t tmpid[2]; tmpid[0] = peer->address().toInt(); tmpid[1] = 0;
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,tmpid,buf.data(),buf.size());
+ } catch ( ... ) {} // sanity check, discard invalid entries
}
} // namespace ZeroTier
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 03c491e5..63946a32 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_TOPOLOGY_HPP
@@ -33,6 +41,7 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "Peer.hpp"
+#include "Path.hpp"
#include "Mutex.hpp"
#include "InetAddress.hpp"
#include "Hashtable.hpp"
@@ -48,7 +57,7 @@ class RuntimeEnvironment;
class Topology
{
public:
- Topology(const RuntimeEnvironment *renv);
+ Topology(const RuntimeEnvironment *renv,void *tPtr);
~Topology();
/**
@@ -57,18 +66,27 @@ public:
* This will not replace existing peers. In that case the existing peer
* record is returned.
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param peer Peer to add
* @return New or existing peer (should replace 'peer')
*/
- SharedPtr<Peer> addPeer(const SharedPtr<Peer> &peer);
+ SharedPtr<Peer> addPeer(void *tPtr,const SharedPtr<Peer> &peer);
/**
* Get a peer from its address
*
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param zta ZeroTier address of peer
* @return Peer or NULL if not found
*/
- SharedPtr<Peer> getPeer(const Address &zta);
+ SharedPtr<Peer> getPeer(void *tPtr,const Address &zta);
+
+ /**
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param zta ZeroTier address of peer
+ * @return Identity or NULL identity if not found
+ */
+ Identity getIdentity(void *tPtr,const Address &zta);
/**
* Get a peer only if it is presently in memory (no disk cache)
@@ -82,7 +100,7 @@ public:
*/
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return *ap;
@@ -90,120 +108,200 @@ public:
}
/**
- * Get the identity of a peer
+ * Get a Path object for a given local and remote physical address, creating if needed
*
- * @param zta ZeroTier address of peer
- * @return Identity or NULL Identity if not found
+ * @param l Local socket
+ * @param r Remote address
+ * @return Pointer to canonicalized Path object
*/
- Identity getIdentity(const Address &zta);
+ inline SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
+ {
+ Mutex::Lock _l(_paths_m);
+ SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
+ if (!p)
+ p.set(new Path(l,r));
+ return p;
+ }
/**
- * Cache an identity
- *
- * This is done automatically on addPeer(), and so is only useful for
- * cluster identity replication.
+ * Get the current best upstream peer
*
- * @param id Identity to cache
+ * @return Upstream or NULL if none available
*/
- void saveIdentity(const Identity &id);
+ SharedPtr<Peer> getUpstreamPeer();
/**
- * Get the current favorite root server
- *
- * @return Root server with lowest latency or NULL if none
+ * @param id Identity to check
+ * @return True if this is a root server or a network preferred relay from one of our networks
*/
- inline SharedPtr<Peer> getBestRoot() { return getBestRoot((const Address *)0,0,false); }
+ bool isUpstream(const Identity &id) const;
/**
- * Get the best root server, avoiding root servers listed in an array
+ * @param addr Address to check
+ * @return True if we should accept a world update from this address
+ */
+ bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
+
+ /**
+ * @param ztaddr ZeroTier address
+ * @return Peer role for this device
+ */
+ ZT_PeerRole role(const Address &ztaddr) const;
+
+ /**
+ * Check for prohibited endpoints
+ *
+ * Right now this returns true if the designated ZT address is a root and if
+ * the IP (IP only, not port) does not equal any of the IPs defined in the
+ * current World. This is an extra little security feature in case root keys
+ * get appropriated or something.
*
- * This will get the best root server (lowest latency, etc.) but will
- * try to avoid the listed root servers, only using them if no others
- * are available.
+ * Otherwise it returns false.
*
- * @param avoid Nodes to avoid
- * @param avoidCount Number of nodes to avoid
- * @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
- * @return Root server or NULL if none available
+ * @param ztaddr ZeroTier address
+ * @param ipaddr IP address
+ * @return True if this ZT/IP pair should not be allowed to be used
*/
- SharedPtr<Peer> getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
+ bool isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const;
/**
- * @param id Identity to check
- * @return True if this is a designated root server in this world
+ * Gets upstreams to contact and their stable endpoints (if known)
+ *
+ * @param eps Hash table to fill with addresses and their stable endpoints
*/
- inline bool isRoot(const Identity &id) const
+ inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
{
- Mutex::Lock _l(_lock);
- return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
+ Mutex::Lock _l(_upstreams_m);
+ for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
+ if (i->identity != RR->identity) {
+ std::vector<InetAddress> &ips = eps[i->identity.address()];
+ for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
+ if (std::find(ips.begin(),ips.end(),*j) == ips.end())
+ ips.push_back(*j);
+ }
+ }
+ }
+ for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
+ for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
+ if (i->identity != RR->identity) {
+ std::vector<InetAddress> &ips = eps[i->identity.address()];
+ for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
+ if (std::find(ips.begin(),ips.end(),*j) == ips.end())
+ ips.push_back(*j);
+ }
+ }
+ }
+ }
+ for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m)
+ eps[m->second];
}
/**
- * @param id Identity to check
- * @return True if this is a root server or a network preferred relay from one of our networks
+ * @return Vector of active upstream addresses (including roots)
*/
- bool isUpstream(const Identity &id) const;
+ inline std::vector<Address> upstreamAddresses() const
+ {
+ Mutex::Lock _l(_upstreams_m);
+ return _upstreamAddresses;
+ }
/**
- * @return Vector of root server addresses
+ * @return Current moons
*/
- inline std::vector<Address> rootAddresses() const
+ inline std::vector<World> moons() const
{
- Mutex::Lock _l(_lock);
- return _rootAddresses;
+ Mutex::Lock _l(_upstreams_m);
+ return _moons;
}
/**
- * @return Current World (copy)
+ * @return Moon IDs we are waiting for from seeds
*/
- inline World world() const
+ inline std::vector<uint64_t> moonsWanted() const
{
- Mutex::Lock _l(_lock);
- return _world;
+ Mutex::Lock _l(_upstreams_m);
+ std::vector<uint64_t> mw;
+ for(std::vector< std::pair<uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
+ if (std::find(mw.begin(),mw.end(),s->first) == mw.end())
+ mw.push_back(s->first);
+ }
+ return mw;
}
/**
- * @return Current world ID
+ * @return Current planet
*/
- inline uint64_t worldId() const
+ inline World planet() const
{
- return _world.id(); // safe to read without lock, and used from within eachPeer() so don't lock
+ Mutex::Lock _l(_upstreams_m);
+ return _planet;
}
/**
- * @return Current world timestamp
+ * @return Current planet's world ID
*/
- inline uint64_t worldTimestamp() const
+ inline uint64_t planetWorldId() const
{
- return _world.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
+ return _planet.id(); // safe to read without lock, and used from within eachPeer() so don't lock
+ }
+
+ /**
+ * @return Current planet's world timestamp
+ */
+ inline uint64_t planetWorldTimestamp() const
+ {
+ return _planet.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
}
/**
* Validate new world and update if newer and signature is okay
*
- * @param newWorld Potential new world definition revision
- * @return True if an update actually occurred
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param newWorld A new or updated planet or moon to learn
+ * @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
+ * @return True if it was valid and newer than current (or totally new for moons)
+ */
+ bool addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew);
+
+ /**
+ * Add a moon
+ *
+ * This loads it from moons.d if present, and if not adds it to
+ * a list of moons that we want to contact.
+ *
+ * @param id Moon ID
+ * @param seed If non-NULL, an address of any member of the moon to contact
+ */
+ void addMoon(void *tPtr,const uint64_t id,const Address &seed);
+
+ /**
+ * Remove a moon
+ *
+ * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+ * @param id Moon's world ID
*/
- bool worldUpdateIfValid(const World &newWorld);
+ void removeMoon(void *tPtr,const uint64_t id);
/**
* Clean and flush database
*/
- void clean(uint64_t now);
+ void doPeriodicTasks(void *tPtr,int64_t now);
/**
* @param now Current time
* @return Number of peers with active direct paths
*/
- inline unsigned long countActive(uint64_t now) const
+ inline unsigned long countActive(int64_t now) const
{
unsigned long cnt = 0;
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
- cnt += (unsigned long)((*p)->hasActiveDirectPath(now));
+ const SharedPtr<Path> pp((*p)->getBestPath(now,false));
+ if (pp)
+ ++cnt;
}
return cnt;
}
@@ -211,30 +309,17 @@ public:
/**
* Apply a function or function object to all peers
*
- * Note: explicitly template this by reference if you want the object
- * passed by reference instead of copied.
- *
- * Warning: be careful not to use features in these that call any other
- * methods of Topology that may lock _lock, otherwise a recursive lock
- * and deadlock or lock corruption may occur.
- *
* @param f Function to apply
* @tparam F Function or function object type
*/
template<typename F>
inline void eachPeer(F f)
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
-#ifdef ZT_TRACE
- if (!(*p)) {
- fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL" ZT_EOL_S,a->toString().c_str());
- abort();
- }
-#endif
f(*this,*((const SharedPtr<Peer> *)p));
}
}
@@ -244,14 +329,49 @@ public:
*/
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
{
- Mutex::Lock _l(_lock);
+ Mutex::Lock _l(_peers_m);
return _peers.entries();
}
/**
- * @return True if I am a root server in the current World
+ * @return True if I am a root server in a planet or moon
+ */
+ inline bool amUpstream() const { return _amUpstream; }
+
+ /**
+ * Get info about a path
+ *
+ * The supplied result variables are not modified if no special config info is found.
+ *
+ * @param physicalAddress Physical endpoint address
+ * @param mtu Variable set to MTU
+ * @param trustedPathId Variable set to trusted path ID
+ */
+ inline void getOutboundPathInfo(const InetAddress &physicalAddress,unsigned int &mtu,uint64_t &trustedPathId)
+ {
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress)) {
+ trustedPathId = _physicalPathConfig[i].second.trustedPathId;
+ mtu = _physicalPathConfig[i].second.mtu;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get the payload MTU for an outbound physical path (returns default if not configured)
+ *
+ * @param physicalAddress Physical endpoint address
+ * @return MTU
*/
- inline bool amRoot() const throw() { return _amRoot; }
+ inline unsigned int getOutboundPathMtu(const InetAddress &physicalAddress)
+ {
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress))
+ return _physicalPathConfig[i].second.mtu;
+ }
+ return ZT_DEFAULT_PHYSMTU;
+ }
/**
* Get the outbound trusted path ID for a physical address, or 0 if none
@@ -261,9 +381,9 @@ public:
*/
inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
{
- for(unsigned int i=0;i<_trustedPathCount;++i) {
- if (_trustedPathNetworks[i].containsAddress(physicalAddress))
- return _trustedPathIds[i];
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if (_physicalPathConfig[i].first.containsAddress(physicalAddress))
+ return _physicalPathConfig[i].second.trustedPathId;
}
return 0;
}
@@ -276,48 +396,72 @@ public:
*/
inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
{
- for(unsigned int i=0;i<_trustedPathCount;++i) {
- if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i) {
+ if ((_physicalPathConfig[i].second.trustedPathId == trustedPathId)&&(_physicalPathConfig[i].first.containsAddress(physicalAddress)))
return true;
}
return false;
}
/**
- * Set trusted paths in this topology
- *
- * @param networks Array of networks (prefix/netmask bits)
- * @param ids Array of trusted path IDs
- * @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
+ * Set or clear physical path configuration (called via Node::setPhysicalPathConfiguration)
*/
- inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
+ inline void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
{
- if (count > ZT_MAX_TRUSTED_PATHS)
- count = ZT_MAX_TRUSTED_PATHS;
- Mutex::Lock _l(_lock);
- for(unsigned int i=0;i<count;++i) {
- _trustedPathIds[i] = ids[i];
- _trustedPathNetworks[i] = networks[i];
+ if (!pathNetwork) {
+ _numConfiguredPhysicalPaths = 0;
+ } else {
+ std::map<InetAddress,ZT_PhysicalPathConfiguration> cpaths;
+ for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i)
+ cpaths[_physicalPathConfig[i].first] = _physicalPathConfig[i].second;
+
+ if (pathConfig) {
+ ZT_PhysicalPathConfiguration pc(*pathConfig);
+
+ if (pc.mtu <= 0)
+ pc.mtu = ZT_DEFAULT_PHYSMTU;
+ else if (pc.mtu < ZT_MIN_PHYSMTU)
+ pc.mtu = ZT_MIN_PHYSMTU;
+ else if (pc.mtu > ZT_MAX_PHYSMTU)
+ pc.mtu = ZT_MAX_PHYSMTU;
+
+ cpaths[*(reinterpret_cast<const InetAddress *>(pathNetwork))] = pc;
+ } else {
+ cpaths.erase(*(reinterpret_cast<const InetAddress *>(pathNetwork)));
+ }
+
+ unsigned int cnt = 0;
+ for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cnt<ZT_MAX_CONFIGURABLE_PATHS));++i) {
+ _physicalPathConfig[cnt].first = i->first;
+ _physicalPathConfig[cnt].second = i->second;
+ ++cnt;
+ }
+ _numConfiguredPhysicalPaths = cnt;
}
- _trustedPathCount = count;
}
private:
- Identity _getIdentity(const Address &zta);
- void _setWorld(const World &newWorld);
+ Identity _getIdentity(void *tPtr,const Address &zta);
+ void _memoizeUpstreams(void *tPtr);
+ void _savePeer(void *tPtr,const SharedPtr<Peer> &peer);
const RuntimeEnvironment *const RR;
- uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
- InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
- unsigned int _trustedPathCount;
- World _world;
+ std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
+ volatile unsigned int _numConfiguredPhysicalPaths;
+
Hashtable< Address,SharedPtr<Peer> > _peers;
- std::vector< Address > _rootAddresses;
- std::vector< SharedPtr<Peer> > _rootPeers;
- bool _amRoot;
+ Mutex _peers_m;
+
+ Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
+ Mutex _paths_m;
- Mutex _lock;
+ World _planet;
+ std::vector<World> _moons;
+ std::vector< std::pair<uint64_t,Address> > _moonSeeds;
+ std::vector<Address> _upstreamAddresses;
+ bool _amUpstream;
+ Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
};
} // namespace ZeroTier
diff --git a/node/Trace.cpp b/node/Trace.cpp
new file mode 100644
index 00000000..386edaac
--- /dev/null
+++ b/node/Trace.cpp
@@ -0,0 +1,540 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+//#define ZT_TRACE
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Trace.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Switch.hpp"
+#include "Node.hpp"
+#include "Utils.hpp"
+#include "Dictionary.hpp"
+#include "CertificateOfMembership.hpp"
+#include "CertificateOfOwnership.hpp"
+#include "Tag.hpp"
+#include "Capability.hpp"
+#include "Revocation.hpp"
+
+namespace ZeroTier {
+
+#ifdef ZT_TRACE
+static void ZT_LOCAL_TRACE(void *const tPtr,const RuntimeEnvironment *const RR,const char *const fmt,...)
+{
+ char traceMsgBuf[1024];
+ va_list ap;
+ va_start(ap,fmt);
+ vsnprintf(traceMsgBuf,sizeof(traceMsgBuf),fmt,ap);
+ va_end(ap);
+ traceMsgBuf[sizeof(traceMsgBuf) - 1] = (char)0;
+ RR->node->postEvent(tPtr,ZT_EVENT_TRACE,traceMsgBuf);
+}
+#else
+#define ZT_LOCAL_TRACE(...)
+#endif
+
+void Trace::resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"RESET and revalidate paths in scope %d; new phy address %s reported by trusted peer %.10llx",(int)scope,myPhysicalAddress.toIpString(tmp),reporter.toInt());
+
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,reporter);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,reporterPhysicalAddress.toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR,myPhysicalAddress.toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__IP_SCOPE,(uint64_t)scope);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ _spamToAllNetworks(tPtr,d,Trace::LEVEL_NORMAL);
+}
+
+void Trace::peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb)
+{
+ char tmp[128];
+ if (!path) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"trying unknown path %s to %.10llx (packet %.16llx verb %d local socket %lld network %.16llx)",path->address().toString(tmp),peer.address().toInt(),packetId,(double)verb,path->localSocket(),networkId);
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId)
+{
+ char tmp[128];
+ if (!newPath) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"learned new path %s to %.10llx (packet %.16llx local socket %lld network %.16llx)",newPath->address().toString(tmp),peer.address().toInt(),packetId,newPath->localSocket(),networkId);
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath)
+{
+ char tmp[128];
+ if (!newPath) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"explicit redirect from %.10llx to path %s",peer.address().toInt(),newPath->address().toString(tmp));
+
+ std::pair<Address,Trace::Level> byn;
+ if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S);
+ if (networkId)
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address());
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket());
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason)
+{
+#ifdef ZT_TRACE
+ char tmp[128],tmp2[128];
+#endif
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROP frame %s -> %s etherType %.4x size %u (%s)",network->id(),sourceMac.toString(tmp),destMac.toString(tmp2),etherType,frameLen,(reason) ? reason : "unknown reason");
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
+ d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested)
+{
+ char tmp[128];
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DENIED packet from %.10llx(%s) verb %d size %u%s",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength,credentialsRequested ? " (credentials requested)" : " (credentials not requested)");
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason)
+{
+ char tmp[128];
+ if (!network) return; // sanity check
+
+ ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROPPED frame from %.10llx(%s) verb %d size %u",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength);
+
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"MAC failed for packet %.16llx from %.10llx(%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"INVALID packet %.16llx from %.10llx(%s) (%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "unknown reason");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops);
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason)
+{
+ char tmp[128];
+
+ ZT_LOCAL_TRACE(tPtr,RR,"DROPPED HELLO from %.10llx(%s) (%s)",source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "???");
+
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId);
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source);
+ if (path) {
+ d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp));
+ d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket());
+ }
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller)
+{
+ ZT_LOCAL_TRACE(tPtr,RR,"requesting configuration for network %.16llx",network.id());
+ if ((_globalTarget)&&((int)_globalLevel >= Trace::LEVEL_DEBUG)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID,controller);
+ _send(tPtr,d,_globalTarget);
+ }
+}
+
+void Trace::networkFilter(
+ void *const tPtr,
+ const Network &network,
+ const RuleResultLog &primaryRuleSetLog,
+ const RuleResultLog *const matchingCapabilityRuleSetLog,
+ const Capability *const matchingCapability,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const bool noTee,
+ const bool inbound,
+ const int accept)
+{
+ std::pair<Address,Trace::Level> byn;
+ { Mutex::Lock l(_byNet_m); _byNet.get(network.id(),byn); }
+
+ if ( ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES)) || ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES)) ) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR,ztSource);
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR,ztDest);
+ d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,macSource.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,macDest.toInt());
+ d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType);
+ d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId);
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE,noTee ? "1" : "0");
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND,inbound ? "1" : "0");
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_RESULT,(int64_t)accept);
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG,(const char *)primaryRuleSetLog.data(),(int)primaryRuleSetLog.sizeBytes());
+ if (matchingCapabilityRuleSetLog)
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG,(const char *)matchingCapabilityRuleSetLog->data(),(int)matchingCapabilityRuleSetLog->sizeBytes());
+ if (matchingCapability)
+ d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID,(uint64_t)matchingCapability->id());
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen);
+ if (frameLen > 0)
+ d.add(ZT_REMOTE_TRACE_FIELD__FRAME_DATA,(const char *)frameData,(frameLen > 256) ? (int)256 : (int)frameLen);
+
+ if ((_globalTarget)&&((int)_globalLevel >= (int)Trace::LEVEL_RULES))
+ _send(tPtr,d,_globalTarget);
+ if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES))
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Capability &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Tag &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO,(uint64_t)c.value());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::credentialRejected(void *const tPtr,const Revocation &c,const char *reason)
+{
+ std::pair<Address,Trace::Level> byn;
+ if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); }
+
+ if ((_globalTarget)||(byn.first)) {
+ Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> d;
+ d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S);
+ d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id());
+ d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET,c.target());
+ if (reason)
+ d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason);
+
+ if (_globalTarget)
+ _send(tPtr,d,_globalTarget);
+ if (byn.first)
+ _send(tPtr,d,byn.first);
+ }
+}
+
+void Trace::updateMemoizedSettings()
+{
+ _globalTarget = RR->node->remoteTraceTarget();
+ _globalLevel = RR->node->remoteTraceLevel();
+ const std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
+ {
+ Mutex::Lock l(_byNet_m);
+ _byNet.clear();
+ for(std::vector< SharedPtr<Network> >::const_iterator n(nws.begin());n!=nws.end();++n) {
+ const Address dest((*n)->config().remoteTraceTarget);
+ if (dest) {
+ std::pair<Address,Trace::Level> &m = _byNet[(*n)->id()];
+ m.first = dest;
+ m.second = (*n)->config().remoteTraceLevel;
+ }
+ }
+ }
+}
+
+void Trace::_send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest)
+{
+ Packet outp(dest,RR->identity.address(),Packet::VERB_REMOTE_TRACE);
+ outp.appendCString(d.data());
+ outp.compress();
+ RR->sw->send(tPtr,outp,true);
+}
+
+void Trace::_spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level)
+{
+ Mutex::Lock l(_byNet_m);
+ Hashtable< uint64_t,std::pair< Address,Trace::Level > >::Iterator i(_byNet);
+ uint64_t *k = (uint64_t *)0;
+ std::pair<Address,Trace::Level> *v = (std::pair<Address,Trace::Level> *)0;
+ while (i.next(k,v)) {
+ if ((v)&&(v->first)&&((int)v->second >= (int)level))
+ _send(tPtr,d,v->first);
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Trace.hpp b/node/Trace.hpp
new file mode 100644
index 00000000..2a2fca6c
--- /dev/null
+++ b/node/Trace.hpp
@@ -0,0 +1,176 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TRACE_HPP
+#define ZT_TRACE_HPP
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../include/ZeroTierOne.h"
+
+#include "Constants.hpp"
+#include "SharedPtr.hpp"
+#include "Packet.hpp"
+#include "Credential.hpp"
+#include "InetAddress.hpp"
+#include "Dictionary.hpp"
+#include "Mutex.hpp"
+#include "Hashtable.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class Address;
+class Identity;
+class Peer;
+class Path;
+class Network;
+class NetworkConfig;
+class MAC;
+class CertificateOfMembership;
+class CertificateOfOwnership;
+class Revocation;
+class Tag;
+class Capability;
+
+/**
+ * Remote tracing and trace logging handler
+ */
+class Trace
+{
+public:
+ /**
+ * Trace verbosity level
+ */
+ enum Level
+ {
+ LEVEL_NORMAL = 0,
+ LEVEL_VERBOSE = 10,
+ LEVEL_RULES = 15,
+ LEVEL_DEBUG = 20,
+ LEVEL_INSANE = 30
+ };
+
+ /**
+ * Filter rule evaluation result log
+ *
+ * Each rule in a rule set gets a four-bit log entry. A log entry
+ * of zero means not evaluated. Otherwise each four-bit log entry
+ * contains two two-bit values of 01 for 'false' and 10 for 'true'.
+ * As with four-bit rules an 00 value here means this was not
+ * evaluated or was not relevant.
+ */
+ class RuleResultLog
+ {
+ public:
+ RuleResultLog() {}
+
+ inline void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches)
+ {
+ _l[rn >> 1] |= ( ((thisRuleMatches + 1) << 2) | (thisSetMatches + 1) ) << ((rn & 1) << 2);
+ }
+ inline void logSkipped(const unsigned int rn,const uint8_t thisSetMatches)
+ {
+ _l[rn >> 1] |= (thisSetMatches + 1) << ((rn & 1) << 2);
+ }
+
+ inline void clear()
+ {
+ memset(_l,0,sizeof(_l));
+ }
+
+ inline const uint8_t *data() const { return _l; }
+ inline unsigned int sizeBytes() const { return (ZT_MAX_NETWORK_RULES / 2); }
+
+ private:
+ uint8_t _l[ZT_MAX_NETWORK_RULES / 2];
+ };
+
+ Trace(const RuntimeEnvironment *renv) :
+ RR(renv),
+ _byNet(8)
+ {
+ }
+
+ void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope);
+
+ void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb);
+ void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId);
+ void peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath);
+
+ void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason);
+ void incomingPacketInvalid(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason);
+ void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr<Path> &path,const uint64_t packetId,const Address &source,const char *reason);
+
+ void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason);
+ void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested);
+ void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr<Network> &network,const SharedPtr<Path> &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason);
+
+ void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller);
+ void networkFilter(
+ void *const tPtr,
+ const Network &network,
+ const RuleResultLog &primaryRuleSetLog,
+ const RuleResultLog *const matchingCapabilityRuleSetLog,
+ const Capability *const matchingCapability,
+ const Address &ztSource,
+ const Address &ztDest,
+ const MAC &macSource,
+ const MAC &macDest,
+ const uint8_t *const frameData,
+ const unsigned int frameLen,
+ const unsigned int etherType,
+ const unsigned int vlanId,
+ const bool noTee,
+ const bool inbound,
+ const int accept);
+
+ void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason);
+ void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Capability &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Tag &c,const char *reason);
+ void credentialRejected(void *const tPtr,const Revocation &c,const char *reason);
+
+ void updateMemoizedSettings();
+
+private:
+ const RuntimeEnvironment *const RR;
+
+ void _send(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Address &dest);
+ void _spamToAllNetworks(void *const tPtr,const Dictionary<ZT_MAX_REMOTE_TRACE_SIZE> &d,const Level level);
+
+ Address _globalTarget;
+ Trace::Level _globalLevel;
+ Hashtable< uint64_t,std::pair< Address,Trace::Level > > _byNet;
+ Mutex _byNet_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Utils.cpp b/node/Utils.cpp
index 2d9515ee..a69a575e 100644
--- a/node/Utils.cpp
+++ b/node/Utils.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -47,96 +55,34 @@ namespace ZeroTier {
const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
-static void _Utils_doBurn(char *ptr,unsigned int len)
-{
- for(unsigned int i=0;i<len;++i)
- ptr[i] = (char)0;
-}
-void (*volatile _Utils_doBurn_ptr)(char *,unsigned int) = _Utils_doBurn;
-void Utils::burn(void *ptr,unsigned int len)
- throw()
+// Crazy hack to force memory to be securely zeroed in spite of the best efforts of optimizing compilers.
+static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len)
{
- // Ridiculous hack: call _doBurn() via a volatile function pointer to
- // hold down compiler optimizers and beat them mercilessly until they
- // cry and mumble something about never eliding secure memory zeroing
- // again.
- (_Utils_doBurn_ptr)((char *)ptr,len);
+ volatile uint8_t *const end = ptr + len;
+ while (ptr != end) *(ptr++) = (uint8_t)0;
}
+static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn;
+void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); }
-std::string Utils::hex(const void *data,unsigned int len)
+static unsigned long _Utils_itoa(unsigned long n,char *s)
{
- std::string r;
- r.reserve(len * 2);
- for(unsigned int i=0;i<len;++i) {
- r.push_back(HEXCHARS[(((const unsigned char *)data)[i] & 0xf0) >> 4]);
- r.push_back(HEXCHARS[((const unsigned char *)data)[i] & 0x0f]);
- }
- return r;
+ if (n == 0)
+ return 0;
+ unsigned long pos = _Utils_itoa(n / 10,s);
+ if (pos >= 22) // sanity check, should be impossible
+ pos = 22;
+ s[pos] = '0' + (char)(n % 10);
+ return pos + 1;
}
-
-std::string Utils::unhex(const char *hex,unsigned int maxlen)
+char *Utils::decimal(unsigned long n,char s[24])
{
- int n = 1;
- unsigned char c,b = 0;
- const char *eof = hex + maxlen;
- std::string r;
-
- if (!maxlen)
- return r;
-
- while ((c = (unsigned char)*(hex++))) {
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1))
- r.push_back((char)(b | (c - 48)));
- else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1))
- r.push_back((char)(b | (c - (65 - 10))));
- else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1))
- r.push_back((char)(b | (c - (97 - 10))));
- else b = (c - (97 - 10)) << 4;
- }
- if (hex == eof)
- break;
+ if (n == 0) {
+ s[0] = '0';
+ s[1] = (char)0;
+ return s;
}
-
- return r;
-}
-
-unsigned int Utils::unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len)
-{
- int n = 1;
- unsigned char c,b = 0;
- unsigned int l = 0;
- const char *eof = hex + maxlen;
-
- if (!maxlen)
- return 0;
-
- while ((c = (unsigned char)*(hex++))) {
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - 48));
- } else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (65 - 10)));
- } else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
- } else b = (c - (97 - 10)) << 4;
- }
- if (hex == eof)
- break;
- }
-
- return l;
+ s[_Utils_itoa(n,s)] = (char)0;
+ return s;
}
void Utils::getSecureRandom(void *buf,unsigned int bytes)
@@ -144,6 +90,8 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
static Mutex globalLock;
static Salsa20 s20;
static bool s20Initialized = false;
+ static uint8_t randomBuf[65536];
+ static unsigned int randomPtr = sizeof(randomBuf);
Mutex::Lock _l(globalLock);
@@ -161,34 +109,39 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
s20Key[1] = (uint64_t)buf; // address of buf
s20Key[2] = (uint64_t)s20Key; // address of s20Key[]
s20Key[3] = (uint64_t)&s20; // address of s20
- s20.init(s20Key,256,s20Key);
+ s20.init(s20Key,s20Key);
}
#ifdef __WINDOWS__
static HCRYPTPROV cryptProvider = NULL;
- if (cryptProvider == NULL) {
- if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
- fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
- exit(1);
- return;
+ for(unsigned int i=0;i<bytes;++i) {
+ if (randomPtr >= sizeof(randomBuf)) {
+ if (cryptProvider == NULL) {
+ if (!CryptAcquireContextA(&cryptProvider,NULL,NULL,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+ fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to obtain WinCrypt context!\r\n");
+ exit(1);
+ }
+ }
+ if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(randomBuf),(BYTE *)randomBuf)) {
+ fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
+ exit(1);
+ }
+ randomPtr = 0;
+ s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
+ s20.init(randomBuf,randomBuf);
}
- }
- if (!CryptGenRandom(cryptProvider,(DWORD)bytes,(BYTE *)buf)) {
- fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
- exit(1);
+ ((uint8_t *)buf)[i] = randomBuf[randomPtr++];
}
#else // not __WINDOWS__
- static char randomBuf[131072];
- static unsigned int randomPtr = sizeof(randomBuf);
static int devURandomFd = -1;
- if (devURandomFd <= 0) {
+ if (devURandomFd < 0) {
devURandomFd = ::open("/dev/urandom",O_RDONLY);
- if (devURandomFd <= 0) {
+ if (devURandomFd < 0) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
exit(1);
return;
@@ -201,7 +154,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
::close(devURandomFd);
devURandomFd = ::open("/dev/urandom",O_RDONLY);
- if (devURandomFd <= 0) {
+ if (devURandomFd < 0) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
exit(1);
return;
@@ -209,93 +162,13 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
} else break;
}
randomPtr = 0;
+ s20.crypt12(randomBuf,randomBuf,sizeof(randomBuf));
+ s20.init(randomBuf,randomBuf);
}
- ((char *)buf)[i] = randomBuf[randomPtr++];
+ ((uint8_t *)buf)[i] = randomBuf[randomPtr++];
}
#endif // __WINDOWS__ or not
-
- s20.encrypt12(buf,buf,bytes);
-}
-
-std::vector<std::string> Utils::split(const char *s,const char *const sep,const char *esc,const char *quot)
-{
- std::vector<std::string> fields;
- std::string buf;
-
- if (!esc)
- esc = "";
- if (!quot)
- quot = "";
-
- bool escapeState = false;
- char quoteState = 0;
- while (*s) {
- if (escapeState) {
- escapeState = false;
- buf.push_back(*s);
- } else if (quoteState) {
- if (*s == quoteState) {
- quoteState = 0;
- fields.push_back(buf);
- buf.clear();
- } else buf.push_back(*s);
- } else {
- const char *quotTmp;
- if (strchr(esc,*s))
- escapeState = true;
- else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
- quoteState = *quotTmp;
- else if (strchr(sep,*s)) {
- if (buf.size() > 0) {
- fields.push_back(buf);
- buf.clear();
- } // else skip runs of seperators
- } else buf.push_back(*s);
- }
- ++s;
- }
-
- if (buf.size())
- fields.push_back(buf);
-
- return fields;
-}
-
-bool Utils::scopy(char *dest,unsigned int len,const char *src)
-{
- if (!len)
- return false; // sanity check
- if (!src) {
- *dest = (char)0;
- return true;
- }
- char *end = dest + len;
- while ((*dest++ = *src++)) {
- if (dest == end) {
- *(--dest) = (char)0;
- return false;
- }
- }
- return true;
-}
-
-unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error)
-{
- va_list ap;
-
- va_start(ap,fmt);
- int n = (int)vsnprintf(buf,len,fmt,ap);
- va_end(ap);
-
- if ((n >= (int)len)||(n < 0)) {
- if (len)
- buf[len - 1] = (char)0;
- throw std::length_error("buf[] overflow in Utils::snprintf");
- }
-
- return (unsigned int)n;
}
} // namespace ZeroTier
diff --git a/node/Utils.hpp b/node/Utils.hpp
index cfe56501..a24f2c9a 100644
--- a/node/Utils.hpp
+++ b/node/Utils.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_UTILS_HPP
@@ -32,6 +40,44 @@
#include "Constants.hpp"
+#ifdef __LINUX__
+//#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+#if 0
+#include <emmintrin.h>
+static inline void ZT_FAST_MEMCPY(void *a,const void *b,unsigned long k)
+{
+ char *aa = reinterpret_cast<char *>(a);
+ const char *bb = reinterpret_cast<const char *>(b);
+ while (k >= 64) {
+ __m128 t1 = _mm_loadu_ps(reinterpret_cast<const float *>(bb));
+ __m128 t2 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 16));
+ __m128 t3 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 32));
+ __m128 t4 = _mm_loadu_ps(reinterpret_cast<const float *>(bb + 48));
+ _mm_storeu_ps(reinterpret_cast<float *>(aa),t1);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 16),t2);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 32),t3);
+ _mm_storeu_ps(reinterpret_cast<float *>(aa + 48),t4);
+ bb += 64;
+ aa += 64;
+ k -= 64;
+ }
+ while (k >= 16) {
+ __m128 t1 = _mm_loadu_ps(reinterpret_cast<const float *>(bb));
+ _mm_storeu_ps(reinterpret_cast<float *>(aa),t1);
+ bb += 16;
+ aa += 16;
+ k -= 16;
+ }
+ for(unsigned long i=0;i<k;++i)
+ aa[i] = bb[i];
+}
+#else
+#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
+#endif
+#else
+#define ZT_FAST_MEMCPY(a,b,c) memcpy(a,b,c)
+#endif
+
namespace ZeroTier {
/**
@@ -59,46 +105,161 @@ public:
/**
* Securely zero memory, avoiding compiler optimizations and such
*/
- static void burn(void *ptr,unsigned int len)
- throw();
+ static void burn(void *ptr,unsigned int len);
/**
- * Convert binary data to hexadecimal
- *
- * @param data Data to convert to hex
- * @param len Length of data
- * @return Hexadecimal string
+ * @param n Number to convert
+ * @param s Buffer, at least 24 bytes in size
+ * @return String containing 'n' in base 10 form
*/
- static std::string hex(const void *data,unsigned int len);
- static inline std::string hex(const std::string &data) { return hex(data.data(),(unsigned int)data.length()); }
+ static char *decimal(unsigned long n,char s[24]);
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII code (non-hex chars are ignored, stops at zero or maxlen)
- * @param maxlen Maximum length of hex string buffer
- * @return Binary data
- */
- static std::string unhex(const char *hex,unsigned int maxlen);
- static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str(),(unsigned int)hex.length()); }
+ static inline char *hex(uint64_t i,char s[17])
+ {
+ s[0] = HEXCHARS[(i >> 60) & 0xf];
+ s[1] = HEXCHARS[(i >> 56) & 0xf];
+ s[2] = HEXCHARS[(i >> 52) & 0xf];
+ s[3] = HEXCHARS[(i >> 48) & 0xf];
+ s[4] = HEXCHARS[(i >> 44) & 0xf];
+ s[5] = HEXCHARS[(i >> 40) & 0xf];
+ s[6] = HEXCHARS[(i >> 36) & 0xf];
+ s[7] = HEXCHARS[(i >> 32) & 0xf];
+ s[8] = HEXCHARS[(i >> 28) & 0xf];
+ s[9] = HEXCHARS[(i >> 24) & 0xf];
+ s[10] = HEXCHARS[(i >> 20) & 0xf];
+ s[11] = HEXCHARS[(i >> 16) & 0xf];
+ s[12] = HEXCHARS[(i >> 12) & 0xf];
+ s[13] = HEXCHARS[(i >> 8) & 0xf];
+ s[14] = HEXCHARS[(i >> 4) & 0xf];
+ s[15] = HEXCHARS[i & 0xf];
+ s[16] = (char)0;
+ return s;
+ }
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII
- * @param maxlen Maximum length of hex string buffer
- * @param buf Buffer to fill
- * @param len Length of buffer
- * @return Number of characters actually written
- */
- static unsigned int unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len);
- static inline unsigned int unhex(const std::string &hex,void *buf,unsigned int len) { return unhex(hex.c_str(),(unsigned int)hex.length(),buf,len); }
+ static inline char *hex10(uint64_t i,char s[11])
+ {
+ s[0] = HEXCHARS[(i >> 36) & 0xf];
+ s[1] = HEXCHARS[(i >> 32) & 0xf];
+ s[2] = HEXCHARS[(i >> 28) & 0xf];
+ s[3] = HEXCHARS[(i >> 24) & 0xf];
+ s[4] = HEXCHARS[(i >> 20) & 0xf];
+ s[5] = HEXCHARS[(i >> 16) & 0xf];
+ s[6] = HEXCHARS[(i >> 12) & 0xf];
+ s[7] = HEXCHARS[(i >> 8) & 0xf];
+ s[8] = HEXCHARS[(i >> 4) & 0xf];
+ s[9] = HEXCHARS[i & 0xf];
+ s[10] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint32_t i,char s[9])
+ {
+ s[0] = HEXCHARS[(i >> 28) & 0xf];
+ s[1] = HEXCHARS[(i >> 24) & 0xf];
+ s[2] = HEXCHARS[(i >> 20) & 0xf];
+ s[3] = HEXCHARS[(i >> 16) & 0xf];
+ s[4] = HEXCHARS[(i >> 12) & 0xf];
+ s[5] = HEXCHARS[(i >> 8) & 0xf];
+ s[6] = HEXCHARS[(i >> 4) & 0xf];
+ s[7] = HEXCHARS[i & 0xf];
+ s[8] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint16_t i,char s[5])
+ {
+ s[0] = HEXCHARS[(i >> 12) & 0xf];
+ s[1] = HEXCHARS[(i >> 8) & 0xf];
+ s[2] = HEXCHARS[(i >> 4) & 0xf];
+ s[3] = HEXCHARS[i & 0xf];
+ s[4] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(uint8_t i,char s[3])
+ {
+ s[0] = HEXCHARS[(i >> 4) & 0xf];
+ s[1] = HEXCHARS[i & 0xf];
+ s[2] = (char)0;
+ return s;
+ }
+
+ static inline char *hex(const void *d,unsigned int l,char *s)
+ {
+ char *const save = s;
+ for(unsigned int i=0;i<l;++i) {
+ const unsigned int b = reinterpret_cast<const uint8_t *>(d)[i];
+ *(s++) = HEXCHARS[b >> 4];
+ *(s++) = HEXCHARS[b & 0xf];
+ }
+ *s = (char)0;
+ return save;
+ }
+
+ static inline unsigned int unhex(const char *h,void *buf,unsigned int buflen)
+ {
+ unsigned int l = 0;
+ while (l < buflen) {
+ uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ uint8_t c = 0;
+ if ((hc >= 48)&&(hc <= 57)) // 0..9
+ c = hc - 48;
+ else if ((hc >= 97)&&(hc <= 102)) // a..f
+ c = hc - 87;
+ else if ((hc >= 65)&&(hc <= 70)) // A..F
+ c = hc - 55;
+
+ hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ c <<= 4;
+ if ((hc >= 48)&&(hc <= 57))
+ c |= hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c |= hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c |= hc - 55;
+
+ reinterpret_cast<uint8_t *>(buf)[l++] = c;
+ }
+ return l;
+ }
+
+ static inline unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen)
+ {
+ unsigned int l = 0;
+ const char *hend = h + hlen;
+ while (l < buflen) {
+ if (h == hend) break;
+ uint8_t hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ uint8_t c = 0;
+ if ((hc >= 48)&&(hc <= 57))
+ c = hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c = hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c = hc - 55;
+
+ if (h == hend) break;
+ hc = *(reinterpret_cast<const uint8_t *>(h++));
+ if (!hc) break;
+
+ c <<= 4;
+ if ((hc >= 48)&&(hc <= 57))
+ c |= hc - 48;
+ else if ((hc >= 97)&&(hc <= 102))
+ c |= hc - 87;
+ else if ((hc >= 65)&&(hc <= 70))
+ c |= hc - 55;
+
+ reinterpret_cast<uint8_t *>(buf)[l++] = c;
+ }
+ return l;
+ }
/**
* Generate secure random bytes
@@ -112,17 +273,6 @@ public:
static void getSecureRandom(void *buf,unsigned int bytes);
/**
- * Split a string by delimiter, with optional escape and quote characters
- *
- * @param s String to split
- * @param sep One or more separators
- * @param esc Zero or more escape characters
- * @param quot Zero or more quote characters
- * @return Vector of tokens
- */
- static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
-
- /**
* Tokenize a string (alias for strtok_r or strtok_s depending on platform)
*
* @param str String to split
@@ -130,7 +280,6 @@ public:
* @param saveptr Pointer to a char * for temporary reentrant storage
*/
static inline char *stok(char *str,const char *delim,char **saveptr)
- throw()
{
#ifdef __WINDOWS__
return strtok_s(str,delim,saveptr);
@@ -139,30 +288,11 @@ public:
#endif
}
- // String to number converters -- defined here to permit portability
- // ifdefs for platforms that lack some of the strtoXX functions.
- static inline unsigned int strToUInt(const char *s)
- throw()
- {
- return (unsigned int)strtoul(s,(char **)0,10);
- }
- static inline int strToInt(const char *s)
- throw()
- {
- return (int)strtol(s,(char **)0,10);
- }
- static inline unsigned long strToULong(const char *s)
- throw()
- {
- return strtoul(s,(char **)0,10);
- }
- static inline long strToLong(const char *s)
- throw()
- {
- return strtol(s,(char **)0,10);
- }
+ static inline unsigned int strToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,10); }
+ static inline int strToInt(const char *s) { return (int)strtol(s,(char **)0,10); }
+ static inline unsigned long strToULong(const char *s) { return strtoul(s,(char **)0,10); }
+ static inline long strToLong(const char *s) { return strtol(s,(char **)0,10); }
static inline unsigned long long strToU64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (unsigned long long)_strtoui64(s,(char **)0,10);
@@ -171,7 +301,6 @@ public:
#endif
}
static inline long long strTo64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (long long)_strtoi64(s,(char **)0,10);
@@ -179,28 +308,11 @@ public:
return strtoll(s,(char **)0,10);
#endif
}
- static inline unsigned int hexStrToUInt(const char *s)
- throw()
- {
- return (unsigned int)strtoul(s,(char **)0,16);
- }
- static inline int hexStrToInt(const char *s)
- throw()
- {
- return (int)strtol(s,(char **)0,16);
- }
- static inline unsigned long hexStrToULong(const char *s)
- throw()
- {
- return strtoul(s,(char **)0,16);
- }
- static inline long hexStrToLong(const char *s)
- throw()
- {
- return strtol(s,(char **)0,16);
- }
+ static inline unsigned int hexStrToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,16); }
+ static inline int hexStrToInt(const char *s) { return (int)strtol(s,(char **)0,16); }
+ static inline unsigned long hexStrToULong(const char *s) { return strtoul(s,(char **)0,16); }
+ static inline long hexStrToLong(const char *s) { return strtol(s,(char **)0,16); }
static inline unsigned long long hexStrToU64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (unsigned long long)_strtoui64(s,(char **)0,16);
@@ -209,7 +321,6 @@ public:
#endif
}
static inline long long hexStrTo64(const char *s)
- throw()
{
#ifdef __WINDOWS__
return (long long)_strtoi64(s,(char **)0,16);
@@ -217,11 +328,6 @@ public:
return strtoll(s,(char **)0,16);
#endif
}
- static inline double strToDouble(const char *s)
- throw()
- {
- return strtod(s,(char **)0);
- }
/**
* Perform a safe C string copy, ALWAYS null-terminating the result
@@ -234,22 +340,23 @@ public:
* @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
* @return True on success, false on overflow (buffer will still be 0-terminated)
*/
- static bool scopy(char *dest,unsigned int len,const char *src);
-
- /**
- * Variant of snprintf that is portable and throws an exception
- *
- * This just wraps the local implementation whatever it's called, while
- * performing a few other checks and adding exceptions for overflow.
- *
- * @param buf Buffer to write to
- * @param len Length of buffer in bytes
- * @param fmt Format string
- * @param ... Format arguments
- * @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
- */
- static unsigned int snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error);
+ static inline bool scopy(char *dest,unsigned int len,const char *src)
+ {
+ if (!len)
+ return false; // sanity check
+ if (!src) {
+ *dest = (char)0;
+ return true;
+ }
+ char *end = dest + len;
+ while ((*dest++ = *src++)) {
+ if (dest == end) {
+ *(--dest) = (char)0;
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Count the number of bits set in an integer
@@ -265,6 +372,20 @@ public:
}
/**
+ * Count the number of bits set in an integer
+ *
+ * @param v 64-bit integer
+ * @return Number of bits set in this integer (0-64)
+ */
+ static inline uint64_t countBits(uint64_t v)
+ {
+ v = v - ((v >> 1) & (uint64_t)~(uint64_t)0/3);
+ v = (v & (uint64_t)~(uint64_t)0/15*3) + ((v >> 2) & (uint64_t)~(uint64_t)0/15*3);
+ v = (v + (v >> 4)) & (uint64_t)~(uint64_t)0/255*15;
+ return (uint64_t)(v * ((uint64_t)~(uint64_t)0/255)) >> 56;
+ }
+
+ /**
* Check if a memory buffer is all-zero
*
* @param p Memory to scan
@@ -281,14 +402,13 @@ public:
}
// Byte swappers for big/little endian conversion
- static inline uint8_t hton(uint8_t n) throw() { return n; }
- static inline int8_t hton(int8_t n) throw() { return n; }
- static inline uint16_t hton(uint16_t n) throw() { return htons(n); }
- static inline int16_t hton(int16_t n) throw() { return (int16_t)htons((uint16_t)n); }
- static inline uint32_t hton(uint32_t n) throw() { return htonl(n); }
- static inline int32_t hton(int32_t n) throw() { return (int32_t)htonl((uint32_t)n); }
+ static inline uint8_t hton(uint8_t n) { return n; }
+ static inline int8_t hton(int8_t n) { return n; }
+ static inline uint16_t hton(uint16_t n) { return htons(n); }
+ static inline int16_t hton(int16_t n) { return (int16_t)htons((uint16_t)n); }
+ static inline uint32_t hton(uint32_t n) { return htonl(n); }
+ static inline int32_t hton(int32_t n) { return (int32_t)htonl((uint32_t)n); }
static inline uint64_t hton(uint64_t n)
- throw()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if defined(__GNUC__) && (!defined(__OpenBSD__))
@@ -309,16 +429,15 @@ public:
return n;
#endif
}
- static inline int64_t hton(int64_t n) throw() { return (int64_t)hton((uint64_t)n); }
-
- static inline uint8_t ntoh(uint8_t n) throw() { return n; }
- static inline int8_t ntoh(int8_t n) throw() { return n; }
- static inline uint16_t ntoh(uint16_t n) throw() { return ntohs(n); }
- static inline int16_t ntoh(int16_t n) throw() { return (int16_t)ntohs((uint16_t)n); }
- static inline uint32_t ntoh(uint32_t n) throw() { return ntohl(n); }
- static inline int32_t ntoh(int32_t n) throw() { return (int32_t)ntohl((uint32_t)n); }
+ static inline int64_t hton(int64_t n) { return (int64_t)hton((uint64_t)n); }
+
+ static inline uint8_t ntoh(uint8_t n) { return n; }
+ static inline int8_t ntoh(int8_t n) { return n; }
+ static inline uint16_t ntoh(uint16_t n) { return ntohs(n); }
+ static inline int16_t ntoh(int16_t n) { return (int16_t)ntohs((uint16_t)n); }
+ static inline uint32_t ntoh(uint32_t n) { return ntohl(n); }
+ static inline int32_t ntoh(int32_t n) { return (int32_t)ntohl((uint32_t)n); }
static inline uint64_t ntoh(uint64_t n)
- throw()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
#if defined(__GNUC__) && !defined(__OpenBSD__)
@@ -339,34 +458,7 @@ public:
return n;
#endif
}
- static inline int64_t ntoh(int64_t n) throw() { return (int64_t)ntoh((uint64_t)n); }
-
- /**
- * Compare Peer version tuples
- *
- * @return -1, 0, or 1 based on whether first tuple is less than, equal to, or greater than second
- */
- static inline int compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int maj2,unsigned int min2,unsigned int rev2)
- throw()
- {
- if (maj1 > maj2)
- return 1;
- else if (maj1 < maj2)
- return -1;
- else {
- if (min1 > min2)
- return 1;
- else if (min1 < min2)
- return -1;
- else {
- if (rev1 > rev2)
- return 1;
- else if (rev1 < rev2)
- return -1;
- else return 0;
- }
- }
- }
+ static inline int64_t ntoh(int64_t n) { return (int64_t)ntoh((uint64_t)n); }
/**
* Hexadecimal characters 0-f
diff --git a/node/World.hpp b/node/World.hpp
index fdada2ad..9edf2a6a 100644
--- a/node/World.hpp
+++ b/node/World.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_WORLD_HPP
@@ -49,16 +57,6 @@
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
/**
- * World ID indicating null / empty World object
- */
-#define ZT_WORLD_ID_NULL 0
-
-/**
- * World ID for a test network with ephemeral or temporary roots
- */
-#define ZT_WORLD_ID_TESTNET 1
-
-/**
* World ID for Earth
*
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
@@ -90,68 +88,85 @@ namespace ZeroTier {
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
* world ID for Mars and nearby space is defined but not yet used, and a test
* world ID is provided for testing purposes.
- *
- * If you absolutely must run your own "unofficial" ZeroTier network, please
- * define your world IDs above 0xffffffff (4294967295). Code to make a World
- * is in mkworld.cpp in the parent directory and must be edited to change
- * settings.
*/
class World
{
public:
+ /**
+ * World type -- do not change IDs
+ */
+ enum Type
+ {
+ TYPE_NULL = 0,
+ TYPE_PLANET = 1, // Planets, of which there is currently one (Earth)
+ TYPE_MOON = 127 // Moons, which are user-created and many
+ };
+
+ /**
+ * Upstream server definition in world/moon
+ */
struct Root
{
Identity identity;
std::vector<InetAddress> stableEndpoints;
- inline bool operator==(const Root &r) const throw() { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
- inline bool operator!=(const Root &r) const throw() { return (!(*this == r)); }
- inline bool operator<(const Root &r) const throw() { return (identity < r.identity); } // for sorting
+ inline bool operator==(const Root &r) const { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
+ inline bool operator!=(const Root &r) const { return (!(*this == r)); }
+ inline bool operator<(const Root &r) const { return (identity < r.identity); } // for sorting
};
/**
* Construct an empty / null World
*/
World() :
- _id(ZT_WORLD_ID_NULL),
- _ts(0) {}
+ _id(0),
+ _ts(0),
+ _type(TYPE_NULL) {}
/**
* @return Root servers for this world and their stable endpoints
*/
- inline const std::vector<World::Root> &roots() const throw() { return _roots; }
+ inline const std::vector<World::Root> &roots() const { return _roots; }
+
+ /**
+ * @return World type: planet or moon
+ */
+ inline Type type() const { return _type; }
/**
* @return World unique identifier
*/
- inline uint64_t id() const throw() { return _id; }
+ inline uint64_t id() const { return _id; }
/**
* @return World definition timestamp
*/
- inline uint64_t timestamp() const throw() { return _ts; }
+ inline uint64_t timestamp() const { return _ts; }
+
+ /**
+ * @return C25519 signature
+ */
+ inline const C25519::Signature &signature() const { return _signature; }
+
+ /**
+ * @return Public key that must sign next update
+ */
+ inline const C25519::Public &updatesMustBeSignedBy() const { return _updatesMustBeSignedBy; }
/**
* Check whether a world update should replace this one
*
- * A new world update is valid if it is for the same world ID, is newer,
- * and is signed by the current world's signing key. If this world object
- * is null, it can always be updated.
- *
* @param update Candidate update
- * @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip)
- * @return True if update is newer than current and is properly signed
+ * @return True if update is newer than current, matches its ID and type, and is properly signed (or if current is NULL)
*/
- inline bool shouldBeReplacedBy(const World &update,bool fullSignatureCheck)
+ inline bool shouldBeReplacedBy(const World &update)
{
- if (_id == ZT_WORLD_ID_NULL)
+ if ((_id == 0)||(_type == TYPE_NULL))
return true;
- if ((_id == update._id)&&(_ts < update._ts)) {
- if (fullSignatureCheck) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
- update.serialize(tmp,true);
- return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
- } else return true;
+ if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+ update.serialize(tmp,true);
+ return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
}
return false;
}
@@ -159,17 +174,17 @@ public:
/**
* @return True if this World is non-empty
*/
- inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); }
+ inline operator bool() const { return (_type != TYPE_NULL); }
template<unsigned int C>
inline void serialize(Buffer<C> &b,bool forSign = false) const
{
- if (forSign)
- b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
- b.append((uint8_t)0x01); // version -- only one valid value for now
+ if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+ b.append((uint8_t)_type);
b.append((uint64_t)_id);
b.append((uint64_t)_ts);
- b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN);
+ b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
if (!forSign)
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
b.append((uint8_t)_roots.size());
@@ -179,8 +194,10 @@ public:
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
ep->serialize(b);
}
- if (forSign)
- b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
+ if (_type == TYPE_MOON)
+ b.append((uint16_t)0); // no attached dictionary (for future use)
+
+ if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
}
template<unsigned int C>
@@ -190,39 +207,74 @@ public:
_roots.clear();
- if (b[p++] != 0x01)
- throw std::invalid_argument("invalid World serialized version");
+ switch((Type)b[p++]) {
+ case TYPE_NULL: _type = TYPE_NULL; break; // shouldn't ever really happen in serialized data but it's not invalid
+ case TYPE_PLANET: _type = TYPE_PLANET; break;
+ case TYPE_MOON: _type = TYPE_MOON; break;
+ default:
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
+ }
_id = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
- memcpy(_updateSigningKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
- memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
- unsigned int numRoots = b[p++];
+ ZT_FAST_MEMCPY(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
+ ZT_FAST_MEMCPY(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+ const unsigned int numRoots = (unsigned int)b[p++];
if (numRoots > ZT_WORLD_MAX_ROOTS)
- throw std::invalid_argument("too many roots in World");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int k=0;k<numRoots;++k) {
_roots.push_back(Root());
Root &r = _roots.back();
p += r.identity.deserialize(b,p);
unsigned int numStableEndpoints = b[p++];
if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)
- throw std::invalid_argument("too many stable endpoints in World/Root");
+ throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
r.stableEndpoints.push_back(InetAddress());
p += r.stableEndpoints.back().deserialize(b,p);
}
}
+ if (_type == TYPE_MOON)
+ p += b.template at<uint16_t>(p) + 2;
return (p - startAt);
}
- inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updateSigningKey == w._updateSigningKey)&&(_signature == w._signature)&&(_roots == w._roots)); }
- inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
+ inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(memcmp(_updatesMustBeSignedBy.data,w._updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)&&(memcmp(_signature.data,w._signature.data,ZT_C25519_SIGNATURE_LEN) == 0)&&(_roots == w._roots)&&(_type == w._type)); }
+ inline bool operator!=(const World &w) const { return (!(*this == w)); }
+
+ /**
+ * Create a World object signed with a key pair
+ *
+ * @param t World type
+ * @param id World ID
+ * @param ts World timestamp / revision
+ * @param sk Key that must be used to sign the next future update to this world
+ * @param roots Roots and their stable endpoints
+ * @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
+ * @return Signed World object
+ */
+ static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
+ {
+ World w;
+ w._id = id;
+ w._ts = ts;
+ w._type = t;
+ w._updatesMustBeSignedBy = sk;
+ w._roots = roots;
+
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+ w.serialize(tmp,true);
+ w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
+
+ return w;
+ }
protected:
uint64_t _id;
uint64_t _ts;
- C25519::Public _updateSigningKey;
+ Type _type;
+ C25519::Public _updatesMustBeSignedBy;
C25519::Signature _signature;
std::vector<Root> _roots;
};
diff --git a/objects.mk b/objects.mk
index 4a7a36a8..d4a3c16c 100644
--- a/objects.mk
+++ b/objects.mk
@@ -1,11 +1,12 @@
-OBJS=\
+CORE_OBJS=\
node/C25519.o \
+ node/Capability.o \
node/CertificateOfMembership.o \
- node/Cluster.o \
- node/DeferredPackets.o \
+ node/CertificateOfOwnership.o \
node/Identity.o \
node/IncomingPacket.o \
node/InetAddress.o \
+ node/Membership.o \
node/Multicaster.o \
node/Network.o \
node/NetworkConfig.o \
@@ -15,15 +16,24 @@ OBJS=\
node/Path.o \
node/Peer.o \
node/Poly1305.o \
+ node/Revocation.o \
node/Salsa20.o \
node/SelfAwareness.o \
node/SHA512.o \
node/Switch.o \
+ node/Tag.o \
node/Topology.o \
- node/Utils.o \
- osdep/BackgroundResolver.o \
+ node/Trace.o \
+ node/Utils.o
+
+ONE_OBJS=\
+ controller/EmbeddedNetworkController.o \
+ controller/DB.o \
+ controller/FileDB.o \
+ controller/RethinkDB.o \
osdep/ManagedRoute.o \
osdep/Http.o \
osdep/OSUtils.o \
- service/ClusterGeoIpService.o \
- service/ControlPlane.o
+ service/SoftwareUpdater.o \
+ service/OneService.o
+
diff --git a/one.cpp b/one.cpp
index 9f7a0a29..9ebc83c1 100644
--- a/one.cpp
+++ b/one.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -43,31 +51,41 @@
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/uio.h>
+#include <dirent.h>
#include <signal.h>
+#ifdef __LINUX__
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <linux/capability.h>
+#include <linux/securebits.h>
+#endif
#endif
#include <string>
#include <stdexcept>
+#include <iostream>
+#include <sstream>
#include "version.h"
#include "include/ZeroTierOne.h"
-#ifdef ZT_USE_SYSTEM_JSON_PARSER
-#include <json-parser/json.h>
-#else
-#include "ext/json-parser/json.h"
-#endif
-
#include "node/Identity.hpp"
#include "node/CertificateOfMembership.hpp"
#include "node/Utils.hpp"
#include "node/NetworkController.hpp"
+#include "node/Buffer.hpp"
+#include "node/World.hpp"
#include "osdep/OSUtils.hpp"
#include "osdep/Http.hpp"
+#include "osdep/Thread.hpp"
#include "service/OneService.hpp"
+#include "ext/json/json.hpp"
+
#define ZT_PID_PATH "zerotier-one.pid"
using namespace ZeroTier;
@@ -75,7 +93,7 @@ using namespace ZeroTier;
static OneService *volatile zt1Service = (OneService *)0;
#define PROGRAM_NAME "ZeroTier One"
-#define COPYRIGHT_NOTICE "Copyright © 2011–2016 ZeroTier, Inc."
+#define COPYRIGHT_NOTICE "Copyright (c) 2011-2018 ZeroTier, Inc."
#define LICENSE_GRANT \
"This is free software: you may copy, modify, and/or distribute this" ZT_EOL_S \
"work under the terms of the GNU General Public License, version 3 or" ZT_EOL_S \
@@ -91,9 +109,10 @@ static OneService *volatile zt1Service = (OneService *)0;
static void cliPrintHelp(const char *pn,FILE *out)
{
fprintf(out,
- "%s version %d.%d.%d" ZT_EOL_S,
+ "%s version %d.%d.%d build %d (platform %d arch %d)" ZT_EOL_S,
PROGRAM_NAME,
- ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION);
+ ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD,
+ ZT_BUILD_PLATFORM, ZT_BUILD_ARCHITECTURE);
fprintf(out,
COPYRIGHT_NOTICE ZT_EOL_S
LICENSE_GRANT ZT_EOL_S);
@@ -112,6 +131,10 @@ static void cliPrintHelp(const char *pn,FILE *out)
fprintf(out," join <network> - Join a network" ZT_EOL_S);
fprintf(out," leave <network> - Leave a network" ZT_EOL_S);
fprintf(out," set <network> <setting> - Set a network setting" ZT_EOL_S);
+ fprintf(out," get <network> <setting> - Get a network setting" ZT_EOL_S);
+ fprintf(out," listmoons - List moons (federated root sets)" ZT_EOL_S);
+ fprintf(out," orbit <world ID> <seed> - Join a moon via any member root" ZT_EOL_S);
+ fprintf(out," deorbit <world ID> - Leave a moon" ZT_EOL_S);
}
static std::string cliFixJsonCRs(const std::string &s)
@@ -238,9 +261,9 @@ static int cli(int argc,char **argv)
if (hd) {
char p[4096];
#ifdef __APPLE__
- Utils::snprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
+ OSUtils::ztsnprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
#else
- Utils::snprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
+ OSUtils::ztsnprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
#endif
OSUtils::readFile(p,authToken);
}
@@ -256,7 +279,7 @@ static int cli(int argc,char **argv)
InetAddress addr;
{
char addrtmp[256];
- Utils::snprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
+ OSUtils::ztsnprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
addr = InetAddress(addrtmp);
}
@@ -276,235 +299,174 @@ static int cli(int argc,char **argv)
responseHeaders,
responseBody);
if (scode == 200) {
- printf("%s",cliFixJsonCRs(responseBody).c_str());
+ printf("%s", cliFixJsonCRs(responseBody).c_str());
return 0;
} else {
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
return 1;
}
} else if ((command == "info")||(command == "status")) {
- unsigned int scode = Http::GET(
- 1024 * 1024 * 16,
- 60000,
- (const struct sockaddr *)&addr,
- "/status",
- requestHeaders,
- responseHeaders,
- responseBody);
+ const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody);
+
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
+ nlohmann::json j;
+ try {
+ j = OSUtils::jsonParse(responseBody);
+ } catch (std::exception &exc) {
+ printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
+ return 1;
+ } catch ( ... ) {
+ printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
+ return 1;
+ }
+
if (scode == 200) {
if (json) {
- printf("%s",cliFixJsonCRs(responseBody).c_str());
- return 0;
+ printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
- json_value *j = json_parse(responseBody.c_str(),responseBody.length());
- bool good = false;
- if (j) {
- if (j->type == json_object) {
- const char *address = (const char *)0;
- bool online = false;
- const char *version = (const char *)0;
- for(unsigned int k=0;k<j->u.object.length;++k) {
- if ((!strcmp(j->u.object.values[k].name,"address"))&&(j->u.object.values[k].value->type == json_string))
- address = j->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(j->u.object.values[k].name,"version"))&&(j->u.object.values[k].value->type == json_string))
- version = j->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(j->u.object.values[k].name,"online"))&&(j->u.object.values[k].value->type == json_boolean))
- online = (j->u.object.values[k].value->u.boolean != 0);
- }
- if ((address)&&(version)) {
- printf("200 info %s %s %s" ZT_EOL_S,address,(online ? "ONLINE" : "OFFLINE"),version);
- good = true;
- }
- }
- json_value_free(j);
- }
- if (good) {
- return 0;
- } else {
- printf("%u %s invalid JSON response" ZT_EOL_S,scode,command.c_str());
- return 1;
+ if (j.is_object()) {
+ printf("200 info %s %s %s" ZT_EOL_S,
+ OSUtils::jsonString(j["address"],"-").c_str(),
+ OSUtils::jsonString(j["version"],"-").c_str(),
+ ((j["tcpFallbackActive"]) ? "TUNNELED" : ((j["online"]) ? "ONLINE" : "OFFLINE")));
}
}
+ return 0;
} else {
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
return 1;
}
} else if (command == "listpeers") {
- unsigned int scode = Http::GET(
- 1024 * 1024 * 16,
- 60000,
- (const struct sockaddr *)&addr,
- "/peer",
- requestHeaders,
- responseHeaders,
- responseBody);
+ const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
+
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
+ nlohmann::json j;
+ try {
+ j = OSUtils::jsonParse(responseBody);
+ } catch (std::exception &exc) {
+ printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
+ return 1;
+ } catch ( ... ) {
+ printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
+ return 1;
+ }
+
if (scode == 200) {
if (json) {
- printf("%s",cliFixJsonCRs(responseBody).c_str());
- return 0;
+ printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
- printf("200 listpeers <ztaddr> <paths> <latency> <version> <role>" ZT_EOL_S);
- json_value *j = json_parse(responseBody.c_str(),responseBody.length());
- if (j) {
- if (j->type == json_array) {
- for(unsigned int p=0;p<j->u.array.length;++p) {
- json_value *jp = j->u.array.values[p];
- if (jp->type == json_object) {
- const char *address = (const char *)0;
- std::string paths;
- int64_t latency = 0;
- int64_t versionMajor = -1,versionMinor = -1,versionRev = -1;
- const char *role = (const char *)0;
- for(unsigned int k=0;k<jp->u.object.length;++k) {
- if ((!strcmp(jp->u.object.values[k].name,"address"))&&(jp->u.object.values[k].value->type == json_string))
- address = jp->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jp->u.object.values[k].name,"versionMajor"))&&(jp->u.object.values[k].value->type == json_integer))
- versionMajor = jp->u.object.values[k].value->u.integer;
- else if ((!strcmp(jp->u.object.values[k].name,"versionMinor"))&&(jp->u.object.values[k].value->type == json_integer))
- versionMinor = jp->u.object.values[k].value->u.integer;
- else if ((!strcmp(jp->u.object.values[k].name,"versionRev"))&&(jp->u.object.values[k].value->type == json_integer))
- versionRev = jp->u.object.values[k].value->u.integer;
- else if ((!strcmp(jp->u.object.values[k].name,"role"))&&(jp->u.object.values[k].value->type == json_string))
- role = jp->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jp->u.object.values[k].name,"latency"))&&(jp->u.object.values[k].value->type == json_integer))
- latency = jp->u.object.values[k].value->u.integer;
- else if ((!strcmp(jp->u.object.values[k].name,"paths"))&&(jp->u.object.values[k].value->type == json_array)) {
- for(unsigned int pp=0;pp<jp->u.object.values[k].value->u.array.length;++pp) {
- json_value *jpath = jp->u.object.values[k].value->u.array.values[pp];
- if (jpath->type == json_object) {
- const char *paddr = (const char *)0;
- int64_t lastSend = 0;
- int64_t lastReceive = 0;
- bool preferred = false;
- bool active = false;
- for(unsigned int kk=0;kk<jpath->u.object.length;++kk) {
- if ((!strcmp(jpath->u.object.values[kk].name,"address"))&&(jpath->u.object.values[kk].value->type == json_string))
- paddr = jpath->u.object.values[kk].value->u.string.ptr;
- else if ((!strcmp(jpath->u.object.values[kk].name,"lastSend"))&&(jpath->u.object.values[kk].value->type == json_integer))
- lastSend = jpath->u.object.values[kk].value->u.integer;
- else if ((!strcmp(jpath->u.object.values[kk].name,"lastReceive"))&&(jpath->u.object.values[kk].value->type == json_integer))
- lastReceive = jpath->u.object.values[kk].value->u.integer;
- else if ((!strcmp(jpath->u.object.values[kk].name,"preferred"))&&(jpath->u.object.values[kk].value->type == json_boolean))
- preferred = (jpath->u.object.values[kk].value->u.boolean != 0);
- else if ((!strcmp(jpath->u.object.values[kk].name,"active"))&&(jpath->u.object.values[kk].value->type == json_boolean))
- active = (jpath->u.object.values[kk].value->u.boolean != 0);
- }
- if ((paddr)&&(active)) {
- int64_t now = (int64_t)OSUtils::now();
- if (lastSend > 0)
- lastSend = now - lastSend;
- if (lastReceive > 0)
- lastReceive = now - lastReceive;
- char pathtmp[256];
- Utils::snprintf(pathtmp,sizeof(pathtmp),"%s;%lld;%lld;%s",
- paddr,
- lastSend,
- lastReceive,
- (preferred ? "preferred" : "active"));
- if (paths.length())
- paths.push_back(',');
- paths.append(pathtmp);
- }
- }
- }
- }
- }
- if ((address)&&(role)) {
- char verstr[64];
- if ((versionMajor >= 0)&&(versionMinor >= 0)&&(versionRev >= 0))
- Utils::snprintf(verstr,sizeof(verstr),"%lld.%lld.%lld",versionMajor,versionMinor,versionRev);
- else {
- verstr[0] = '-';
- verstr[1] = (char)0;
- }
- printf("200 listpeers %s %s %lld %s %s" ZT_EOL_S,address,(paths.length()) ? paths.c_str() : "-",(long long)latency,verstr,role);
+ printf("200 listpeers <ztaddr> <path> <latency> <version> <role>" ZT_EOL_S);
+ if (j.is_array()) {
+ for(unsigned long k=0;k<j.size();++k) {
+ nlohmann::json &p = j[k];
+ std::string bestPath;
+ nlohmann::json &paths = p["paths"];
+ if (paths.is_array()) {
+ for(unsigned long i=0;i<paths.size();++i) {
+ nlohmann::json &path = paths[i];
+ if (path["preferred"]) {
+ char tmp[256];
+ std::string addr = path["address"];
+ const int64_t now = OSUtils::now();
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s;%lld;%lld",addr.c_str(),now - (int64_t)path["lastSend"],now - (int64_t)path["lastReceive"]);
+ bestPath = tmp;
+ break;
}
}
}
+ if (bestPath.length() == 0) bestPath = "-";
+ char ver[128];
+ int64_t vmaj = p["versionMajor"];
+ int64_t vmin = p["versionMinor"];
+ int64_t vrev = p["versionRev"];
+ if (vmaj >= 0) {
+ OSUtils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
+ } else {
+ ver[0] = '-';
+ ver[1] = (char)0;
+ }
+ printf("200 listpeers %s %s %d %s %s" ZT_EOL_S,
+ OSUtils::jsonString(p["address"],"-").c_str(),
+ bestPath.c_str(),
+ (int)OSUtils::jsonInt(p["latency"],0),
+ ver,
+ OSUtils::jsonString(p["role"],"-").c_str());
}
- json_value_free(j);
}
- return 0;
}
+ return 0;
} else {
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
return 1;
}
} else if (command == "listnetworks") {
- unsigned int scode = Http::GET(
- 1024 * 1024 * 16,
- 60000,
- (const struct sockaddr *)&addr,
- "/network",
- requestHeaders,
- responseHeaders,
- responseBody);
+ const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
+
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
+ nlohmann::json j;
+ try {
+ j = OSUtils::jsonParse(responseBody);
+ } catch (std::exception &exc) {
+ printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
+ return 1;
+ } catch ( ... ) {
+ printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
+ return 1;
+ }
+
if (scode == 200) {
if (json) {
- printf("%s",cliFixJsonCRs(responseBody).c_str());
- return 0;
+ printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
} else {
printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S);
- json_value *j = json_parse(responseBody.c_str(),responseBody.length());
- if (j) {
- if (j->type == json_array) {
- for(unsigned int p=0;p<j->u.array.length;++p) {
- json_value *jn = j->u.array.values[p];
- if (jn->type == json_object) {
- const char *nwid = (const char *)0;
- const char *name = "";
- const char *mac = (const char *)0;
- const char *status = (const char *)0;
- const char *type = (const char *)0;
- const char *portDeviceName = "";
- std::string ips;
- for(unsigned int k=0;k<jn->u.object.length;++k) {
- if ((!strcmp(jn->u.object.values[k].name,"nwid"))&&(jn->u.object.values[k].value->type == json_string))
- nwid = jn->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jn->u.object.values[k].name,"name"))&&(jn->u.object.values[k].value->type == json_string))
- name = jn->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jn->u.object.values[k].name,"mac"))&&(jn->u.object.values[k].value->type == json_string))
- mac = jn->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jn->u.object.values[k].name,"status"))&&(jn->u.object.values[k].value->type == json_string))
- status = jn->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jn->u.object.values[k].name,"type"))&&(jn->u.object.values[k].value->type == json_string))
- type = jn->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jn->u.object.values[k].name,"portDeviceName"))&&(jn->u.object.values[k].value->type == json_string))
- portDeviceName = jn->u.object.values[k].value->u.string.ptr;
- else if ((!strcmp(jn->u.object.values[k].name,"assignedAddresses"))&&(jn->u.object.values[k].value->type == json_array)) {
- for(unsigned int a=0;a<jn->u.object.values[k].value->u.array.length;++a) {
- json_value *aa = jn->u.object.values[k].value->u.array.values[a];
- if (aa->type == json_string) {
- if (ips.length())
- ips.push_back(',');
- ips.append(aa->u.string.ptr);
- }
- }
+ if (j.is_array()) {
+ for(unsigned long i=0;i<j.size();++i) {
+ nlohmann::json &n = j[i];
+ if (n.is_object()) {
+ std::string aa;
+ nlohmann::json &assignedAddresses = n["assignedAddresses"];
+ if (assignedAddresses.is_array()) {
+ for(unsigned long j=0;j<assignedAddresses.size();++j) {
+ nlohmann::json &addr = assignedAddresses[j];
+ if (addr.is_string()) {
+ if (aa.length() > 0) aa.push_back(',');
+ aa.append(addr.get<std::string>());
}
}
- if ((nwid)&&(mac)&&(status)&&(type)) {
- printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
- nwid,
- (((name)&&(name[0])) ? name : "-"),
- mac,
- status,
- type,
- (((portDeviceName)&&(portDeviceName[0])) ? portDeviceName : "-"),
- ((ips.length() > 0) ? ips.c_str() : "-"));
- }
}
+ if (aa.length() == 0) aa = "-";
+ printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
+ OSUtils::jsonString(n["nwid"],"-").c_str(),
+ OSUtils::jsonString(n["name"],"-").c_str(),
+ OSUtils::jsonString(n["mac"],"-").c_str(),
+ OSUtils::jsonString(n["status"],"-").c_str(),
+ OSUtils::jsonString(n["type"],"-").c_str(),
+ OSUtils::jsonString(n["portDeviceName"],"-").c_str(),
+ aa.c_str());
}
}
- json_value_free(j);
}
}
+ return 0;
} else {
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
return 1;
}
} else if (command == "join") {
if (arg1.length() != 16) {
- cliPrintHelp(argv[0],stderr);
+ printf("invalid network id" ZT_EOL_S);
return 2;
}
requestHeaders["Content-Type"] = "application/json";
@@ -532,7 +494,7 @@ static int cli(int argc,char **argv)
}
} else if (command == "leave") {
if (arg1.length() != 16) {
- cliPrintHelp(argv[0],stderr);
+ printf("invalid network id" ZT_EOL_S);
return 2;
}
unsigned int scode = Http::DEL(
@@ -554,20 +516,94 @@ static int cli(int argc,char **argv)
printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
return 1;
}
+ } else if (command == "listmoons") {
+ const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/moon",requestHeaders,responseHeaders,responseBody);
+
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
+ nlohmann::json j;
+ try {
+ j = OSUtils::jsonParse(responseBody);
+ } catch (std::exception &exc) {
+ printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
+ return 1;
+ } catch ( ... ) {
+ printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
+ return 1;
+ }
+
+ if (scode == 200) {
+ printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
+ return 0;
+ } else {
+ printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+ return 1;
+ }
+ } else if (command == "orbit") {
+ const uint64_t worldId = Utils::hexStrToU64(arg1.c_str());
+ const uint64_t seed = Utils::hexStrToU64(arg2.c_str());
+ if ((worldId)&&(seed)) {
+ char jsons[1024];
+ OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
+ char cl[128];
+ OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ requestHeaders["Content-Type"] = "application/json";
+ requestHeaders["Content-Length"] = cl;
+ unsigned int scode = Http::POST(
+ 1024 * 1024 * 16,
+ 60000,
+ (const struct sockaddr *)&addr,
+ (std::string("/moon/") + arg1).c_str(),
+ requestHeaders,
+ jsons,
+ (unsigned long)strlen(jsons),
+ responseHeaders,
+ responseBody);
+ if (scode == 200) {
+ printf("200 orbit OK" ZT_EOL_S);
+ return 0;
+ } else {
+ printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+ return 1;
+ }
+ }
+ } else if (command == "deorbit") {
+ unsigned int scode = Http::DEL(
+ 1024 * 1024 * 16,
+ 60000,
+ (const struct sockaddr *)&addr,
+ (std::string("/moon/") + arg1).c_str(),
+ requestHeaders,
+ responseHeaders,
+ responseBody);
+ if (scode == 200) {
+ if (json) {
+ printf("%s",cliFixJsonCRs(responseBody).c_str());
+ } else {
+ printf("200 deorbit OK" ZT_EOL_S);
+ }
+ return 0;
+ } else {
+ printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+ return 1;
+ }
} else if (command == "set") {
if (arg1.length() != 16) {
- cliPrintHelp(argv[0],stderr);
+ printf("invalid network id" ZT_EOL_S);
return 2;
}
std::size_t eqidx = arg2.find('=');
if (eqidx != std::string::npos) {
if ((arg2.substr(0,eqidx) == "allowManaged")||(arg2.substr(0,eqidx) == "allowGlobal")||(arg2.substr(0,eqidx) == "allowDefault")) {
char jsons[1024];
- Utils::snprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
+ OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
arg2.substr(0,eqidx).c_str(),
(((arg2.substr(eqidx,2) == "=t")||(arg2.substr(eqidx,2) == "=1")) ? "true" : "false"));
char cl[128];
- Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
requestHeaders["Content-Type"] = "application/json";
requestHeaders["Content-Length"] = cl;
unsigned int scode = Http::POST(
@@ -577,7 +613,7 @@ static int cli(int argc,char **argv)
(std::string("/network/") + arg1).c_str(),
requestHeaders,
jsons,
- strlen(jsons),
+ (unsigned long)strlen(jsons),
responseHeaders,
responseBody);
if (scode == 200) {
@@ -592,6 +628,48 @@ static int cli(int argc,char **argv)
cliPrintHelp(argv[0],stderr);
return 2;
}
+ } else if (command == "get") {
+ if (arg1.length() != 16) {
+ cliPrintHelp(argv[0],stderr);
+ return 2;
+ }
+ char jsons[1024], cl[128];
+ OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ requestHeaders["Content-Type"] = "application/json";
+ requestHeaders["Content-Length"] = cl;
+ const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
+
+ if (scode == 0) {
+ printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
+ return 1;
+ }
+
+ nlohmann::json j;
+ try {
+ j = OSUtils::jsonParse(responseBody);
+ } catch (std::exception &exc) {
+ printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
+ return 1;
+ } catch ( ... ) {
+ printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
+ return 1;
+ }
+ if (j.is_array()) {
+ for(unsigned long i=0;i<j.size();++i) {
+ nlohmann::json &n = j[i];
+ if (n.is_object()) {
+ if (n["id"] == arg1) {
+ printf("%s\n", OSUtils::jsonString(n[arg2],"-").c_str());
+ }
+ }
+ }
+ }
+ if (scode == 200) {
+ return 0;
+ } else {
+ printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+ return 1;
+ }
} else {
cliPrintHelp(argv[0],stderr);
return 0;
@@ -619,7 +697,8 @@ static void idtoolPrintHelp(FILE *out,const char *pn)
fprintf(out," getpublic <identity.secret>" ZT_EOL_S);
fprintf(out," sign <identity.secret> <file>" ZT_EOL_S);
fprintf(out," verify <identity.secret/public> <file> <signature>" ZT_EOL_S);
- fprintf(out," mkcom <identity.secret> [<id,value,maxDelta> ...] (hexadecimal integers)" ZT_EOL_S);
+ fprintf(out," initmoon <identity.public of first seed>" ZT_EOL_S);
+ fprintf(out," genmoon <moon json>" ZT_EOL_S);
}
static Identity getIdFromArg(char *arg)
@@ -631,7 +710,7 @@ static Identity getIdFromArg(char *arg)
} else { // identity is to be read from a file
std::string idser;
if (OSUtils::readFile(arg,idser)) {
- if (id.fromString(idser))
+ if (id.fromString(idser.c_str()))
return id;
}
}
@@ -654,7 +733,7 @@ static int idtool(int argc,char **argv)
int vanityBits = 0;
if (argc >= 5) {
vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL;
- vanityBits = 4 * strlen(argv[4]);
+ vanityBits = 4 * (int)strlen(argv[4]);
if (vanityBits > 40)
vanityBits = 40;
}
@@ -672,14 +751,15 @@ static int idtool(int argc,char **argv)
}
}
- std::string idser = id.toString(true);
+ char idtmp[1024];
+ std::string idser = id.toString(true,idtmp);
if (argc >= 3) {
if (!OSUtils::writeFile(argv[2],idser)) {
fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[2]);
return 1;
} else printf("%s written" ZT_EOL_S,argv[2]);
if (argc >= 4) {
- idser = id.toString(false);
+ idser = id.toString(false,idtmp);
if (!OSUtils::writeFile(argv[3],idser)) {
fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[3]);
return 1;
@@ -714,7 +794,8 @@ static int idtool(int argc,char **argv)
return 1;
}
- printf("%s",id.toString(false).c_str());
+ char idtmp[1024];
+ printf("%s",id.toString(false,idtmp));
} else if (!strcmp(argv[1],"sign")) {
if (argc < 4) {
idtoolPrintHelp(stdout,argv[0]);
@@ -738,9 +819,10 @@ static int idtool(int argc,char **argv)
return 1;
}
C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
- printf("%s",Utils::hex(signature.data,(unsigned int)signature.size()).c_str());
+ char hexbuf[1024];
+ printf("%s",Utils::hex(signature.data,ZT_C25519_SIGNATURE_LEN,hexbuf));
} else if (!strcmp(argv[1],"verify")) {
- if (argc < 4) {
+ if (argc < 5) {
idtoolPrintHelp(stdout,argv[0]);
return 1;
}
@@ -757,41 +839,113 @@ static int idtool(int argc,char **argv)
return 1;
}
- std::string signature(Utils::unhex(argv[4]));
+ char buf[4096];
+ std::string signature(buf,Utils::unhex(argv[4],buf,(unsigned int)sizeof(buf)));
if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
printf("%s signature valid" ZT_EOL_S,argv[3]);
} else {
- fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
- return 1;
+ signature.clear();
+ if (OSUtils::readFile(argv[4],signature)) {
+ signature.assign(buf,Utils::unhex(signature.c_str(),buf,(unsigned int)sizeof(buf)));
+ if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
+ printf("%s signature valid" ZT_EOL_S,argv[3]);
+ } else {
+ fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
+ return 1;
+ }
+ } else {
+ fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
+ return 1;
+ }
}
- } else if (!strcmp(argv[1],"mkcom")) {
+ } else if (!strcmp(argv[1],"initmoon")) {
if (argc < 3) {
idtoolPrintHelp(stdout,argv[0]);
- return 1;
- }
+ } else {
+ const Identity id = getIdFromArg(argv[2]);
+ if (!id) {
+ fprintf(stderr,"%s is not a valid identity" ZT_EOL_S,argv[2]);
+ return 1;
+ }
- Identity id = getIdFromArg(argv[2]);
- if ((!id)||(!id.hasPrivate())) {
- fprintf(stderr,"Identity argument invalid, does not include private key, or file unreadable: %s" ZT_EOL_S,argv[2]);
- return 1;
+ C25519::Pair kp(C25519::generate());
+
+ char idtmp[4096];
+ nlohmann::json mj;
+ mj["objtype"] = "world";
+ mj["worldType"] = "moon";
+ mj["updatesMustBeSignedBy"] = mj["signingKey"] = Utils::hex(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,idtmp);
+ mj["signingKey_SECRET"] = Utils::hex(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN,idtmp);
+ mj["id"] = id.address().toString(idtmp);
+ nlohmann::json seedj;
+ seedj["identity"] = id.toString(false,idtmp);
+ seedj["stableEndpoints"] = nlohmann::json::array();
+ (mj["roots"] = nlohmann::json::array()).push_back(seedj);
+ std::string mjd(OSUtils::jsonDump(mj));
+
+ printf("%s" ZT_EOL_S,mjd.c_str());
}
+ } else if (!strcmp(argv[1],"genmoon")) {
+ if (argc < 3) {
+ idtoolPrintHelp(stdout,argv[0]);
+ } else {
+ std::string buf;
+ if (!OSUtils::readFile(argv[2],buf)) {
+ fprintf(stderr,"cannot read %s" ZT_EOL_S,argv[2]);
+ return 1;
+ }
+ nlohmann::json mj(OSUtils::jsonParse(buf));
- CertificateOfMembership com;
- for(int a=3;a<argc;++a) {
- std::vector<std::string> params(Utils::split(argv[a],",","",""));
- if (params.size() == 3) {
- uint64_t qId = Utils::hexStrToU64(params[0].c_str());
- uint64_t qValue = Utils::hexStrToU64(params[1].c_str());
- uint64_t qMaxDelta = Utils::hexStrToU64(params[2].c_str());
- com.setQualifier(qId,qValue,qMaxDelta);
+ const uint64_t id = Utils::hexStrToU64(OSUtils::jsonString(mj["id"],"0").c_str());
+ if (!id) {
+ fprintf(stderr,"ID in %s is invalid" ZT_EOL_S,argv[2]);
+ return 1;
+ }
+
+ World::Type t;
+ if (mj["worldType"] == "moon") {
+ t = World::TYPE_MOON;
+ } else if (mj["worldType"] == "planet") {
+ t = World::TYPE_PLANET;
+ } else {
+ fprintf(stderr,"invalid worldType" ZT_EOL_S);
+ return 1;
}
- }
- if (!com.sign(id)) {
- fprintf(stderr,"Signature of certificate of membership failed." ZT_EOL_S);
- return 1;
- }
- printf("%s",com.toString().c_str());
+ C25519::Pair signingKey;
+ C25519::Public updatesMustBeSignedBy;
+ Utils::unhex(OSUtils::jsonString(mj["signingKey"],"").c_str(),signingKey.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+ Utils::unhex(OSUtils::jsonString(mj["signingKey_SECRET"],"").c_str(),signingKey.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+ Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy"],"").c_str(),updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
+
+ std::vector<World::Root> roots;
+ nlohmann::json &rootsj = mj["roots"];
+ if (rootsj.is_array()) {
+ for(unsigned long i=0;i<(unsigned long)rootsj.size();++i) {
+ nlohmann::json &r = rootsj[i];
+ if (r.is_object()) {
+ roots.push_back(World::Root());
+ roots.back().identity = Identity(OSUtils::jsonString(r["identity"],"").c_str());
+ nlohmann::json &stableEndpointsj = r["stableEndpoints"];
+ if (stableEndpointsj.is_array()) {
+ for(unsigned long k=0;k<(unsigned long)stableEndpointsj.size();++k)
+ roots.back().stableEndpoints.push_back(InetAddress(OSUtils::jsonString(stableEndpointsj[k],"").c_str()));
+ std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
+ }
+ }
+ }
+ }
+ std::sort(roots.begin(),roots.end());
+
+ const int64_t now = OSUtils::now();
+ World w(World::make(t,id,now,updatesMustBeSignedBy,roots,signingKey));
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wbuf;
+ w.serialize(wbuf);
+ char fn[128];
+ OSUtils::ztsnprintf(fn,sizeof(fn),"%.16llx.moon",w.id());
+ OSUtils::writeFile(fn,wbuf.data(),wbuf.size());
+ printf("wrote %s (signed world with timestamp %llu)" ZT_EOL_S,fn,(unsigned long long)now);
+ }
} else {
idtoolPrintHelp(stdout,argv[0]);
return 1;
@@ -817,6 +971,147 @@ static void _sighandlerQuit(int sig)
}
#endif
+// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system
+#ifdef __LINUX__
+#ifndef PR_CAP_AMBIENT
+#define PR_CAP_AMBIENT 47
+#define PR_CAP_AMBIENT_IS_SET 1
+#define PR_CAP_AMBIENT_RAISE 2
+#define PR_CAP_AMBIENT_LOWER 3
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
+#endif
+#define ZT_LINUX_USER "zerotier-one"
+#define ZT_HAVE_DROP_PRIVILEGES 1
+namespace {
+
+// libc doesn't export capset, it is instead located in libcap
+// We ignore libcap and call it manually.
+struct cap_header_struct {
+ __u32 version;
+ int pid;
+};
+struct cap_data_struct {
+ __u32 effective;
+ __u32 permitted;
+ __u32 inheritable;
+};
+static inline int _zt_capset(cap_header_struct* hdrp, cap_data_struct* datap) { return syscall(SYS_capset, hdrp, datap); }
+
+static void _notDropping(const char *procName,const std::string &homeDir)
+{
+ struct stat buf;
+ if (lstat(homeDir.c_str(),&buf) < 0) {
+ if (buf.st_uid != 0 || buf.st_gid != 0) {
+ fprintf(stderr, "%s: FATAL: failed to drop privileges and can't run as root since privileges were previously dropped (home directory not owned by root)" ZT_EOL_S,procName);
+ exit(1);
+ }
+ }
+ fprintf(stderr, "%s: WARNING: failed to drop privileges (kernel may not support required prctl features), running as root" ZT_EOL_S,procName);
+}
+
+static int _setCapabilities(int flags)
+{
+ cap_header_struct capheader = {_LINUX_CAPABILITY_VERSION_1, 0};
+ cap_data_struct capdata;
+ capdata.inheritable = capdata.permitted = capdata.effective = flags;
+ return _zt_capset(&capheader, &capdata);
+}
+
+static void _recursiveChown(const char *path,uid_t uid,gid_t gid)
+{
+ struct dirent de;
+ struct dirent *dptr;
+ lchown(path,uid,gid);
+ DIR *d = opendir(path);
+ if (!d)
+ return;
+ dptr = (struct dirent *)0;
+ for(;;) {
+ if (readdir_r(d,&de,&dptr) != 0)
+ break;
+ if (!dptr)
+ break;
+ if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
+ std::string p(path);
+ p.push_back(ZT_PATH_SEPARATOR);
+ p.append(dptr->d_name);
+ _recursiveChown(p.c_str(),uid,gid); // will just fail and return on regular files
+ }
+ }
+ closedir(d);
+}
+
+static void dropPrivileges(const char *procName,const std::string &homeDir)
+{
+ if (getuid() != 0)
+ return;
+
+ // dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN
+ // and CAP_NET_RAW capabilities.
+ struct passwd *targetUser = getpwnam(ZT_LINUX_USER);
+ if (!targetUser)
+ return;
+
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) {
+ // Kernel has no support for ambient capabilities.
+ _notDropping(procName,homeDir);
+ return;
+ }
+ if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_NOROOT) < 0) {
+ _notDropping(procName,homeDir);
+ return;
+ }
+
+ // Change ownership of our home directory if everything looks good (does nothing if already chown'd)
+ _recursiveChown(homeDir.c_str(),targetUser->pw_uid,targetUser->pw_gid);
+
+ if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID) | (1 << CAP_NET_BIND_SERVICE)) < 0) {
+ _notDropping(procName,homeDir);
+ return;
+ }
+
+ int oldDumpable = prctl(PR_GET_DUMPABLE);
+ if (prctl(PR_SET_DUMPABLE, 0) < 0) {
+ // Disable ptracing. Otherwise there is a small window when previous
+ // compromised ZeroTier process could ptrace us, when we still have CAP_SETUID.
+ // (this is mitigated anyway on most distros by ptrace_scope=1)
+ fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
+ exit(1);
+ }
+
+ // Relinquish root
+ if (setgid(targetUser->pw_gid) < 0) {
+ perror("setgid");
+ exit(1);
+ }
+ if (setuid(targetUser->pw_uid) < 0) {
+ perror("setuid");
+ exit(1);
+ }
+
+ if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE)) < 0) {
+ fprintf(stderr,"%s: FATAL: unable to drop capabilities after relinquishing root" ZT_EOL_S,procName);
+ exit(1);
+ }
+
+ if (prctl(PR_SET_DUMPABLE, oldDumpable) < 0) {
+ fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
+ exit(1);
+ }
+
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) < 0) {
+ fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_ADMIN) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
+ exit(1);
+ }
+ if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0) < 0) {
+ fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_RAW) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
+ exit(1);
+ }
+}
+
+} // anonymous namespace
+#endif // __LINUX__
+
/****************************************************************************/
/* Windows helper functions and signal handlers */
/****************************************************************************/
@@ -979,14 +1274,11 @@ static void printHelp(const char *cn,FILE *out)
fprintf(out,
COPYRIGHT_NOTICE ZT_EOL_S
LICENSE_GRANT ZT_EOL_S);
- std::string updateUrl(OneService::autoUpdateUrl());
- if (updateUrl.length())
- fprintf(out,"Automatic updates enabled:" ZT_EOL_S" %s" ZT_EOL_S" (all updates are securely authenticated by 256-bit ECDSA signature)" ZT_EOL_S"" ZT_EOL_S,updateUrl.c_str());
fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn);
fprintf(out,"Available switches:" ZT_EOL_S);
fprintf(out," -h - Display this help" ZT_EOL_S);
fprintf(out," -v - Show version" ZT_EOL_S);
- fprintf(out," -U - Run as unprivileged user (skip privilege check)" ZT_EOL_S);
+ fprintf(out," -U - Skip privilege check and do not attempt to drop privileges" ZT_EOL_S);
fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S);
#ifdef __UNIX_LIKE__
@@ -1004,8 +1296,54 @@ static void printHelp(const char *cn,FILE *out)
fprintf(out," -q - Query API (zerotier-cli)" ZT_EOL_S);
}
+class _OneServiceRunner
+{
+public:
+ _OneServiceRunner(const char *pn,const std::string &hd,unsigned int p) : progname(pn),returnValue(0),port(p),homeDir(hd) {}
+ void threadMain()
+ throw()
+ {
+ try {
+ for(;;) {
+ zt1Service = OneService::newInstance(homeDir.c_str(),port);
+ switch(zt1Service->run()) {
+ case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
+ case OneService::ONE_NORMAL_TERMINATION:
+ break;
+ case OneService::ONE_UNRECOVERABLE_ERROR:
+ fprintf(stderr,"%s: fatal error: %s" ZT_EOL_S,progname,zt1Service->fatalErrorMessage().c_str());
+ returnValue = 1;
+ break;
+ case OneService::ONE_IDENTITY_COLLISION: {
+ delete zt1Service;
+ zt1Service = (OneService *)0;
+ std::string oldid;
+ OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid);
+ if (oldid.length()) {
+ OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid);
+ OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str());
+ OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str());
+ }
+ } continue; // restart!
+ }
+ break; // terminate loop -- normally we don't keep restarting
+ }
+
+ delete zt1Service;
+ zt1Service = (OneService *)0;
+ } catch ( ... ) {
+ fprintf(stderr,"%s: unexpected exception starting main OneService instance" ZT_EOL_S,progname);
+ returnValue = 1;
+ }
+ }
+ const char *progname;
+ unsigned int returnValue;
+ unsigned int port;
+ const std::string &homeDir;
+};
+
#ifdef __WINDOWS__
-int _tmain(int argc, _TCHAR* argv[])
+int __cdecl _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc,char **argv)
#endif
@@ -1086,7 +1424,7 @@ int main(int argc,char **argv)
if (argv[i][2]) {
printHelp(argv[0],stdout);
return 0;
- } else return idtool(argc,argv);
+ } else return idtool(argc-1,argv+1);
case 'q': // Invoke cli personality
if (argv[i][2]) {
@@ -1157,7 +1495,7 @@ int main(int argc,char **argv)
fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]);
return 1;
} else {
- std::vector<std::string> hpsp(Utils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
+ std::vector<std::string> hpsp(OSUtils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
std::string ptmp;
if (homeDir[0] == ZT_PATH_SEPARATOR)
ptmp.push_back(ZT_PATH_SEPARATOR);
@@ -1172,6 +1510,12 @@ int main(int argc,char **argv)
}
}
+ // This can be removed once the new controller code has been around for many versions
+ if (OSUtils::fileExists((homeDir + ZT_PATH_SEPARATOR_S + "controller.db").c_str(),true)) {
+ fprintf(stderr,"%s: FATAL: an old controller.db exists in %s -- see instructions in controller/README.md for how to migrate!" ZT_EOL_S,argv[0],homeDir.c_str());
+ return 1;
+ }
+
#ifdef __UNIX_LIKE__
#ifndef ZT_ONE_NO_ROOT_CHECK
if ((!skipRootCheck)&&(getuid() != 0)) {
@@ -1210,8 +1554,8 @@ int main(int argc,char **argv)
} else {
// Running from service manager
_winPokeAHole();
- ZeroTierOneService zt1Service;
- if (CServiceBase::Run(zt1Service) == TRUE) {
+ ZeroTierOneService zt1WindowsService;
+ if (CServiceBase::Run(zt1WindowsService) == TRUE) {
return 0;
} else {
fprintf(stderr,"%s: unable to start service (try -h for help)" ZT_EOL_S,argv[0]);
@@ -1221,6 +1565,11 @@ int main(int argc,char **argv)
#endif // __WINDOWS__
#ifdef __UNIX_LIKE__
+#ifdef ZT_HAVE_DROP_PRIVILEGES
+ if (!skipRootCheck)
+ dropPrivileges(argv[0],homeDir);
+#endif
+
std::string pidPath(homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH);
{
// Write .pid file to home folder
@@ -1232,39 +1581,13 @@ int main(int argc,char **argv)
}
#endif // __UNIX_LIKE__
- unsigned int returnValue = 0;
-
- for(;;) {
- zt1Service = OneService::newInstance(homeDir.c_str(),port);
- switch(zt1Service->run()) {
- case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
- case OneService::ONE_NORMAL_TERMINATION:
- break;
- case OneService::ONE_UNRECOVERABLE_ERROR:
- fprintf(stderr,"%s: fatal error: %s" ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str());
- returnValue = 1;
- break;
- case OneService::ONE_IDENTITY_COLLISION: {
- delete zt1Service;
- zt1Service = (OneService *)0;
- std::string oldid;
- OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid);
- if (oldid.length()) {
- OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid);
- OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str());
- OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str());
- }
- } continue; // restart!
- }
- break; // terminate loop -- normally we don't keep restarting
- }
-
- delete zt1Service;
- zt1Service = (OneService *)0;
+ _OneServiceRunner thr(argv[0],homeDir,port);
+ thr.threadMain();
+ //Thread::join(Thread::start(&thr));
#ifdef __UNIX_LIKE__
OSUtils::rm(pidPath.c_str());
#endif
- return returnValue;
+ return thr.returnValue;
}
diff --git a/osdep/Arp.cpp b/osdep/Arp.cpp
index fcc122f0..cc4e920a 100644
--- a/osdep/Arp.cpp
+++ b/osdep/Arp.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
diff --git a/osdep/Arp.hpp b/osdep/Arp.hpp
index 5f0d199a..27e92fdb 100644
--- a/osdep/Arp.hpp
+++ b/osdep/Arp.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ARP_HPP
diff --git a/osdep/BSDEthernetTap.cpp b/osdep/BSDEthernetTap.cpp
index e8d36c9c..053df21d 100644
--- a/osdep/BSDEthernetTap.cpp
+++ b/osdep/BSDEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -71,7 +79,7 @@ BSDEthernetTap::BSDEthernetTap(
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_handler(handler),
_arg(arg),
@@ -83,10 +91,12 @@ BSDEthernetTap::BSDEthernetTap(
{
static Mutex globalTapCreateLock;
char devpath[64],ethaddr[64],mtustr[32],metstr[32],tmpdevname[32];
- struct stat stattmp;
- // On FreeBSD at least we can rename, so use nwid to generate a deterministic unique zt#### name using base32
- // As a result we don't use desiredDevice
+ Mutex::Lock _gl(globalTapCreateLock);
+
+#ifdef __FreeBSD__
+ /* FreeBSD allows long interface names and interface renaming */
+
_dev = "zt";
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 60) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 55) & 0x1f)]);
@@ -102,17 +112,10 @@ BSDEthernetTap::BSDEthernetTap(
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)((nwid >> 5) & 0x1f)]);
_dev.push_back(ZT_BASE32_CHARS[(unsigned long)(nwid & 0x1f)]);
- Mutex::Lock _gl(globalTapCreateLock);
-
- if (mtu > 2800)
- throw std::runtime_error("max tap MTU is 2800");
-
- // On BSD we create taps and they can have high numbers, so use ones starting
- // at 9993 to not conflict with other stuff. Then we rename it to zt<base32 of nwid>
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
for(int i=9993;i<(9993+128);++i) {
- Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
- Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
+ OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
+ OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) {
long cpid = (long)vfork();
if (cpid == 0) {
@@ -123,6 +126,7 @@ BSDEthernetTap::BSDEthernetTap(
::waitpid(cpid,&exitcode,0);
} else throw std::runtime_error("fork() failed");
+ struct stat stattmp;
if (!stat(devpath,&stattmp)) {
cpid = (long)vfork();
if (cpid == 0) {
@@ -144,6 +148,19 @@ BSDEthernetTap::BSDEthernetTap(
}
}
}
+#else
+ /* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */
+
+ for(int i=0;i<64;++i) {
+ OSUtils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
+ OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
+ _fd = ::open(devpath,O_RDWR);
+ if (_fd > 0) {
+ _dev = tmpdevname;
+ break;
+ }
+ }
+#endif
if (_fd <= 0)
throw std::runtime_error("unable to open TAP device or no more devices available");
@@ -154,9 +171,9 @@ BSDEthernetTap::BSDEthernetTap(
}
// Configure MAC address and MTU, bring interface up
- Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
- Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
- Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
+ OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
+ OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
@@ -210,7 +227,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
{
long cpid = (long)vfork();
if (cpid == 0) {
- execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+ char ipbuf[64];
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString(ipbuf),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -239,7 +257,8 @@ bool BSDEthernetTap::addIp(const InetAddress &ip)
long cpid = (long)vfork();
if (cpid == 0) {
- ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
+ char tmp[128];
+ ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString(tmp),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -301,7 +320,7 @@ std::vector<InetAddress> BSDEthernetTap::ips() const
void BSDEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- char putBuf[4096];
+ char putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
@@ -325,6 +344,7 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
{
std::vector<MulticastGroup> newGroups;
+#ifndef __OpenBSD__
struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
if (!getifmaddrs(&ifmap)) {
struct ifmaddrs *p = ifmap;
@@ -339,6 +359,7 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
}
freeifmaddrs(ifmap);
}
+#endif // __OpenBSD__
std::vector<InetAddress> allIps(ips());
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
@@ -359,49 +380,22 @@ void BSDEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
_multicastGroups.swap(newGroups);
}
-/*
-bool BSDEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
+void BSDEthernetTap::setMtu(unsigned int mtu)
{
- std::set<MulticastGroup> newGroups;
- struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
- if (!getifmaddrs(&ifmap)) {
- struct ifmaddrs *p = ifmap;
- while (p) {
- if (p->ifma_addr->sa_family == AF_LINK) {
- struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
- struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
- if ((la->sdl_alen == 6)&&(in->sdl_nlen <= _dev.length())&&(!memcmp(_dev.data(),in->sdl_data,in->sdl_nlen)))
- newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen,6),0));
- }
- p = p->ifma_next;
+ if (mtu != _mtu) {
+ _mtu = mtu;
+ long cpid = (long)vfork();
+ if (cpid == 0) {
+ char tmp[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
+ _exit(-1);
+ } else if (cpid > 0) {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
}
- freeifmaddrs(ifmap);
}
-
- {
- std::set<InetAddress> allIps(ips());
- for(std::set<InetAddress>::const_iterator i(allIps.begin());i!=allIps.end();++i)
- newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
- }
-
- bool changed = false;
-
- for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
- if (!groups.count(*mg)) {
- groups.insert(*mg);
- changed = true;
- }
- }
- for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
- if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) {
- groups.erase(mg++);
- changed = true;
- } else ++mg;
- }
-
- return changed;
}
-*/
void BSDEthernetTap::threadMain()
throw()
@@ -409,7 +403,7 @@ void BSDEthernetTap::threadMain()
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
- char getBuf[8194];
+ char getBuf[ZT_MAX_MTU + 64];
// Wait for a moment after startup -- wait for Network to finish
// constructing itself.
@@ -446,8 +440,7 @@ void BSDEthernetTap::threadMain()
to.setTo(getBuf,6);
from.setTo(getBuf + 6,6);
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
- // TODO: VLAN support
- _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
+ _handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
}
r = 0;
diff --git a/osdep/BSDEthernetTap.hpp b/osdep/BSDEthernetTap.hpp
index 1bb48d31..3d91dbbb 100644
--- a/osdep/BSDEthernetTap.hpp
+++ b/osdep/BSDEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BSDETHERNETTAP_HPP
@@ -43,7 +51,7 @@ public:
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
~BSDEthernetTap();
@@ -57,12 +65,13 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
void threadMain()
throw();
private:
- void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+ void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
uint64_t _nwid;
Thread _thread;
diff --git a/osdep/BackgroundResolver.cpp b/osdep/BackgroundResolver.cpp
deleted file mode 100644
index ffcfdbae..00000000
--- a/osdep/BackgroundResolver.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "OSUtils.hpp"
-#include "Thread.hpp"
-#include "BackgroundResolver.hpp"
-
-namespace ZeroTier {
-
-/*
- * We can't actually abort a job. This is a legacy characteristic of the
- * ancient synchronous resolver APIs. So to abort jobs, we just abandon
- * them by setting their parent to null.
- */
-class BackgroundResolverJob
-{
-public:
- std::string name;
- BackgroundResolver *volatile parent;
- Mutex lock;
-
- void threadMain()
- throw()
- {
- std::vector<InetAddress> ips;
- try {
- ips = OSUtils::resolve(name.c_str());
- } catch ( ... ) {}
- {
- Mutex::Lock _l(lock);
- BackgroundResolver *p = parent;
- if (p)
- p->_postResult(ips);
- }
- delete this;
- }
-};
-
-BackgroundResolver::BackgroundResolver(const char *name) :
- _name(name),
- _job((BackgroundResolverJob *)0),
- _callback(0),
- _arg((void *)0),
- _ips(),
- _lock()
-{
-}
-
-BackgroundResolver::~BackgroundResolver()
-{
- abort();
-}
-
-std::vector<InetAddress> BackgroundResolver::get() const
-{
- Mutex::Lock _l(_lock);
- return _ips;
-}
-
-void BackgroundResolver::resolveNow(void (*callback)(BackgroundResolver *,void *),void *arg)
-{
- Mutex::Lock _l(_lock);
-
- if (_job) {
- Mutex::Lock _l2(_job->lock);
- _job->parent = (BackgroundResolver *)0;
- _job = (BackgroundResolverJob *)0;
- }
-
- BackgroundResolverJob *j = new BackgroundResolverJob();
- j->name = _name;
- j->parent = this;
-
- _job = j;
- _callback = callback;
- _arg = arg;
-
- _jobThread = Thread::start(j);
-}
-
-void BackgroundResolver::abort()
-{
- Mutex::Lock _l(_lock);
- if (_job) {
- Mutex::Lock _l2(_job->lock);
- _job->parent = (BackgroundResolver *)0;
- _job = (BackgroundResolverJob *)0;
- }
-}
-
-void BackgroundResolver::_postResult(const std::vector<InetAddress> &ips)
-{
- void (*cb)(BackgroundResolver *,void *);
- void *a;
- {
- Mutex::Lock _l(_lock);
- _job = (BackgroundResolverJob *)0;
- cb = _callback;
- a = _arg;
- _ips = ips;
- }
- if (cb)
- cb(this,a);
-}
-
-} // namespace ZeroTier
diff --git a/osdep/BackgroundResolver.hpp b/osdep/BackgroundResolver.hpp
deleted file mode 100644
index ba895487..00000000
--- a/osdep/BackgroundResolver.hpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_BACKGROUNDRESOLVER_HPP
-#define ZT_BACKGROUNDRESOLVER_HPP
-
-#include <vector>
-#include <string>
-
-#include "../node/Constants.hpp"
-#include "../node/Mutex.hpp"
-#include "../node/InetAddress.hpp"
-#include "../node/NonCopyable.hpp"
-#include "Thread.hpp"
-
-namespace ZeroTier {
-
-class BackgroundResolverJob;
-
-/**
- * A simple background resolver
- */
-class BackgroundResolver : NonCopyable
-{
- friend class BackgroundResolverJob;
-
-public:
- /**
- * Construct a new resolver
- *
- * resolveNow() must be called to actually initiate background resolution.
- *
- * @param name Name to resolve
- */
- BackgroundResolver(const char *name);
-
- ~BackgroundResolver();
-
- /**
- * @return Most recent resolver results or empty vector if none
- */
- std::vector<InetAddress> get() const;
-
- /**
- * Launch a background resolve job now
- *
- * If a resolve job is currently in progress, it is aborted and another
- * job is started.
- *
- * Note that jobs can't actually be aborted due to the limitations of the
- * ancient synchronous OS resolver APIs. As a result, in progress jobs
- * that are aborted are simply abandoned. Don't call this too frequently
- * or background threads might pile up.
- *
- * @param callback Callback function to receive notification or NULL if none
- * @praam arg Second argument to callback function
- */
- void resolveNow(void (*callback)(BackgroundResolver *,void *) = 0,void *arg = 0);
-
- /**
- * Abort (abandon) any current resolve jobs
- */
- void abort();
-
- /**
- * @return True if a background job is in progress
- */
- inline bool running() const
- {
- Mutex::Lock _l(_lock);
- return (_job != (BackgroundResolverJob *)0);
- }
-
- /**
- * Wait for pending job to complete (if any)
- */
- inline void wait() const
- {
- Thread t;
- {
- Mutex::Lock _l(_lock);
- if (!_job)
- return;
- t = _jobThread;
- }
- Thread::join(t);
- }
-
-private:
- void _postResult(const std::vector<InetAddress> &ips);
-
- std::string _name;
- BackgroundResolverJob *_job;
- Thread _jobThread;
- void (*_callback)(BackgroundResolver *,void *);
- void *_arg;
- std::vector<InetAddress> _ips;
- Mutex _lock;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp
index 72456d38..93fad9f1 100644
--- a/osdep/Binder.hpp
+++ b/osdep/Binder.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_BINDER_HPP
@@ -49,20 +57,22 @@
#include <algorithm>
#include <utility>
#include <map>
+#include <set>
+#include <atomic>
-#include "../node/NonCopyable.hpp"
#include "../node/InetAddress.hpp"
#include "../node/Mutex.hpp"
+#include "../node/Utils.hpp"
#include "Phy.hpp"
+#include "OSUtils.hpp"
-/**
- * Period between binder rescans/refreshes
- *
- * OneService also does this on detected restarts.
- */
+// Period between refreshes of bindings
#define ZT_BINDER_REFRESH_PERIOD 30000
+// Max number of bindings
+#define ZT_BINDER_MAX_BINDINGS 256
+
namespace ZeroTier {
/**
@@ -76,29 +86,22 @@ namespace ZeroTier {
* On OSes that do not support local port enumeration or where this is not
* meaningful, this degrades to binding to wildcard.
*/
-class Binder : NonCopyable
+class Binder
{
private:
struct _Binding
{
- _Binding() :
- udpSock((PhySocket *)0),
- tcpListenSock((PhySocket *)0),
- address() {}
-
+ _Binding() : udpSock((PhySocket *)0),tcpListenSock((PhySocket *)0) {}
PhySocket *udpSock;
PhySocket *tcpListenSock;
InetAddress address;
};
public:
- Binder() {}
+ Binder() : _bindingCount(0) {}
/**
- * Close all bound ports
- *
- * This should be called on shutdown. It closes listen sockets and UDP ports
- * but not TCP connections from any TCP listen sockets.
+ * Close all bound ports, should be called on shutdown
*
* @param phy Physical interface
*/
@@ -106,10 +109,11 @@ public:
void closeAll(Phy<PHY_HANDLER_TYPE> &phy)
{
Mutex::Lock _l(_lock);
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- phy.close(i->udpSock,false);
- phy.close(i->tcpListenSock,false);
+ for(unsigned int b=0,c=_bindingCount;b<c;++b) {
+ phy.close(_bindings[b].udpSock,false);
+ phy.close(_bindings[b].tcpListenSock,false);
}
+ _bindingCount = 0;
}
/**
@@ -119,206 +123,341 @@ public:
* changes, on startup, or periodically (e.g. every 30-60s).
*
* @param phy Physical interface
- * @param port Port to bind to on all interfaces (TCP and UDP)
- * @param ignoreInterfacesByName Ignore these interfaces by name
- * @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*)
- * @param ignoreInterfacesByAddress Ignore these interfaces by address
+ * @param ports Ports to bind on all interfaces
+ * @param portCount Number of ports
+ * @param explicitBind If present, override interface IP detection and bind to these (if possible)
+ * @param ifChecker Interface checker function to see if an interface should be used
* @tparam PHY_HANDLER_TYPE Type for Phy<> template
* @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method
*/
template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
- void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker)
+ void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int *ports,unsigned int portCount,const std::vector<InetAddress> explicitBind,INTERFACE_CHECKER &ifChecker)
{
std::map<InetAddress,std::string> localIfAddrs;
- PhySocket *udps;
- //PhySocket *tcps;
+ PhySocket *udps,*tcps;
Mutex::Lock _l(_lock);
+ bool interfacesEnumerated = true;
+ if (explicitBind.empty()) {
#ifdef __WINDOWS__
- char aabuf[32768];
- ULONG aalen = sizeof(aabuf);
- if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
- PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
- while (a) {
- PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
- while (ua) {
- InetAddress ip(ua->Address.lpSockaddr);
- if (ifChecker.shouldBindInterface("",ip)) {
- switch(ip.ipScope()) {
- default: break;
- case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
- case InetAddress::IP_SCOPE_GLOBAL:
- case InetAddress::IP_SCOPE_SHARED:
- case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
- break;
+ char aabuf[32768];
+ ULONG aalen = sizeof(aabuf);
+ if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
+ PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
+ while (a) {
+ PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
+ while (ua) {
+ InetAddress ip(ua->Address.lpSockaddr);
+ if (ifChecker.shouldBindInterface("",ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
+ }
+ break;
+ }
}
+ ua = ua->Next;
}
- ua = ua->Next;
+ a = a->Next;
}
- a = a->Next;
}
- }
+ else {
+ interfacesEnumerated = false;
+ }
#else // not __WINDOWS__
- struct ifaddrs *ifatbl = (struct ifaddrs *)0;
- struct ifaddrs *ifa;
- if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
- ifa = ifatbl;
- while (ifa) {
- if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
- InetAddress ip = *(ifa->ifa_addr);
- if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
- switch(ip.ipScope()) {
- default: break;
- case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
- case InetAddress::IP_SCOPE_GLOBAL:
- case InetAddress::IP_SCOPE_SHARED:
- case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
+ /* On Linux we use an alternative method if available since getifaddrs()
+ * gets very slow when there are lots of network namespaces. This won't
+ * work unless /proc/PID/net/if_inet6 exists and it may not on some
+ * embedded systems, so revert to getifaddrs() there. */
+
+#ifdef __LINUX__
+ char fn[256],tmp[256];
+ std::set<std::string> ifnames;
+ const unsigned long pid = (unsigned long)getpid();
+
+ // Get all device names
+ OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid);
+ FILE *procf = fopen(fn,"r");
+ if (procf) {
+ while (fgets(tmp,sizeof(tmp),procf)) {
+ tmp[255] = 0;
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) {
+ if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0))
+ ifnames.insert(f);
+ break; // we only want the first field
+ }
+ }
+ fclose(procf);
+ }
+ else {
+ interfacesEnumerated = false;
+ }
+
+ // Get IPv6 addresses (and any device names we don't already know)
+ OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid);
+ procf = fopen(fn,"r");
+ if (procf) {
+ while (fgets(tmp,sizeof(tmp),procf)) {
+ tmp[255] = 0;
+ char *saveptr = (char *)0;
+ unsigned char ipbits[16];
+ memset(ipbits,0,sizeof(ipbits));
+ char *devname = (char *)0;
+ int n = 0;
+ for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) {
+ switch(n++) {
+ case 0: // IP in hex
+ Utils::unhex(f,32,ipbits,16);
+ break;
+ case 5: // device name
+ devname = f;
break;
}
}
+ if (devname) {
+ ifnames.insert(devname);
+ InetAddress ip(ipbits,16,0);
+ if (ifChecker.shouldBindInterface(devname,ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
+ }
+ break;
+ }
+ }
+ }
}
- ifa = ifa->ifa_next;
+ fclose(procf);
+ }
+
+ // Get IPv4 addresses for each device
+ if (ifnames.size() > 0) {
+ const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0);
+ struct ifconf configuration;
+ configuration.ifc_len = 0;
+ configuration.ifc_buf = nullptr;
+
+ if (controlfd < 0) goto ip4_address_error;
+ if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
+ configuration.ifc_buf = (char*)malloc(configuration.ifc_len);
+ if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
+
+ for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) {
+ struct ifreq& request = configuration.ifc_req[i];
+ struct sockaddr* addr = &request.ifr_ifru.ifru_addr;
+ if (addr->sa_family != AF_INET) continue;
+ std::string ifname = request.ifr_ifrn.ifrn_name;
+ // name can either be just interface name or interface name followed by ':' and arbitrary label
+ if (ifname.find(':') != std::string::npos)
+ ifname = ifname.substr(0, ifname.find(':'));
+
+ InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0);
+ if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,ifname));
+ }
+ break;
+ }
+ }
+ }
+
+ ip4_address_error:
+ free(configuration.ifc_buf);
+ if (controlfd > 0) close(controlfd);
}
- freeifaddrs(ifatbl);
- }
+ const bool gotViaProc = (localIfAddrs.size() > 0);
+#else
+ const bool gotViaProc = false;
#endif
+ if (!gotViaProc) {
+ struct ifaddrs *ifatbl = (struct ifaddrs *)0;
+ struct ifaddrs *ifa;
+ if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
+ ifa = ifatbl;
+ while (ifa) {
+ if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
+ InetAddress ip = *(ifa->ifa_addr);
+ if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
+ switch(ip.ipScope()) {
+ default: break;
+ case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+ case InetAddress::IP_SCOPE_GLOBAL:
+ case InetAddress::IP_SCOPE_SHARED:
+ case InetAddress::IP_SCOPE_PRIVATE:
+ for(int x=0;x<(int)portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
+ }
+ break;
+ }
+ }
+ }
+ ifa = ifa->ifa_next;
+ }
+ freeifaddrs(ifatbl);
+ }
+ else {
+ interfacesEnumerated = false;
+ }
+ }
+
+#endif
+ } else {
+ for(std::vector<InetAddress>::const_iterator i(explicitBind.begin());i!=explicitBind.end();++i)
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(*i,std::string()));
+ }
+
// Default to binding to wildcard if we can't enumerate addresses
- if (localIfAddrs.empty()) {
- localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string()));
- localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string()));
+ if (!interfacesEnumerated && localIfAddrs.empty()) {
+ for(int x=0;x<(int)portCount;++x) {
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,ports[x]),std::string()));
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string()));
+ }
}
- // Close any old bindings to anything that doesn't exist anymore
- for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) {
- if (localIfAddrs.find(bi->address) == localIfAddrs.end()) {
- phy.close(bi->udpSock,false);
- phy.close(bi->tcpListenSock,false);
+ const unsigned int oldBindingCount = _bindingCount;
+ _bindingCount = 0;
+
+ // Save bindings that are still valid, close those that are not
+ for(unsigned int b=0;b<oldBindingCount;++b) {
+ if (localIfAddrs.find(_bindings[b].address) != localIfAddrs.end()) {
+ if (_bindingCount != b)
+ _bindings[(unsigned int)_bindingCount] = _bindings[b];
+ ++_bindingCount;
+ } else {
+ PhySocket *const udps = _bindings[b].udpSock;
+ PhySocket *const tcps = _bindings[b].tcpListenSock;
+ _bindings[b].udpSock = (PhySocket *)0;
+ _bindings[b].tcpListenSock = (PhySocket *)0;
+ phy.close(udps,false);
+ phy.close(tcps,false);
}
}
- std::vector<_Binding> newBindings;
+ // Create new bindings for those not already bound
for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) {
- typename std::vector<_Binding>::const_iterator bi(_bindings.begin());
- while (bi != _bindings.end()) {
- if (bi->address == ii->first) {
- newBindings.push_back(*bi);
+ unsigned int bi = 0;
+ while (bi != _bindingCount) {
+ if (_bindings[bi].address == ii->first)
break;
- }
++bi;
}
-
- if (bi == _bindings.end()) {
+ if (bi == _bindingCount) {
udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
- if (udps) {
- //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
- //if (tcps) {
+ tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0);
+ if ((udps)&&(tcps)) {
#ifdef __LINUX__
- // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
- if (ii->second.length() > 0) {
- int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
- char tmp[256];
- Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
- if (fd >= 0) {
- if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) {
- fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str());
- }
- }
- }
+ // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
+ if (ii->second.length() > 0) {
+ char tmp[256];
+ Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
+ int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
+ if (fd >= 0)
+ setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
+ fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(tcps);
+ if (fd >= 0)
+ setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
+ }
#endif // __LINUX__
- newBindings.push_back(_Binding());
- newBindings.back().udpSock = udps;
- //newBindings.back().tcpListenSock = tcps;
- newBindings.back().address = ii->first;
- //} else {
- // phy.close(udps,false);
- //}
+ if (_bindingCount < ZT_BINDER_MAX_BINDINGS) {
+ _bindings[_bindingCount].udpSock = udps;
+ _bindings[_bindingCount].tcpListenSock = tcps;
+ _bindings[_bindingCount].address = ii->first;
+ ++_bindingCount;
+ }
+ } else {
+ phy.close(udps,false);
+ phy.close(tcps,false);
}
}
}
+ }
- // Swapping pointers and then letting the old one fall out of scope is faster than copying again
- _bindings.swap(newBindings);
+ /**
+ * @return All currently bound local interface addresses
+ */
+ inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() const
+ {
+ std::vector<InetAddress> aa;
+ Mutex::Lock _l(_lock);
+ for(unsigned int b=0,c=_bindingCount;b<c;++b)
+ aa.push_back(_bindings[b].address);
+ return aa;
}
/**
- * Send a UDP packet from the specified local interface, or all
- *
- * Unfortunately even by examining the routing table there is no ultimately
- * robust way to tell where we might reach another host that works in all
- * environments. As a result, we send packets with null (wildcard) local
- * addresses from *every* bound interface.
- *
- * These are typically initial HELLOs, path probes, etc., since normal
- * conversations will have a local endpoint address. So the cost is low and
- * if the peer is not reachable via that route then the packet will go
- * nowhere and nothing will happen.
- *
- * It will of course only send via interface bindings of the same socket
- * family. No point in sending V4 via V6 or vice versa.
- *
- * In any case on most hosts there's only one or two interfaces that we
- * will use, so none of this is particularly costly.
- *
- * @param local Local interface address or null address for 'all'
- * @param remote Remote address
- * @param data Data to send
- * @param len Length of data
- * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only)
+ * Send from all bound UDP sockets
*/
template<typename PHY_HANDLER_TYPE>
- inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
+ inline bool udpSendAll(Phy<PHY_HANDLER_TYPE> &phy,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{
+ bool r = false;
Mutex::Lock _l(_lock);
- if (local) {
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- if (i->address == local) {
- if ((v4ttl)&&(local.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,v4ttl);
- const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
- if ((v4ttl)&&(local.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,255);
- return result;
- }
- }
- return false;
- } else {
- bool result = false;
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- if (i->address.ss_family == remote.ss_family) {
- if ((v4ttl)&&(remote.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,v4ttl);
- result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
- if ((v4ttl)&&(remote.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,255);
- }
- }
- return result;
+ for(unsigned int b=0,c=_bindingCount;b<c;++b) {
+ if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,ttl);
+ if (phy.udpSend(_bindings[b].udpSock,(const struct sockaddr *)addr,data,len)) r = true;
+ if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,255);
}
+ return r;
}
/**
- * @return All currently bound local interface addresses
+ * @param addr Address to check
+ * @return True if this is a bound local interface address
*/
- inline std::vector<InetAddress> allBoundLocalInterfaceAddresses()
+ inline bool isBoundLocalInterfaceAddress(const InetAddress &addr) const
{
Mutex::Lock _l(_lock);
- std::vector<InetAddress> aa;
- for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i)
- aa.push_back(i->address);
- return aa;
+ for(unsigned int b=0;b<_bindingCount;++b) {
+ if (_bindings[b].address == addr)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Quickly check that a UDP socket is valid
+ *
+ * @param udpSock UDP socket to check
+ * @return True if socket is currently bound/allocated
+ */
+ inline bool isUdpSocketValid(PhySocket *const udpSock)
+ {
+ for(unsigned int b=0,c=_bindingCount;b<c;++b) {
+ if (_bindings[b].udpSock == udpSock)
+ return (b < _bindingCount); // double check atomic which may have changed
+ }
+ return false;
}
private:
- std::vector<_Binding> _bindings;
+ _Binding _bindings[ZT_BINDER_MAX_BINDINGS];
+ std::atomic<unsigned int> _bindingCount;
Mutex _lock;
};
diff --git a/osdep/BlockingQueue.hpp b/osdep/BlockingQueue.hpp
new file mode 100644
index 00000000..351a095a
--- /dev/null
+++ b/osdep/BlockingQueue.hpp
@@ -0,0 +1,107 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_BLOCKINGQUEUE_HPP
+#define ZT_BLOCKINGQUEUE_HPP
+
+#include <queue>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+namespace ZeroTier {
+
+/**
+ * Simple C++11 thread-safe queue
+ *
+ * Do not use in node/ since we have not gone C++11 there yet.
+ */
+template <class T>
+class BlockingQueue
+{
+public:
+ BlockingQueue(void) : r(true) {}
+
+ inline void post(T t)
+ {
+ std::lock_guard<std::mutex> lock(m);
+ q.push(t);
+ c.notify_one();
+ }
+
+ inline void stop(void)
+ {
+ std::lock_guard<std::mutex> lock(m);
+ r = false;
+ c.notify_all();
+ }
+
+ inline bool get(T &value)
+ {
+ std::unique_lock<std::mutex> lock(m);
+ if (!r) return false;
+ while (q.empty()) {
+ c.wait(lock);
+ if (!r) return false;
+ }
+ value = q.front();
+ q.pop();
+ return true;
+ }
+
+ enum TimedWaitResult
+ {
+ OK,
+ TIMED_OUT,
+ STOP
+ };
+
+ inline TimedWaitResult get(T &value,const unsigned long ms)
+ {
+ const std::chrono::milliseconds ms2{ms};
+ std::unique_lock<std::mutex> lock(m);
+ if (!r) return STOP;
+ while (q.empty()) {
+ if (c.wait_for(lock,ms2) == std::cv_status::timeout)
+ return ((r) ? TIMED_OUT : STOP);
+ else if (!r)
+ return STOP;
+ }
+ value = q.front();
+ q.pop();
+ return OK;
+ }
+
+private:
+ volatile bool r;
+ std::queue<T> q;
+ std::mutex m;
+ std::condition_variable c;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/osdep/Http.cpp b/osdep/Http.cpp
index 064ccd0c..16785c96 100644
--- a/osdep/Http.cpp
+++ b/osdep/Http.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -104,12 +112,12 @@ struct HttpPhyHandler
inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
{
- if (writePtr < writeSize) {
- long n = phy->streamSend(sock,writeBuf + writePtr,writeSize - writePtr,true);
+ if (writePtr < (unsigned long)writeBuf.length()) {
+ long n = phy->streamSend(sock,writeBuf.data() + writePtr,(unsigned long)writeBuf.length() - writePtr,true);
if (n > 0)
writePtr += n;
}
- if (writePtr >= writeSize)
+ if (writePtr >= (unsigned long)writeBuf.length())
phy->setNotifyWritable(sock,false);
}
@@ -127,8 +135,7 @@ struct HttpPhyHandler
unsigned long messageSize;
unsigned long writePtr;
uint64_t lastActivity;
- unsigned long writeSize;
- char writeBuf[32768];
+ std::string writeBuf;
unsigned long maxResponseSize;
std::map<std::string,std::string> *responseHeaders;
@@ -236,24 +243,26 @@ unsigned int Http::_do(
handler.lastActivity = OSUtils::now();
try {
- handler.writeSize = Utils::snprintf(handler.writeBuf,sizeof(handler.writeBuf),"%s %s HTTP/1.1\r\n",method,path);
- for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h)
- handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"%s: %s\r\n",h->first.c_str(),h->second.c_str());
- handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"\r\n");
- if ((requestBody)&&(requestBodyLength)) {
- if ((handler.writeSize + requestBodyLength) > sizeof(handler.writeBuf)) {
- responseBody = "request too large";
- return 0;
- }
- memcpy(handler.writeBuf + handler.writeSize,requestBody,requestBodyLength);
- handler.writeSize += requestBodyLength;
+ char tmp[1024];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path);
+ handler.writeBuf.append(tmp);
+ for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) {
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str());
+ handler.writeBuf.append(tmp);
}
+ handler.writeBuf.append("\r\n");
+ if ((requestBody)&&(requestBodyLength))
+ handler.writeBuf.append((const char *)requestBody,requestBodyLength);
} catch ( ... ) {
responseBody = "request too large";
return 0;
}
- handler.maxResponseSize = maxResponseSize;
+ if (maxResponseSize) {
+ handler.maxResponseSize = maxResponseSize;
+ } else {
+ handler.maxResponseSize = 2147483647;
+ }
handler.responseHeaders = &responseHeaders;
handler.responseBody = &responseBody;
handler.error = false;
diff --git a/osdep/Http.hpp b/osdep/Http.hpp
index 1ecf4eec..b717c5c9 100644
--- a/osdep/Http.hpp
+++ b/osdep/Http.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_HTTP_HPP
@@ -135,6 +143,39 @@ public:
responseBody);
}
+ /**
+ * Make HTTP PUT request
+ *
+ * It is the responsibility of the caller to set all headers. With PUT, the
+ * Content-Length and Content-Type headers must be set or the PUT will not
+ * work.
+ *
+ * @return HTTP status code or 0 on error (responseBody will contain error message)
+ */
+ static inline unsigned int PUT(
+ unsigned long maxResponseSize,
+ unsigned long timeout,
+ const struct sockaddr *remoteAddress,
+ const char *path,
+ const std::map<std::string,std::string> &requestHeaders,
+ const void *postData,
+ unsigned long postDataLength,
+ std::map<std::string,std::string> &responseHeaders,
+ std::string &responseBody)
+ {
+ return _do(
+ "PUT",
+ maxResponseSize,
+ timeout,
+ remoteAddress,
+ path,
+ requestHeaders,
+ postData,
+ postDataLength,
+ responseHeaders,
+ responseBody);
+ }
+
private:
static unsigned int _do(
const char *method,
diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp
index e336bb67..06bbbada 100644
--- a/osdep/LinuxEthernetTap.cpp
+++ b/osdep/LinuxEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -40,6 +48,7 @@
#include <algorithm>
#include <utility>
+#include <string>
#include "../node/Constants.hpp"
#include "../node/Utils.hpp"
@@ -55,6 +64,19 @@ namespace ZeroTier {
static Mutex __tapCreateLock;
+static const char _base32_chars[32] = { 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','2','3','4','5','6','7' };
+static void _base32_5_to_8(const uint8_t *in,char *out)
+{
+ out[0] = _base32_chars[(in[0]) >> 3];
+ out[1] = _base32_chars[(in[0] & 0x07) << 2 | (in[1] & 0xc0) >> 6];
+ out[2] = _base32_chars[(in[1] & 0x3e) >> 1];
+ out[3] = _base32_chars[(in[1] & 0x01) << 4 | (in[2] & 0xf0) >> 4];
+ out[4] = _base32_chars[(in[2] & 0x0f) << 1 | (in[3] & 0x80) >> 7];
+ out[5] = _base32_chars[(in[3] & 0x7c) >> 2];
+ out[6] = _base32_chars[(in[3] & 0x03) << 3 | (in[4] & 0xe0) >> 5];
+ out[7] = _base32_chars[(in[4] & 0x1f)];
+}
+
LinuxEthernetTap::LinuxEthernetTap(
const char *homePath,
const MAC &mac,
@@ -62,7 +84,7 @@ LinuxEthernetTap::LinuxEthernetTap(
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_handler(handler),
_arg(arg),
@@ -75,13 +97,10 @@ LinuxEthernetTap::LinuxEthernetTap(
char procpath[128],nwids[32];
struct stat sbuf;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
- if (mtu > 2800)
- throw std::runtime_error("max tap MTU is 2800");
-
_fd = ::open("/dev/net/tun",O_RDWR);
if (_fd <= 0) {
_fd = ::open("/dev/tun",O_RDWR);
@@ -92,26 +111,59 @@ LinuxEthernetTap::LinuxEthernetTap(
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
- // Try to recall our last device name, or pick an unused one if that fails.
- bool recalledDevice = false;
- std::string devmapbuf;
- Dictionary<8194> devmap;
- if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
- devmap.load(devmapbuf.c_str());
- char desiredDevice[128];
- if (devmap.get(nwids,desiredDevice,sizeof(desiredDevice)) > 0) {
- Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),desiredDevice);
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
- recalledDevice = (stat(procpath,&sbuf) != 0);
+ // Restore device names from legacy devicemap, but for new devices we use a base32-based canonical naming
+ std::map<std::string,std::string> globalDeviceMap;
+ FILE *devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"r");
+ if (devmapf) {
+ char buf[256];
+ while (fgets(buf,sizeof(buf),devmapf)) {
+ char *x = (char *)0;
+ char *y = (char *)0;
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(buf,"\r\n=",&saveptr);(f);f=Utils::stok((char *)0,"\r\n=",&saveptr)) {
+ if (!x) x = f;
+ else if (!y) y = f;
+ else break;
+ }
+ if ((x)&&(y)&&(x[0])&&(y[0]))
+ globalDeviceMap[x] = y;
}
+ fclose(devmapf);
+ }
+ bool recalledDevice = false;
+ std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids);
+ if (gdmEntry != globalDeviceMap.end()) {
+ Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),gdmEntry->second.c_str());
+ OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ recalledDevice = (stat(procpath,&sbuf) != 0);
}
if (!recalledDevice) {
- int devno = 0;
+#ifdef __SYNOLOGY__
+ int devno = 50;
do {
- Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++);
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ OSUtils::ztsnprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
+ OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
} while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
+#else
+ uint64_t trial = 0; // incremented in the very unlikely event of a name collision with another network
+ do {
+ const uint64_t nwid40 = (nwid ^ (nwid >> 24)) + trial++;
+ uint8_t tmp2[5];
+ char tmp3[11];
+ tmp2[0] = (uint8_t)((nwid40 >> 32) & 0xff);
+ tmp2[1] = (uint8_t)((nwid40 >> 24) & 0xff);
+ tmp2[2] = (uint8_t)((nwid40 >> 16) & 0xff);
+ tmp2[3] = (uint8_t)((nwid40 >> 8) & 0xff);
+ tmp2[4] = (uint8_t)(nwid40 & 0xff);
+ tmp3[0] = 'z';
+ tmp3[1] = 't';
+ _base32_5_to_8(tmp2,tmp3 + 2);
+ tmp3[10] = (char)0;
+ memcpy(ifr.ifr_name,tmp3,11);
+ OSUtils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ } while (stat(procpath,&sbuf) == 0);
+#endif
}
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
@@ -174,9 +226,18 @@ LinuxEthernetTap::LinuxEthernetTap(
(void)::pipe(_shutdownSignalPipe);
- devmap.erase(nwids);
- devmap.add(nwids,_dev.c_str());
- OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),(const void *)devmap.data(),devmap.sizeBytes());
+ /*
+ globalDeviceMap[nwids] = _dev;
+ devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"w");
+ if (devmapf) {
+ gdmEntry = globalDeviceMap.begin();
+ while (gdmEntry != globalDeviceMap.end()) {
+ fprintf(devmapf,"%s=%s\n",gdmEntry->first.c_str(),gdmEntry->second.c_str());
+ ++gdmEntry;
+ }
+ fclose(devmapf);
+ }
+ */
_thread = Thread::start(this);
}
@@ -206,7 +267,8 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
if (cpid == 0) {
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
- ::execlp("ip","ip","addr","del",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ char iptmp[128];
+ ::execlp("ip","ip","addr","del",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0);
::_exit(-1);
} else {
int exitcode = -1;
@@ -215,6 +277,62 @@ static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
}
}
+#ifdef __SYNOLOGY__
+bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips)
+{
+ // Here we fill out interface config (ifcfg-dev) to prevent it from being killed
+ std::string filepath = "/etc/sysconfig/network-scripts/ifcfg-"+_dev;
+ std::string cfg_contents = "DEVICE="+_dev+"\nBOOTPROTO=static";
+ int ip4=0,ip6=0,ip4_tot=0,ip6_tot=0;
+
+ long cpid = (long)vfork();
+ if (cpid == 0) {
+ OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
+ setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
+ // We must know if there is at least (one) of each protocol version so we
+ // can properly enumerate address/netmask combinations in the ifcfg-dev file
+ for(int i=0; i<(int)ips.size(); i++) {
+ if (ips[i].isV4())
+ ip4_tot++;
+ else
+ ip6_tot++;
+ }
+ // Assemble and write contents of ifcfg-dev file
+ for(int i=0; i<(int)ips.size(); i++) {
+ if (ips[i].isV4()) {
+ char iptmp[64],iptmp2[64];
+ std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : "";
+ cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString(iptmp)
+ + "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
+ ip4++;
+ }
+ else {
+ char iptmp[64],iptmp2[64];
+ std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : "";
+ cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString(iptmp)
+ + "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
+ ip6++;
+ }
+ }
+ OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length());
+ // Finaly, add IPs
+ for(int i=0; i<(int)ips.size(); i++){
+ char iptmp[128],iptmp2[128];
+ if (ips[i].isV4())
+ ::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"broadcast",ips[i].broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0);
+ else
+ ::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"dev",_dev.c_str(),(const char *)0);
+ }
+ ::_exit(-1);
+ } else if (cpid > 0) {
+ int exitcode = -1;
+ ::waitpid(cpid,&exitcode,0);
+ return (exitcode == 0);
+ }
+ return true;
+}
+#endif // __SYNOLOGY__
+
bool LinuxEthernetTap::addIp(const InetAddress &ip)
{
if (!ip)
@@ -234,10 +352,11 @@ bool LinuxEthernetTap::addIp(const InetAddress &ip)
if (cpid == 0) {
OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
+ char iptmp[128],iptmp2[128];
if (ip.isV4()) {
- ::execlp("ip","ip","addr","add",ip.toString().c_str(),"broadcast",ip.broadcast().toIpString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ ::execlp("ip","ip","addr","add",ip.toString(iptmp),"broadcast",ip.broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0);
} else {
- ::execlp("ip","ip","addr","add",ip.toString().c_str(),"dev",_dev.c_str(),(const char *)0);
+ ::execlp("ip","ip","addr","add",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0);
}
::_exit(-1);
} else if (cpid > 0) {
@@ -301,7 +420,7 @@ std::vector<InetAddress> LinuxEthernetTap::ips() const
void LinuxEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- char putBuf[8194];
+ char putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
@@ -370,13 +489,28 @@ void LinuxEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,st
_multicastGroups.swap(newGroups);
}
+void LinuxEthernetTap::setMtu(unsigned int mtu)
+{
+ if (_mtu != mtu) {
+ _mtu = mtu;
+ int sock = socket(AF_INET,SOCK_DGRAM,0);
+ if (sock > 0) {
+ struct ifreq ifr;
+ memset(&ifr,0,sizeof(ifr));
+ ifr.ifr_ifru.ifru_mtu = (int)mtu;
+ ioctl(sock,SIOCSIFMTU,(void *)&ifr);
+ close(sock);
+ }
+ }
+}
+
void LinuxEthernetTap::threadMain()
throw()
{
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
- char getBuf[8194];
+ char getBuf[ZT_MAX_MTU + 64];
Thread::sleep(500);
@@ -412,7 +546,7 @@ void LinuxEthernetTap::threadMain()
from.setTo(getBuf + 6,6);
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
// TODO: VLAN support
- _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
+ _handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
}
r = 0;
diff --git a/osdep/LinuxEthernetTap.hpp b/osdep/LinuxEthernetTap.hpp
index cbb58efb..5142eec1 100644
--- a/osdep/LinuxEthernetTap.hpp
+++ b/osdep/LinuxEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_LINUXETHERNETTAP_HPP
@@ -44,7 +52,7 @@ public:
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
~LinuxEthernetTap();
@@ -52,18 +60,22 @@ public:
void setEnabled(bool en);
bool enabled() const;
bool addIp(const InetAddress &ip);
+#ifdef __SYNOLOGY__
+ bool addIpSyn(std::vector<InetAddress> ips);
+#endif
bool removeIp(const InetAddress &ip);
std::vector<InetAddress> ips() const;
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
void threadMain()
throw();
private:
- void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+ void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
uint64_t _nwid;
Thread _thread;
diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp
index 0bb74c1a..d7c80704 100644
--- a/osdep/ManagedRoute.cpp
+++ b/osdep/ManagedRoute.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include "../node/Constants.hpp"
@@ -57,8 +65,6 @@
#define ZT_LINUX_IP_COMMAND "/sbin/ip"
#define ZT_LINUX_IP_COMMAND_2 "/usr/sbin/ip"
-// NOTE: BSD is mostly tested on Apple/Mac but is likely to work on other BSD too
-
namespace ZeroTier {
namespace {
@@ -69,23 +75,28 @@ static void _forkTarget(const InetAddress &t,InetAddress &left,InetAddress &righ
{
const unsigned int bits = t.netmaskBits() + 1;
left = t;
- if ((t.ss_family == AF_INET)&&(bits <= 32)) {
- left.setPort(bits);
- right = t;
- reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
- right.setPort(bits);
- } else if ((t.ss_family == AF_INET6)&&(bits <= 128)) {
- left.setPort(bits);
- right = t;
- uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
- b[bits / 8] ^= 1 << (8 - (bits % 8));
- right.setPort(bits);
+ if (t.ss_family == AF_INET) {
+ if (bits <= 32) {
+ left.setPort(bits);
+ right = t;
+ reinterpret_cast<struct sockaddr_in *>(&right)->sin_addr.s_addr ^= Utils::hton((uint32_t)(1 << (32 - bits)));
+ right.setPort(bits);
+ } else {
+ right.zero();
+ }
+ } else if (t.ss_family == AF_INET6) {
+ if (bits <= 128) {
+ left.setPort(bits);
+ right = t;
+ uint8_t *b = reinterpret_cast<uint8_t *>(reinterpret_cast<struct sockaddr_in6 *>(&right)->sin6_addr.s6_addr);
+ b[bits / 8] ^= 1 << (8 - (bits % 8));
+ right.setPort(bits);
+ } else {
+ right.zero();
+ }
}
}
-#ifdef __BSD__ // ------------------------------------------------------------
-#define ZT_ROUTING_SUPPORT_FOUND 1
-
struct _RTE
{
InetAddress target;
@@ -95,6 +106,9 @@ struct _RTE
bool ifscope;
};
+#ifdef __BSD__ // ------------------------------------------------------------
+#define ZT_ROUTING_SUPPORT_FOUND 1
+
static std::vector<_RTE> _getRTEs(const InetAddress &target,bool contains)
{
std::vector<_RTE> rtes;
@@ -239,17 +253,19 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress
} else if (p == 0) {
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
+ char ttmp[64];
+ char iptmp[64];
if (via) {
if ((ifscope)&&(ifscope[0])) {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),via.toIpString(iptmp),(const char *)0);
} else {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),via.toIpString().c_str(),(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),via.toIpString(iptmp),(const char *)0);
}
} else if ((localInterface)&&(localInterface[0])) {
if ((ifscope)&&(ifscope[0])) {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,"-ifscope",ifscope,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),"-interface",localInterface,(const char *)0);
} else {
- ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString().c_str(),"-interface",localInterface,(const char *)0);
+ ::execl(ZT_BSD_ROUTE_CMD,ZT_BSD_ROUTE_CMD,op,((target.ss_family == AF_INET6) ? "-inet6" : "-inet"),target.toString(ttmp),"-interface",localInterface,(const char *)0);
}
}
::_exit(-1);
@@ -270,12 +286,13 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress
} else if (p == 0) {
::close(STDOUT_FILENO);
::close(STDERR_FILENO);
+ char ipbuf[64],ipbuf2[64];
if (via) {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0);
- ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"via",via.toIpString().c_str(),(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"via",via.toIpString(ipbuf2),(const char *)0);
} else if ((localInterface)&&(localInterface[0])) {
- ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0);
- ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString().c_str(),"dev",localInterface,(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND,ZT_LINUX_IP_COMMAND,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0);
+ ::execl(ZT_LINUX_IP_COMMAND_2,ZT_LINUX_IP_COMMAND_2,(target.ss_family == AF_INET6) ? "-6" : "-4","route",op,target.toString(ipbuf),"dev",localInterface,(const char *)0);
}
::_exit(-1);
}
@@ -339,10 +356,42 @@ static bool _winRoute(bool del,const NET_LUID &interfaceLuid,const NET_IFINDEX &
}
}
+static bool _winHasRoute(const NET_LUID &interfaceLuid, const NET_IFINDEX &interfaceIndex, const InetAddress &target, const InetAddress &via)
+{
+ MIB_IPFORWARD_ROW2 rtrow;
+ InitializeIpForwardEntry(&rtrow);
+ rtrow.InterfaceLuid.Value = interfaceLuid.Value;
+ rtrow.InterfaceIndex = interfaceIndex;
+ if (target.ss_family == AF_INET) {
+ rtrow.DestinationPrefix.Prefix.si_family = AF_INET;
+ rtrow.DestinationPrefix.Prefix.Ipv4.sin_family = AF_INET;
+ rtrow.DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr = reinterpret_cast<const struct sockaddr_in *>(&target)->sin_addr.S_un.S_addr;
+ if (via.ss_family == AF_INET) {
+ rtrow.NextHop.si_family = AF_INET;
+ rtrow.NextHop.Ipv4.sin_family = AF_INET;
+ rtrow.NextHop.Ipv4.sin_addr.S_un.S_addr = reinterpret_cast<const struct sockaddr_in *>(&via)->sin_addr.S_un.S_addr;
+ }
+ } else if (target.ss_family == AF_INET6) {
+ rtrow.DestinationPrefix.Prefix.si_family = AF_INET6;
+ rtrow.DestinationPrefix.Prefix.Ipv6.sin6_family = AF_INET6;
+ memcpy(rtrow.DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte, reinterpret_cast<const struct sockaddr_in6 *>(&target)->sin6_addr.u.Byte, 16);
+ if (via.ss_family == AF_INET6) {
+ rtrow.NextHop.si_family = AF_INET6;
+ rtrow.NextHop.Ipv6.sin6_family = AF_INET6;
+ memcpy(rtrow.NextHop.Ipv6.sin6_addr.u.Byte, reinterpret_cast<const struct sockaddr_in6 *>(&via)->sin6_addr.u.Byte, 16);
+ }
+ } else {
+ return false;
+ }
+ rtrow.DestinationPrefix.PrefixLength = target.netmaskBits();
+ rtrow.SitePrefixLength = rtrow.DestinationPrefix.PrefixLength;
+ return (GetIpForwardEntry2(&rtrow) == NO_ERROR);
+}
+
#endif // __WINDOWS__ --------------------------------------------------------
#ifndef ZT_ROUTING_SUPPORT_FOUND
-#error "ManagedRoute.cpp has no support for managing routes on this platform! You'll need to check and see if one of the existing ones will work and make sure proper defines are set, or write one. Please do a Github pull request if you do this for a new OS."
+#error "ManagedRoute.cpp has no support for managing routes on this platform! You'll need to check and see if one of the existing ones will work and make sure proper defines are set, or write one. Please do a GitHub pull request if you do this for a new OS."
#endif
} // anonymous namespace
@@ -369,145 +418,104 @@ bool ManagedRoute::sync()
return false;
#endif
- if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
- /* In ZeroTier we create two more specific routes for every one route. We
- * do this for default routes and IPv4 routes other than /32s. If there
- * is a pre-existing system route that this route will override, we create
- * two more specific interface-bound shadow routes for it.
- *
- * This means that ZeroTier can *itself* continue communicating over
- * whatever physical routes might be present while simultaneously
- * overriding them for general system traffic. This is mostly for
- * "full tunnel" VPN modes of operation, but might be useful for
- * virtualizing physical networks in a hybrid design as well. */
-
- // Generate two more specific routes than target with one extra bit
- InetAddress leftt,rightt;
+ InetAddress leftt,rightt;
+ if (_target.netmaskBits() == 0) // bifurcate only the default route
_forkTarget(_target,leftt,rightt);
+ else leftt = _target;
#ifdef __BSD__ // ------------------------------------------------------------
- // Find lowest metric system route that this route should override (if any)
- InetAddress newSystemVia;
- char newSystemDevice[128];
- newSystemDevice[0] = (char)0;
- int systemMetric = 9999999;
- std::vector<_RTE> rtes(_getRTEs(_target,false));
- for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
- if (r->via) {
- if ((!newSystemVia)||(r->metric < systemMetric)) {
- newSystemVia = r->via;
- Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
- systemMetric = r->metric;
- }
+ // Find lowest metric system route that this route should override (if any)
+ InetAddress newSystemVia;
+ char newSystemDevice[128];
+ newSystemDevice[0] = (char)0;
+ int systemMetric = 9999999;
+ std::vector<_RTE> rtes(_getRTEs(_target,false));
+ for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
+ if (r->via) {
+ if ( ((!newSystemVia)||(r->metric < systemMetric)) && (strcmp(r->device,_device) != 0) ) {
+ newSystemVia = r->via;
+ Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
+ systemMetric = r->metric;
}
}
- if ((newSystemVia)&&(!newSystemDevice[0])) {
- rtes = _getRTEs(newSystemVia,true);
- for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
- if (r->device[0]) {
- Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
- break;
- }
+ }
+
+ // Get device corresponding to route if we don't have that already
+ if ((newSystemVia)&&(!newSystemDevice[0])) {
+ rtes = _getRTEs(newSystemVia,true);
+ for(std::vector<_RTE>::iterator r(rtes.begin());r!=rtes.end();++r) {
+ if ( (r->device[0]) && (strcmp(r->device,_device) != 0) ) {
+ Utils::scopy(newSystemDevice,sizeof(newSystemDevice),r->device);
+ break;
}
}
-
- // Shadow system route if it exists, also delete any obsolete shadows
- // and replace them with the new state. sync() is called periodically to
- // allow us to do that if underlying connectivity changes.
- if ( ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
- if ((_systemVia)&&(_systemDevice[0])) {
- _routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
+ }
+ if (!newSystemDevice[0])
+ newSystemVia.zero();
+
+ // Shadow system route if it exists, also delete any obsolete shadows
+ // and replace them with the new state. sync() is called periodically to
+ // allow us to do that if underlying connectivity changes.
+ if ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice) != 0)) {
+ if (_systemVia) {
+ _routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
+ if (rightt)
_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
- }
+ }
- _systemVia = newSystemVia;
- Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
+ _systemVia = newSystemVia;
+ Utils::scopy(_systemDevice,sizeof(_systemDevice),newSystemDevice);
- if ((_systemVia)&&(_systemDevice[0])) {
- _routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
- _routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
+ if (_systemVia) {
+ _routeCmd("add",leftt,_systemVia,_systemDevice,(const char *)0);
+ _routeCmd("change",leftt,_systemVia,_systemDevice,(const char *)0);
+ if (rightt) {
_routeCmd("add",rightt,_systemVia,_systemDevice,(const char *)0);
_routeCmd("change",rightt,_systemVia,_systemDevice,(const char *)0);
}
}
+ }
- // Apply overriding non-device-scoped routes
- if (!_applied) {
- if (_via) {
- _routeCmd("add",leftt,_via,(const char *)0,(const char *)0);
- _routeCmd("change",leftt,_via,(const char *)0,(const char *)0);
- _routeCmd("add",rightt,_via,(const char *)0,(const char *)0);
- _routeCmd("change",rightt,_via,(const char *)0,(const char *)0);
- } else if (_device[0]) {
- _routeCmd("add",leftt,_via,(const char *)0,_device);
- _routeCmd("change",leftt,_via,(const char *)0,_device);
- _routeCmd("add",rightt,_via,(const char *)0,_device);
- _routeCmd("change",rightt,_via,(const char *)0,_device);
- }
-
- _applied = true;
- }
-
-#endif // __BSD__ ------------------------------------------------------------
-
-#ifdef __LINUX__ // ----------------------------------------------------------
-
- if (!_applied) {
- _routeCmd("replace",leftt,_via,(_via) ? _device : (const char *)0);
- _routeCmd("replace",rightt,_via,(_via) ? _device : (const char *)0);
- _applied = true;
- }
-
-#endif // __LINUX__ ----------------------------------------------------------
-
-#ifdef __WINDOWS__ // --------------------------------------------------------
-
- if (!_applied) {
- _winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
- _winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
- _applied = true;
- }
-
-#endif // __WINDOWS__ --------------------------------------------------------
-
- } else {
-
-#ifdef __BSD__ // ------------------------------------------------------------
-
- if (!_applied) {
- if (_via) {
- _routeCmd("add",_target,_via,(const char *)0,(const char *)0);
- _routeCmd("change",_target,_via,(const char *)0,(const char *)0);
- } else if (_device[0]) {
- _routeCmd("add",_target,_via,(const char *)0,_device);
- _routeCmd("change",_target,_via,(const char *)0,_device);
- }
- _applied = true;
- }
+ if (!_applied.count(leftt)) {
+ _applied[leftt] = false; // not ifscoped
+ _routeCmd("add",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
+ _routeCmd("change",leftt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
+ }
+ if ((rightt)&&(!_applied.count(rightt))) {
+ _applied[rightt] = false; // not ifscoped
+ _routeCmd("add",rightt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
+ _routeCmd("change",rightt,_via,(const char *)0,(_via) ? (const char *)0 : _device);
+ }
#endif // __BSD__ ------------------------------------------------------------
#ifdef __LINUX__ // ----------------------------------------------------------
- if (!_applied) {
- _routeCmd("replace",_target,_via,(_via) ? _device : (const char *)0);
- _applied = true;
- }
+ if (!_applied.count(leftt)) {
+ _applied[leftt] = false; // boolean unused
+ _routeCmd("replace",leftt,_via,(_via) ? (const char *)0 : _device);
+ }
+ if ((rightt)&&(!_applied.count(rightt))) {
+ _applied[rightt] = false; // boolean unused
+ _routeCmd("replace",rightt,_via,(_via) ? (const char *)0 : _device);
+ }
#endif // __LINUX__ ----------------------------------------------------------
#ifdef __WINDOWS__ // --------------------------------------------------------
- if (!_applied) {
- _winRoute(false,interfaceLuid,interfaceIndex,_target,_via);
- _applied = true;
- }
+ if ( (!_applied.count(leftt)) || (!_winHasRoute(interfaceLuid,interfaceIndex,leftt,_via)) ) {
+ _applied[leftt] = false; // boolean unused
+ _winRoute(false,interfaceLuid,interfaceIndex,leftt,_via);
+ }
+ if ( (rightt) && ( (!_applied.count(rightt)) || (!_winHasRoute(interfaceLuid,interfaceIndex,rightt,_via)) ) ) {
+ _applied[rightt] = false; // boolean unused
+ _winRoute(false,interfaceLuid,interfaceIndex,rightt,_via);
+ }
#endif // __WINDOWS__ --------------------------------------------------------
- }
-
return true;
}
@@ -521,66 +529,28 @@ void ManagedRoute::remove()
return;
#endif
- if (_applied) {
- if ((_target.isDefaultRoute())||((_target.ss_family == AF_INET)&&(_target.netmaskBits() < 32))) {
- InetAddress leftt,rightt;
- _forkTarget(_target,leftt,rightt);
-
-#ifdef __BSD__ // ------------------------------------------------------------
-
- if ((_systemVia)&&(_systemDevice[0])) {
- _routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
- _routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
- }
- if (_via) {
- _routeCmd("delete",leftt,_via,(const char *)0,(const char *)0);
- _routeCmd("delete",rightt,_via,(const char *)0,(const char *)0);
- } else if (_device[0]) {
- _routeCmd("delete",leftt,_via,(const char *)0,_device);
- _routeCmd("delete",rightt,_via,(const char *)0,_device);
- }
-
+#ifdef __BSD__
+ if (_systemVia) {
+ InetAddress leftt,rightt;
+ _forkTarget(_target,leftt,rightt);
+ _routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
+ if (rightt)
+ _routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
+ }
#endif // __BSD__ ------------------------------------------------------------
-#ifdef __LINUX__ // ----------------------------------------------------------
-
- _routeCmd("del",leftt,_via,(_via) ? _device : (const char *)0);
- _routeCmd("del",rightt,_via,(_via) ? _device : (const char *)0);
-
-#endif // __LINUX__ ----------------------------------------------------------
-
-#ifdef __WINDOWS__ // --------------------------------------------------------
-
- _winRoute(true,interfaceLuid,interfaceIndex,leftt,_via);
- _winRoute(true,interfaceLuid,interfaceIndex,rightt,_via);
-
-#endif // __WINDOWS__ --------------------------------------------------------
-
- } else {
-
+ for(std::map<InetAddress,bool>::iterator r(_applied.begin());r!=_applied.end();++r) {
#ifdef __BSD__ // ------------------------------------------------------------
-
- if (_via) {
- _routeCmd("delete",_target,_via,(const char *)0,(const char *)0);
- } else if (_device[0]) {
- _routeCmd("delete",_target,_via,(const char *)0,_device);
- }
-
+ _routeCmd("delete",r->first,_via,r->second ? _device : (const char *)0,(_via) ? (const char *)0 : _device);
#endif // __BSD__ ------------------------------------------------------------
#ifdef __LINUX__ // ----------------------------------------------------------
-
- _routeCmd("del",_target,_via,(_via) ? _device : (const char *)0);
-
+ _routeCmd("del",r->first,_via,(_via) ? (const char *)0 : _device);
#endif // __LINUX__ ----------------------------------------------------------
#ifdef __WINDOWS__ // --------------------------------------------------------
-
- _winRoute(true,interfaceLuid,interfaceIndex,_target,_via);
-
+ _winRoute(true,interfaceLuid,interfaceIndex,r->first,_via);
#endif // __WINDOWS__ --------------------------------------------------------
-
- }
}
_target.zero();
@@ -588,7 +558,7 @@ void ManagedRoute::remove()
_systemVia.zero();
_device[0] = (char)0;
_systemDevice[0] = (char)0;
- _applied = false;
+ _applied.clear();
}
} // namespace ZeroTier
diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp
index 63310f24..779ad6a1 100644
--- a/osdep/ManagedRoute.hpp
+++ b/osdep/ManagedRoute.hpp
@@ -1,3 +1,29 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
#ifndef ZT_MANAGEDROUTE_HPP
#define ZT_MANAGEDROUTE_HPP
@@ -6,9 +32,12 @@
#include "../node/InetAddress.hpp"
#include "../node/Utils.hpp"
+#include "../node/SharedPtr.hpp"
+#include "../node/AtomicCounter.hpp"
#include <stdexcept>
#include <vector>
+#include <map>
namespace ZeroTier {
@@ -17,12 +46,19 @@ namespace ZeroTier {
*/
class ManagedRoute
{
+ friend class SharedPtr<ManagedRoute>;
+
public:
- ManagedRoute()
+ ManagedRoute(const InetAddress &target,const InetAddress &via,const char *device)
{
- _device[0] = (char)0;
+ _target = target;
+ _via = via;
+ if (via.ss_family == AF_INET)
+ _via.setPort(32);
+ else if (via.ss_family == AF_INET6)
+ _via.setPort(128);
+ Utils::scopy(_device,sizeof(_device),device);
_systemDevice[0] = (char)0;
- _applied = false;
}
~ManagedRoute()
@@ -30,45 +66,6 @@ public:
this->remove();
}
- ManagedRoute(const ManagedRoute &r)
- {
- _applied = false;
- *this = r;
- }
-
- inline ManagedRoute &operator=(const ManagedRoute &r)
- {
- if ((!_applied)&&(!r._applied)) {
- memcpy(this,&r,sizeof(ManagedRoute)); // InetAddress is memcpy'able
- } else {
- fprintf(stderr,"Applied ManagedRoute isn't copyable!\n");
- abort();
- }
- return *this;
- }
-
- /**
- * Initialize object and set route
- *
- * Note: on Windows, use the interface NET_LUID in hexadecimal as the
- * "device name."
- *
- * @param target Route target (e.g. 0.0.0.0/0 for default)
- * @param via Route next L3 hop or NULL InetAddress if local in which case it will be routed via device
- * @param device Name or hex LUID of ZeroTier device (e.g. zt#)
- * @return True if route was successfully set
- */
- inline bool set(const InetAddress &target,const InetAddress &via,const char *device)
- {
- if ((!via)&&(!device[0]))
- return false;
- this->remove();
- _target = target;
- _via = via;
- Utils::scopy(_device,sizeof(_device),device);
- return this->sync();
- }
-
/**
* Set or update currently set route
*
@@ -93,13 +90,17 @@ public:
inline const char *device() const { return _device; }
private:
+ ManagedRoute(const ManagedRoute &) {}
+ inline ManagedRoute &operator=(const ManagedRoute &) { return *this; }
InetAddress _target;
InetAddress _via;
InetAddress _systemVia; // for route overrides
+ std::map<InetAddress,bool> _applied; // routes currently applied
char _device[128];
char _systemDevice[128]; // for route overrides
- bool _applied;
+
+ AtomicCounter __refCount;
};
} // namespace ZeroTier
diff --git a/osdep/NeighborDiscovery.cpp b/osdep/NeighborDiscovery.cpp
new file mode 100644
index 00000000..d9862f3d
--- /dev/null
+++ b/osdep/NeighborDiscovery.cpp
@@ -0,0 +1,272 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "NeighborDiscovery.hpp"
+#include "OSUtils.hpp"
+
+#include "../include/ZeroTierOne.h"
+
+#include <assert.h>
+
+namespace ZeroTier {
+
+uint16_t calc_checksum (uint16_t *addr, int len)
+{
+ int count = len;
+ uint32_t sum = 0;
+ uint16_t answer = 0;
+
+ // Sum up 2-byte values until none or only one byte left.
+ while (count > 1) {
+ sum += *(addr++);
+ count -= 2;
+ }
+
+ // Add left-over byte, if any.
+ if (count > 0) {
+ sum += *(uint8_t *) addr;
+ }
+
+ // Fold 32-bit sum into 16 bits; we lose information by doing this,
+ // increasing the chances of a collision.
+ // sum = (lower 16 bits) + (upper 16 bits shifted right 16 bits)
+ while (sum >> 16) {
+ sum = (sum & 0xffff) + (sum >> 16);
+ }
+
+ // Checksum is one's compliment of sum.
+ answer = ~sum;
+
+ return (answer);
+}
+
+struct _pseudo_header {
+ uint8_t sourceAddr[16];
+ uint8_t targetAddr[16];
+ uint32_t length;
+ uint8_t zeros[3];
+ uint8_t next; // 58
+};
+
+struct _option {
+ _option(int optionType)
+ : type(optionType)
+ , length(8)
+ {
+ memset(mac, 0, sizeof(mac));
+ }
+
+ uint8_t type;
+ uint8_t length;
+ uint8_t mac[6];
+};
+
+struct _neighbor_solicitation {
+ _neighbor_solicitation()
+ : type(135)
+ , code(0)
+ , checksum(0)
+ , option(1)
+ {
+ memset(&reserved, 0, sizeof(reserved));
+ memset(target, 0, sizeof(target));
+ }
+
+ void calculateChecksum(const sockaddr_storage &sourceIp, const sockaddr_storage &destIp) {
+ _pseudo_header ph;
+ memset(&ph, 0, sizeof(_pseudo_header));
+ const sockaddr_in6 *src = (const sockaddr_in6*)&sourceIp;
+ const sockaddr_in6 *dest = (const sockaddr_in6*)&destIp;
+
+ memcpy(ph.sourceAddr, &src->sin6_addr, sizeof(struct in6_addr));
+ memcpy(ph.targetAddr, &dest->sin6_addr, sizeof(struct in6_addr));
+ ph.next = 58;
+ ph.length = htonl(sizeof(_neighbor_solicitation));
+
+ size_t len = sizeof(_pseudo_header) + sizeof(_neighbor_solicitation);
+ uint8_t *tmp = (uint8_t*)malloc(len);
+ memcpy(tmp, &ph, sizeof(_pseudo_header));
+ memcpy(tmp+sizeof(_pseudo_header), this, sizeof(_neighbor_solicitation));
+
+ checksum = calc_checksum((uint16_t*)tmp, (int)len);
+
+ free(tmp);
+ tmp = NULL;
+ }
+
+ uint8_t type; // 135
+ uint8_t code; // 0
+ uint16_t checksum;
+ uint32_t reserved;
+ uint8_t target[16];
+ _option option;
+};
+
+struct _neighbor_advertisement {
+ _neighbor_advertisement()
+ : type(136)
+ , code(0)
+ , checksum(0)
+ , rso(0x40)
+ , option(2)
+ {
+ memset(padding, 0, sizeof(padding));
+ memset(target, 0, sizeof(target));
+ }
+
+ void calculateChecksum(const sockaddr_storage &sourceIp, const sockaddr_storage &destIp) {
+ _pseudo_header ph;
+ memset(&ph, 0, sizeof(_pseudo_header));
+ const sockaddr_in6 *src = (const sockaddr_in6*)&sourceIp;
+ const sockaddr_in6 *dest = (const sockaddr_in6*)&destIp;
+
+ memcpy(ph.sourceAddr, &src->sin6_addr, sizeof(struct in6_addr));
+ memcpy(ph.targetAddr, &dest->sin6_addr, sizeof(struct in6_addr));
+ ph.next = 58;
+ ph.length = htonl(sizeof(_neighbor_advertisement));
+
+ size_t len = sizeof(_pseudo_header) + sizeof(_neighbor_advertisement);
+ uint8_t *tmp = (uint8_t*)malloc(len);
+ memcpy(tmp, &ph, sizeof(_pseudo_header));
+ memcpy(tmp+sizeof(_pseudo_header), this, sizeof(_neighbor_advertisement));
+
+ checksum = calc_checksum((uint16_t*)tmp, (int)len);
+
+ free(tmp);
+ tmp = NULL;
+ }
+
+ uint8_t type; // 136
+ uint8_t code; // 0
+ uint16_t checksum;
+ uint8_t rso;
+ uint8_t padding[3];
+ uint8_t target[16];
+ _option option;
+};
+
+NeighborDiscovery::NeighborDiscovery()
+ : _cache(256)
+ , _lastCleaned(OSUtils::now())
+{}
+
+void NeighborDiscovery::addLocal(const sockaddr_storage &address, const MAC &mac)
+{
+ _NDEntry &e = _cache[InetAddress(address)];
+ e.lastQuerySent = 0;
+ e.lastResponseReceived = 0;
+ e.mac = mac;
+ e.local = true;
+}
+
+void NeighborDiscovery::remove(const sockaddr_storage &address)
+{
+ _cache.erase(InetAddress(address));
+}
+
+sockaddr_storage NeighborDiscovery::processIncomingND(const uint8_t *nd, unsigned int len, const sockaddr_storage &localIp, uint8_t *response, unsigned int &responseLen, MAC &responseDest)
+{
+ assert(sizeof(_neighbor_solicitation) == 28);
+ assert(sizeof(_neighbor_advertisement) == 32);
+
+ const uint64_t now = OSUtils::now();
+ sockaddr_storage ip = {0};
+
+ if (len >= sizeof(_neighbor_solicitation) && nd[0] == 0x87) {
+ // respond to Neighbor Solicitation request for local address
+ _neighbor_solicitation solicitation;
+ memcpy(&solicitation, nd, len);
+ InetAddress targetAddress(solicitation.target, 16, 0);
+ _NDEntry *targetEntry = _cache.get(targetAddress);
+ if (targetEntry && targetEntry->local) {
+ _neighbor_advertisement adv;
+ targetEntry->mac.copyTo(adv.option.mac, 6);
+ memcpy(adv.target, solicitation.target, 16);
+ adv.calculateChecksum(localIp, targetAddress);
+ memcpy(response, &adv, sizeof(_neighbor_advertisement));
+ responseLen = sizeof(_neighbor_advertisement);
+ responseDest.setTo(solicitation.option.mac, 6);
+ }
+ } else if (len >= sizeof(_neighbor_advertisement) && nd[0] == 0x88) {
+ _neighbor_advertisement adv;
+ memcpy(&adv, nd, len);
+ InetAddress responseAddress(adv.target, 16, 0);
+ _NDEntry *queryEntry = _cache.get(responseAddress);
+ if(queryEntry && !queryEntry->local && (now - queryEntry->lastQuerySent <= ZT_ND_QUERY_MAX_TTL)) {
+ queryEntry->lastResponseReceived = now;
+ queryEntry->mac.setTo(adv.option.mac, 6);
+ ip = responseAddress;
+ }
+ }
+
+ if ((now - _lastCleaned) >= ZT_ND_EXPIRE) {
+ _lastCleaned = now;
+ Hashtable<InetAddress, _NDEntry>::Iterator i(_cache);
+ InetAddress *k = NULL;
+ _NDEntry *v = NULL;
+ while (i.next(k, v)) {
+ if(!v->local && (now - v->lastResponseReceived) >= ZT_ND_EXPIRE) {
+ _cache.erase(*k);
+ }
+ }
+ }
+
+ return ip;
+}
+
+MAC NeighborDiscovery::query(const MAC &localMac, const sockaddr_storage &localIp, const sockaddr_storage &targetIp, uint8_t *query, unsigned int &queryLen, MAC &queryDest)
+{
+ const uint64_t now = OSUtils::now();
+
+ InetAddress localAddress(localIp);
+ localAddress.setPort(0);
+ InetAddress targetAddress(targetIp);
+ targetAddress.setPort(0);
+
+ _NDEntry &e = _cache[targetAddress];
+
+ if ( (e.mac && ((now - e.lastResponseReceived) >= (ZT_ND_EXPIRE / 3))) ||
+ (!e.mac && ((now - e.lastQuerySent) >= ZT_ND_QUERY_INTERVAL))) {
+ e.lastQuerySent = now;
+
+ _neighbor_solicitation ns;
+ memcpy(ns.target, targetAddress.rawIpData(), 16);
+ localMac.copyTo(ns.option.mac, 6);
+ ns.calculateChecksum(localIp, targetIp);
+ if (e.mac) {
+ queryDest = e.mac;
+ } else {
+ queryDest = (uint64_t)0xffffffffffffULL;
+ }
+ } else {
+ queryLen = 0;
+ queryDest.zero();
+ }
+
+ return e.mac;
+}
+
+}
diff --git a/osdep/NeighborDiscovery.hpp b/osdep/NeighborDiscovery.hpp
new file mode 100644
index 00000000..59186289
--- /dev/null
+++ b/osdep/NeighborDiscovery.hpp
@@ -0,0 +1,84 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_NEIGHBORDISCOVERY_HPP
+#define ZT_NEIGHBORDISCOVERY_HPP
+
+#include "../node/Hashtable.hpp"
+#include "../node/MAC.hpp"
+#include "../node/InetAddress.hpp"
+
+
+#define ZT_ND_QUERY_INTERVAL 2000
+
+#define ZT_ND_QUERY_MAX_TTL 5000
+
+#define ZT_ND_EXPIRE 600000
+
+
+namespace ZeroTier {
+
+class NeighborDiscovery
+{
+public:
+ NeighborDiscovery();
+
+ /**
+ * Set a local IP entry that we should respond to Neighbor Requests withPrefix64k
+ *
+ * @param mac Our local MAC address
+ * @param ip Our IPv6 address
+ */
+ void addLocal(const sockaddr_storage &address, const MAC &mac);
+
+ /**
+ * Delete a local IP entry or cached Neighbor entry
+ *
+ * @param address IPv6 address to remove
+ */
+ void remove(const sockaddr_storage &address);
+
+ sockaddr_storage processIncomingND(const uint8_t *nd, unsigned int len, const sockaddr_storage &localIp, uint8_t *response, unsigned int &responseLen, MAC &responseDest);
+
+ MAC query(const MAC &localMac, const sockaddr_storage &localIp, const sockaddr_storage &targetIp, uint8_t *query, unsigned int &queryLen, MAC &queryDest);
+
+private:
+ struct _NDEntry
+ {
+ _NDEntry() : lastQuerySent(0), lastResponseReceived(0), mac(), local(false) {}
+ uint64_t lastQuerySent;
+ uint64_t lastResponseReceived;
+ MAC mac;
+ bool local;
+ };
+
+ Hashtable<InetAddress, _NDEntry> _cache;
+ uint64_t _lastCleaned;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp
index 3a04308b..cadd4e6b 100644
--- a/osdep/OSUtils.cpp
+++ b/osdep/OSUtils.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -23,6 +31,7 @@
#include <sys/stat.h>
#include "../node/Constants.hpp"
+#include "../node/Utils.hpp"
#ifdef __UNIX_LIKE__
#include <unistd.h>
@@ -48,6 +57,23 @@
namespace ZeroTier {
+unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap,fmt);
+ int n = (int)vsnprintf(buf,len,fmt,ap);
+ va_end(ap);
+
+ if ((n >= (int)len)||(n < 0)) {
+ if (len)
+ buf[len - 1] = (char)0;
+ throw std::length_error("buf[] overflow");
+ }
+
+ return (unsigned int)n;
+}
+
#ifdef __UNIX_LIKE__
bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
throw()
@@ -72,7 +98,7 @@ bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
}
#endif // __UNIX_LIKE__
-std::vector<std::string> OSUtils::listDirectory(const char *path)
+std::vector<std::string> OSUtils::listDirectory(const char *path,bool includeDirectories)
{
std::vector<std::string> r;
@@ -81,7 +107,7 @@ std::vector<std::string> OSUtils::listDirectory(const char *path)
WIN32_FIND_DATAA ffd;
if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
do {
- if ((strcmp(ffd.cFileName,"."))&&(strcmp(ffd.cFileName,".."))&&((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0))
+ if ( (strcmp(ffd.cFileName,".")) && (strcmp(ffd.cFileName,"..")) && (((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)||(((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)&&(includeDirectories))) )
r.push_back(std::string(ffd.cFileName));
} while (FindNextFileA(hFind,&ffd));
FindClose(hFind);
@@ -97,7 +123,7 @@ std::vector<std::string> OSUtils::listDirectory(const char *path)
if (readdir_r(d,&de,&dptr))
break;
if (dptr) {
- if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&(dptr->d_type != DT_DIR))
+ if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&((dptr->d_type != DT_DIR)||(includeDirectories)))
r.push_back(std::string(dptr->d_name));
} else break;
}
@@ -107,6 +133,111 @@ std::vector<std::string> OSUtils::listDirectory(const char *path)
return r;
}
+long OSUtils::cleanDirectory(const char *path,const int64_t olderThan)
+{
+ long cleaned = 0;
+
+#ifdef __WINDOWS__
+ HANDLE hFind;
+ WIN32_FIND_DATAA ffd;
+ LARGE_INTEGER date,adjust;
+ adjust.QuadPart = 11644473600000 * 10000;
+ char tmp[4096];
+ if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
+ do {
+ if ((strcmp(ffd.cFileName,"."))&&(strcmp(ffd.cFileName,".."))&&((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)) {
+ date.HighPart = ffd.ftLastWriteTime.dwHighDateTime;
+ date.LowPart = ffd.ftLastWriteTime.dwLowDateTime;
+ if (date.QuadPart > 0) {
+ date.QuadPart -= adjust.QuadPart;
+ if ((int64_t)((date.QuadPart / 10000000) * 1000) < olderThan) {
+ ztsnprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName);
+ if (DeleteFileA(tmp))
+ ++cleaned;
+ }
+ }
+ }
+ } while (FindNextFileA(hFind,&ffd));
+ FindClose(hFind);
+ }
+#else
+ struct dirent de;
+ struct dirent *dptr;
+ struct stat st;
+ char tmp[4096];
+ DIR *d = opendir(path);
+ if (!d)
+ return -1;
+ dptr = (struct dirent *)0;
+ for(;;) {
+ if (readdir_r(d,&de,&dptr))
+ break;
+ if (dptr) {
+ if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&(dptr->d_type == DT_REG)) {
+ ztsnprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name);
+ if (stat(tmp,&st) == 0) {
+ int64_t mt = (int64_t)(st.st_mtime);
+ if ((mt > 0)&&((mt * 1000) < olderThan)) {
+ if (unlink(tmp) == 0)
+ ++cleaned;
+ }
+ }
+ }
+ } else break;
+ }
+ closedir(d);
+#endif
+
+ return cleaned;
+}
+
+bool OSUtils::rmDashRf(const char *path)
+{
+#ifdef __WINDOWS__
+ HANDLE hFind;
+ WIN32_FIND_DATAA ffd;
+ if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
+ do {
+ if ((strcmp(ffd.cFileName,".") != 0)&&(strcmp(ffd.cFileName,"..") != 0)) {
+ if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ if (DeleteFileA((std::string(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()) == FALSE)
+ return false;
+ } else {
+ if (!rmDashRf((std::string(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()))
+ return false;
+ }
+ }
+ } while (FindNextFileA(hFind,&ffd));
+ FindClose(hFind);
+ }
+ return (RemoveDirectoryA(path) != FALSE);
+#else
+ struct dirent de;
+ struct dirent *dptr;
+ DIR *d = opendir(path);
+ if (!d)
+ return true;
+ dptr = (struct dirent *)0;
+ for(;;) {
+ if (readdir_r(d,&de,&dptr) != 0)
+ break;
+ if (!dptr)
+ break;
+ if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
+ std::string p(path);
+ p.push_back(ZT_PATH_SEPARATOR);
+ p.append(dptr->d_name);
+ if (unlink(p.c_str()) != 0) { // unlink first will remove symlinks instead of recursing them
+ if (!rmDashRf(p.c_str()))
+ return false;
+ }
+ }
+ }
+ closedir(d);
+ return (rmdir(path) == 0);
+#endif
+}
+
void OSUtils::lockDownFile(const char *path,bool isDir)
{
#ifdef __UNIX_LIKE__
@@ -171,37 +302,9 @@ int64_t OSUtils::getFileSize(const char *path)
return -1;
}
-std::vector<InetAddress> OSUtils::resolve(const char *name)
-{
- std::vector<InetAddress> r;
- std::vector<InetAddress>::iterator i;
- InetAddress tmp;
- struct addrinfo *ai = (struct addrinfo *)0,*p;
- if (!getaddrinfo(name,(const char *)0,(const struct addrinfo *)0,&ai)) {
- try {
- p = ai;
- while (p) {
- if ((p->ai_addr)&&((p->ai_addr->sa_family == AF_INET)||(p->ai_addr->sa_family == AF_INET6))) {
- tmp = *(p->ai_addr);
- for(i=r.begin();i!=r.end();++i) {
- if (i->ipsEqual(tmp))
- goto skip_add_inetaddr;
- }
- r.push_back(tmp);
- }
-skip_add_inetaddr:
- p = p->ai_next;
- }
- } catch ( ... ) {}
- freeaddrinfo(ai);
- }
- std::sort(r.begin(),r.end());
- return r;
-}
-
bool OSUtils::readFile(const char *path,std::string &buf)
{
- char tmp[1024];
+ char tmp[16384];
FILE *f = fopen(path,"rb");
if (f) {
for(;;) {
@@ -231,8 +334,70 @@ bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
return false;
}
+std::vector<std::string> OSUtils::split(const char *s,const char *const sep,const char *esc,const char *quot)
+{
+ std::vector<std::string> fields;
+ std::string buf;
+
+ if (!esc)
+ esc = "";
+ if (!quot)
+ quot = "";
+
+ bool escapeState = false;
+ char quoteState = 0;
+ while (*s) {
+ if (escapeState) {
+ escapeState = false;
+ buf.push_back(*s);
+ } else if (quoteState) {
+ if (*s == quoteState) {
+ quoteState = 0;
+ fields.push_back(buf);
+ buf.clear();
+ } else buf.push_back(*s);
+ } else {
+ const char *quotTmp;
+ if (strchr(esc,*s))
+ escapeState = true;
+ else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
+ quoteState = *quotTmp;
+ else if (strchr(sep,*s)) {
+ if (buf.size() > 0) {
+ fields.push_back(buf);
+ buf.clear();
+ } // else skip runs of seperators
+ } else buf.push_back(*s);
+ }
+ ++s;
+ }
+
+ if (buf.size())
+ fields.push_back(buf);
+
+ return fields;
+}
+
std::string OSUtils::platformDefaultHomePath()
{
+#ifdef __QNAP__
+ char *cmd = "/sbin/getcfg zerotier Install_Path -f /etc/config/qpkg.conf";
+ char buf[128];
+ FILE *fp;
+ if ((fp = popen(cmd, "r")) == NULL) {
+ printf("Error opening pipe!\n");
+ return NULL;
+ }
+ while (fgets(buf, 128, fp) != NULL) { }
+ if(pclose(fp)) {
+ printf("Command not found or exited with error status\n");
+ return NULL;
+ }
+ std::string homeDir = std::string(buf);
+ homeDir.erase(std::remove(homeDir.begin(), homeDir.end(), '\n'), homeDir.end());
+ return homeDir;
+#endif
+
#ifdef __UNIX_LIKE__
#ifdef __APPLE__
@@ -267,6 +432,97 @@ std::string OSUtils::platformDefaultHomePath()
#endif // __UNIX_LIKE__ or not...
}
+// Inline these massive JSON operations in one place only to reduce binary footprint and compile time
+nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf.c_str()); }
+std::string OSUtils::jsonDump(const nlohmann::json &j,int indentation) { return j.dump(indentation); }
+
+uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl)
+{
+ try {
+ if (jv.is_number()) {
+ return (uint64_t)jv;
+ } else if (jv.is_string()) {
+ std::string s = jv;
+ return Utils::strToU64(s.c_str());
+ } else if (jv.is_boolean()) {
+ return ((bool)jv ? 1ULL : 0ULL);
+ }
+ } catch ( ... ) {}
+ return dfl;
+}
+
+uint64_t OSUtils::jsonIntHex(const nlohmann::json &jv,const uint64_t dfl)
+{
+ try {
+ if (jv.is_number()) {
+ return (uint64_t)jv;
+ } else if (jv.is_string()) {
+ std::string s = jv;
+ return Utils::hexStrToU64(s.c_str());
+ } else if (jv.is_boolean()) {
+ return ((bool)jv ? 1ULL : 0ULL);
+ }
+ } catch ( ... ) {}
+ return dfl;
+}
+
+bool OSUtils::jsonBool(const nlohmann::json &jv,const bool dfl)
+{
+ try {
+ if (jv.is_boolean()) {
+ return (bool)jv;
+ } else if (jv.is_number()) {
+ return ((uint64_t)jv > 0ULL);
+ } else if (jv.is_string()) {
+ std::string s = jv;
+ if (s.length() > 0) {
+ switch(s[0]) {
+ case 't':
+ case 'T':
+ case '1':
+ return true;
+ }
+ }
+ return false;
+ }
+ } catch ( ... ) {}
+ return dfl;
+}
+
+std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl)
+{
+ try {
+ if (jv.is_string()) {
+ return jv;
+ } else if (jv.is_number()) {
+ char tmp[64];
+ ztsnprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
+ return tmp;
+ } else if (jv.is_boolean()) {
+ return ((bool)jv ? std::string("1") : std::string("0"));
+ }
+ } catch ( ... ) {}
+ return std::string((dfl) ? dfl : "");
+}
+
+std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv)
+{
+ std::string s(jsonString(jv,""));
+ if (s.length() > 0) {
+ unsigned int buflen = (unsigned int)((s.length() / 2) + 1);
+ char *buf = new char[buflen];
+ try {
+ unsigned int l = Utils::unhex(s.c_str(),buf,buflen);
+ std::string b(buf,l);
+ delete [] buf;
+ return b;
+ } catch ( ... ) {
+ delete [] buf;
+ }
+ }
+ return std::string();
+}
+
// Used to convert HTTP header names to ASCII lower case
const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp
index 25bed9fe..324b7923 100644
--- a/osdep/OSUtils.hpp
+++ b/osdep/OSUtils.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OSUTILS_HPP
@@ -25,7 +33,6 @@
#include <string.h>
#include <time.h>
-#include <string>
#include <stdexcept>
#include <vector>
#include <map>
@@ -43,7 +50,12 @@
#include <sys/time.h>
#include <sys/stat.h>
#include <arpa/inet.h>
+#ifdef __LINUX__
+#include <sys/syscall.h>
#endif
+#endif
+
+#include "../ext/json/json.hpp"
namespace ZeroTier {
@@ -53,6 +65,20 @@ namespace ZeroTier {
class OSUtils
{
public:
+ /**
+ * Variant of snprintf that is portable and throws an exception
+ *
+ * This just wraps the local implementation whatever it's called, while
+ * performing a few other checks and adding exceptions for overflow.
+ *
+ * @param buf Buffer to write to
+ * @param len Length of buffer in bytes
+ * @param fmt Format string
+ * @param ... Format arguments
+ * @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
+ */
+ static unsigned int ztsnprintf(char *buf,unsigned int len,const char *fmt,...);
+
#ifdef __UNIX_LIKE__
/**
* Close STDOUT_FILENO and STDERR_FILENO and replace them with output to given path
@@ -75,7 +101,6 @@ public:
* @return True if delete was successful
*/
static inline bool rm(const char *path)
- throw()
{
#ifdef __WINDOWS__
return (DeleteFileA(path) != FALSE);
@@ -83,7 +108,7 @@ public:
return (unlink(path) == 0);
#endif
}
- static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); }
+ static inline bool rm(const std::string &path) { return rm(path.c_str()); }
static inline bool mkdir(const char *path)
{
@@ -97,17 +122,44 @@ public:
return true;
#endif
}
- static inline bool mkdir(const std::string &path) throw() { return OSUtils::mkdir(path.c_str()); }
+ static inline bool mkdir(const std::string &path) { return OSUtils::mkdir(path.c_str()); }
+
+ static inline bool rename(const char *o,const char *n)
+ {
+#ifdef __WINDOWS__
+ DeleteFileA(n);
+ return (::rename(o,n) == 0);
+#else
+ return (::rename(o,n) == 0);
+#endif
+ }
/**
* List a directory's contents
*
- * This returns only files, not sub-directories.
- *
* @param path Path to list
- * @return Names of files in directory
+ * @param includeDirectories If true, include directories as well as files
+ * @return Names of files in directory (without path prepended)
*/
- static std::vector<std::string> listDirectory(const char *path);
+ static std::vector<std::string> listDirectory(const char *path,bool includeDirectories = false);
+
+ /**
+ * Clean a directory of files whose last modified time is older than this
+ *
+ * This ignores directories, symbolic links, and other special files.
+ *
+ * @param olderThan Last modified older than timestamp (ms since epoch)
+ * @return Number of cleaned files or negative on fatal error
+ */
+ static long cleanDirectory(const char *path,const int64_t olderThan);
+
+ /**
+ * Delete a directory and all its files and subdirectories recursively
+ *
+ * @param path Path to delete
+ * @return True on success
+ */
+ static bool rmDashRf(const char *path);
/**
* Set modes on a file to something secure
@@ -157,8 +209,7 @@ public:
/**
* @return Current time in milliseconds since epoch
*/
- static inline uint64_t now()
- throw()
+ static inline int64_t now()
{
#ifdef __WINDOWS__
FILETIME ft;
@@ -168,35 +219,17 @@ public:
SystemTimeToFileTime(&st,&ft);
tmp.LowPart = ft.dwLowDateTime;
tmp.HighPart = ft.dwHighDateTime;
- return ( ((tmp.QuadPart - 116444736000000000ULL) / 10000L) + st.wMilliseconds );
+ return (int64_t)( ((tmp.QuadPart - 116444736000000000LL) / 10000L) + st.wMilliseconds );
#else
struct timeval tv;
- gettimeofday(&tv,(struct timezone *)0);
- return ( (1000ULL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) );
-#endif
- };
-
- /**
- * @return Current time in seconds since epoch, to the highest available resolution
- */
- static inline double nowf()
- throw()
- {
-#ifdef __WINDOWS__
- FILETIME ft;
- SYSTEMTIME st;
- ULARGE_INTEGER tmp;
- GetSystemTime(&st);
- SystemTimeToFileTime(&st,&ft);
- tmp.LowPart = ft.dwLowDateTime;
- tmp.HighPart = ft.dwHighDateTime;
- return (((double)(tmp.QuadPart - 116444736000000000ULL)) / 10000000.0);
+#ifdef __LINUX__
+ syscall(SYS_gettimeofday,&tv,0); /* fix for musl libc broken gettimeofday bug */
#else
- struct timeval tv;
gettimeofday(&tv,(struct timezone *)0);
- return ( ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0) );
#endif
- }
+ return ( (1000LL * (int64_t)tv.tv_sec) + (int64_t)(tv.tv_usec / 1000) );
+#endif
+ };
/**
* Read the full contents of a file into a string buffer
@@ -221,6 +254,17 @@ public:
static bool writeFile(const char *path,const void *buf,unsigned int len);
/**
+ * Split a string by delimiter, with optional escape and quote characters
+ *
+ * @param s String to split
+ * @param sep One or more separators
+ * @param esc Zero or more escape characters
+ * @param quot Zero or more quote characters
+ * @return Vector of tokens
+ */
+ static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
+
+ /**
* Write a block of data to disk, replacing any current file contents
*
* @param path Path to write
@@ -240,6 +284,14 @@ public:
*/
static std::string platformDefaultHomePath();
+ static nlohmann::json jsonParse(const std::string &buf);
+ static std::string jsonDump(const nlohmann::json &j,int indentation = 1);
+ static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl);
+ static uint64_t jsonIntHex(const nlohmann::json &jv,const uint64_t dfl);
+ static bool jsonBool(const nlohmann::json &jv,const bool dfl);
+ static std::string jsonString(const nlohmann::json &jv,const char *dfl);
+ static std::string jsonBinFromHex(const nlohmann::json &jv);
+
private:
static const unsigned char TOLOWER_TABLE[256];
};
diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp
index b3580929..dbff6200 100644
--- a/osdep/OSXEthernetTap.cpp
+++ b/osdep/OSXEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdint.h>
@@ -314,7 +322,7 @@ OSXEthernetTap::OSXEthernetTap(
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
void *arg) :
_handler(handler),
_arg(arg),
@@ -328,10 +336,7 @@ OSXEthernetTap::OSXEthernetTap(
char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
struct stat stattmp;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
-
- if (mtu > 2800)
- throw std::runtime_error("max tap MTU is 2800");
+ OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
Mutex::Lock _gl(globalTapCreateLock);
@@ -352,20 +357,33 @@ OSXEthernetTap::OSXEthernetTap(
}
// Try to reopen the last device we had, if we had one and it's still unused.
+ std::map<std::string,std::string> globalDeviceMap;
+ FILE *devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"r");
+ if (devmapf) {
+ char buf[256];
+ while (fgets(buf,sizeof(buf),devmapf)) {
+ char *x = (char *)0;
+ char *y = (char *)0;
+ char *saveptr = (char *)0;
+ for(char *f=Utils::stok(buf,"\r\n=",&saveptr);(f);f=Utils::stok((char *)0,"\r\n=",&saveptr)) {
+ if (!x) x = f;
+ else if (!y) y = f;
+ else break;
+ }
+ if ((x)&&(y)&&(x[0])&&(y[0]))
+ globalDeviceMap[x] = y;
+ }
+ fclose(devmapf);
+ }
bool recalledDevice = false;
- std::string devmapbuf;
- Dictionary<8194> devmap;
- if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),devmapbuf)) {
- devmap.load(devmapbuf.c_str());
- char desiredDevice[128];
- if (devmap.get(nwids,desiredDevice,sizeof(desiredDevice)) > 0) {
- Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",desiredDevice);
- if (stat(devpath,&stattmp) == 0) {
- _fd = ::open(devpath,O_RDWR);
- if (_fd > 0) {
- _dev = desiredDevice;
- recalledDevice = true;
- }
+ std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids);
+ if (gdmEntry != globalDeviceMap.end()) {
+ std::string devpath("/dev/"); devpath.append(gdmEntry->second);
+ if (stat(devpath.c_str(),&stattmp) == 0) {
+ _fd = ::open(devpath.c_str(),O_RDWR);
+ if (_fd > 0) {
+ _dev = gdmEntry->second;
+ recalledDevice = true;
}
}
}
@@ -373,13 +391,13 @@ OSXEthernetTap::OSXEthernetTap(
// Open the first unused tap device if we didn't recall a previous one.
if (!recalledDevice) {
for(int i=0;i<64;++i) {
- Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
+ OSUtils::ztsnprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
if (stat(devpath,&stattmp))
throw std::runtime_error("no more TAP devices available");
_fd = ::open(devpath,O_RDWR);
if (_fd > 0) {
char foo[16];
- Utils::snprintf(foo,sizeof(foo),"zt%d",i);
+ OSUtils::ztsnprintf(foo,sizeof(foo),"zt%d",i);
_dev = foo;
break;
}
@@ -395,9 +413,9 @@ OSXEthernetTap::OSXEthernetTap(
}
// Configure MAC address and MTU, bring interface up
- Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
- Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
- Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
+ OSUtils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ OSUtils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
+ OSUtils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
@@ -420,9 +438,16 @@ OSXEthernetTap::OSXEthernetTap(
++globalTapsRunning;
- devmap.erase(nwids);
- devmap.add(nwids,_dev.c_str());
- OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),(const void *)devmap.data(),devmap.sizeBytes());
+ globalDeviceMap[nwids] = _dev;
+ devmapf = fopen((_homePath + ZT_PATH_SEPARATOR_S + "devicemap").c_str(),"w");
+ if (devmapf) {
+ gdmEntry = globalDeviceMap.begin();
+ while (gdmEntry != globalDeviceMap.end()) {
+ fprintf(devmapf,"%s=%s\n",gdmEntry->first.c_str(),gdmEntry->second.c_str());
+ ++gdmEntry;
+ }
+ fclose(devmapf);
+ }
_thread = Thread::start(this);
}
@@ -474,7 +499,8 @@ bool OSXEthernetTap::addIp(const InetAddress &ip)
long cpid = (long)vfork();
if (cpid == 0) {
- ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0);
+ char tmp[128];
+ ::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString(tmp),"alias",(const char *)0);
::_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -494,7 +520,8 @@ bool OSXEthernetTap::removeIp(const InetAddress &ip)
if (*i == ip) {
long cpid = (long)vfork();
if (cpid == 0) {
- execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+ char tmp[128];
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString(tmp),"-alias",(const char *)0);
_exit(-1);
} else if (cpid > 0) {
int exitcode = -1;
@@ -546,7 +573,7 @@ std::vector<InetAddress> OSXEthernetTap::ips() const
void OSXEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- char putBuf[4096];
+ char putBuf[ZT_MAX_MTU + 64];
if ((_fd > 0)&&(len <= _mtu)&&(_enabled)) {
to.copyTo(putBuf,6);
from.copyTo(putBuf + 6,6);
@@ -604,13 +631,30 @@ void OSXEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std:
_multicastGroups.swap(newGroups);
}
+void OSXEthernetTap::setMtu(unsigned int mtu)
+{
+ if (mtu != _mtu) {
+ _mtu = mtu;
+ long cpid = (long)vfork();
+ if (cpid == 0) {
+ char tmp[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
+ execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
+ _exit(-1);
+ } else if (cpid > 0) {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ }
+ }
+}
+
void OSXEthernetTap::threadMain()
throw()
{
fd_set readfds,nullfds;
MAC to,from;
int n,nfds,r;
- char getBuf[8194];
+ char getBuf[ZT_MAX_MTU + 64];
Thread::sleep(500);
@@ -646,7 +690,7 @@ void OSXEthernetTap::threadMain()
from.setTo(getBuf + 6,6);
unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
// TODO: VLAN support
- _handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
+ _handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
}
r = 0;
diff --git a/osdep/OSXEthernetTap.hpp b/osdep/OSXEthernetTap.hpp
index de48f9a4..fe402901 100644
--- a/osdep/OSXEthernetTap.hpp
+++ b/osdep/OSXEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_OSXETHERNETTAP_HPP
@@ -48,7 +56,7 @@ public:
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
~OSXEthernetTap();
@@ -62,12 +70,13 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
void threadMain()
throw();
private:
- void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+ void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
uint64_t _nwid;
Thread _thread;
diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp
index eab8a317..e359ccdd 100644
--- a/osdep/Phy.hpp
+++ b/osdep/Phy.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_PHY_HPP
@@ -680,25 +688,26 @@ public:
* until one works.
*
* @param sock Socket
- * @param bufferSize Desired buffer sizes
+ * @param receiveBufferSize Desired size of receive buffer
+ * @param sendBufferSize Desired size of send buffer
*/
- inline void setBufferSizes(const PhySocket *sock,int bufferSize)
+ inline void setBufferSizes(const PhySocket *sock,int receiveBufferSize,int sendBufferSize)
{
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
- if (bufferSize > 0) {
- int bs = bufferSize;
- while (bs >= 65536) {
- int tmpbs = bs;
+ if (receiveBufferSize > 0) {
+ while (receiveBufferSize > 0) {
+ int tmpbs = receiveBufferSize;
if (::setsockopt(sws.sock,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
break;
- bs -= 16384;
+ receiveBufferSize -= 16384;
}
- bs = bufferSize;
- while (bs >= 65536) {
- int tmpbs = bs;
+ }
+ if (sendBufferSize > 0) {
+ while (sendBufferSize > 0) {
+ int tmpbs = sendBufferSize;
if (::setsockopt(sws.sock,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
break;
- bs -= 16384;
+ sendBufferSize -= 16384;
}
}
}
@@ -807,7 +816,7 @@ public:
* @param sock Stream connection socket
* @param notifyWritable Want writable notifications?
*/
- inline const void setNotifyWritable(PhySocket *sock,bool notifyWritable)
+ inline void setNotifyWritable(PhySocket *sock,bool notifyWritable)
{
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
if (notifyWritable) {
@@ -827,7 +836,7 @@ public:
* @param sock Socket to modify
* @param notifyReadable True if socket should be monitored for readability
*/
- inline const void setNotifyReadable(PhySocket *sock,bool notifyReadable)
+ inline void setNotifyReadable(PhySocket *sock,bool notifyReadable)
{
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
if (notifyReadable) {
@@ -957,7 +966,7 @@ public:
case ZT_PHY_SOCKET_UDP:
if (FD_ISSET(s->sock,&rfds)) {
- for(;;) {
+ for(int k=0;k<1024;++k) {
memset(&ss,0,sizeof(ss));
socklen_t slen = sizeof(ss);
long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen);
diff --git a/osdep/PortMapper.cpp b/osdep/PortMapper.cpp
index d3a19384..a7dd3046 100644
--- a/osdep/PortMapper.cpp
+++ b/osdep/PortMapper.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_USE_MINIUPNPC
@@ -21,6 +29,13 @@
// Uncomment to dump debug messages
//#define ZT_PORTMAPPER_TRACE 1
+#ifdef __ANDROID__
+#include <android/log.h>
+#define PM_TRACE(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "PortMapper", __VA_ARGS__))
+#else
+#define PM_TRACE(...) fprintf(stderr, __VA_ARGS__)
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -45,15 +60,24 @@
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
#else
+#ifdef __ANDROID__
+#include "miniupnpc.h"
+#include "upnpcommands.h"
+#else
#include "../ext/miniupnpc/miniupnpc.h"
#include "../ext/miniupnpc/upnpcommands.h"
#endif
+#endif
#ifdef ZT_USE_SYSTEM_NATPMP
#include <natpmp.h>
#else
+#ifdef __ANDROID__
+#include "natpmp.h"
+#else
#include "../ext/libnatpmp/natpmp.h"
#endif
+#endif
namespace ZeroTier {
@@ -75,7 +99,7 @@ public:
int mode = 0; // 0 == NAT-PMP, 1 == UPnP
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: started for UDP port %d"ZT_EOL_S,localPort);
+ fprintf(stderr,"PortMapper: started for UDP port %d" ZT_EOL_S,localPort);
#endif
while (run) {
@@ -99,15 +123,16 @@ public:
if (initnatpmp(&natpmp,0,0) != 0) {
mode = 1;
+ closenatpmp(&natpmp);
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: init failed, switching to UPnP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: NAT-PMP: init failed, switching to UPnP mode" ZT_EOL_S);
#endif
break;
}
InetAddress publicAddress;
sendpublicaddressrequest(&natpmp);
- uint64_t myTimeout = OSUtils::now() + 5000;
+ int64_t myTimeout = OSUtils::now() + 5000;
do {
fd_set fds;
struct timeval timeout;
@@ -123,7 +148,7 @@ public:
publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0);
} else {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: request for external address failed, aborting..."ZT_EOL_S);
+ PM_TRACE("PortMapper: NAT-PMP: request for external address failed, aborting..." ZT_EOL_S);
#endif
closenatpmp(&natpmp);
break;
@@ -145,7 +170,8 @@ public:
if (r == 0) {
publicAddress.setPort(response.pnu.newportmapping.mappedpublicport);
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: mapped %u to %s"ZT_EOL_S,(unsigned int)localPort,publicAddress.toString().c_str());
+ char paddr[128];
+ PM_TRACE("PortMapper: NAT-PMP: mapped %u to %s" ZT_EOL_S,(unsigned int)localPort,publicAddress.toString(paddr));
#endif
Mutex::Lock sl(surface_l);
surface.clear();
@@ -162,7 +188,7 @@ public:
if (!natPmpSuccess) {
mode = 1;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: NAT-PMP: request failed, switching to UPnP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: NAT-PMP: request failed, switching to UPnP mode" ZT_EOL_S);
#endif
}
}
@@ -187,7 +213,7 @@ public:
{
UPNPDev *dev = devlist;
while (dev) {
- fprintf(stderr,"PortMapper: found UPnP device at URL '%s': %s"ZT_EOL_S,dev->descURL,dev->st);
+ PM_TRACE("PortMapper: found UPnP device at URL '%s': %s" ZT_EOL_S,dev->descURL,dev->st);
dev = dev->pNext;
}
}
@@ -197,22 +223,22 @@ public:
memset(externalip,0,sizeof(externalip));
memset(&urls,0,sizeof(urls));
memset(&data,0,sizeof(data));
- Utils::snprintf(inport,sizeof(inport),"%d",localPort);
+ OSUtils::ztsnprintf(inport,sizeof(inport),"%d",localPort);
if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: my LAN IP address: %s"ZT_EOL_S,lanaddr);
+ PM_TRACE("PortMapper: UPnP: my LAN IP address: %s" ZT_EOL_S,lanaddr);
#endif
if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: my external IP address: %s"ZT_EOL_S,externalip);
+ PM_TRACE("PortMapper: UPnP: my external IP address: %s" ZT_EOL_S,externalip);
#endif
for(int tries=0;tries<60;++tries) {
int tryPort = (int)localPort + tries;
if (tryPort >= 65535)
tryPort = (tryPort - 65535) + 1025;
- Utils::snprintf(outport,sizeof(outport),"%u",tryPort);
+ OSUtils::ztsnprintf(outport,sizeof(outport),"%u",tryPort);
// First check and see if this port is already mapped to the
// same unique name. If so, keep this mapping and don't try
@@ -231,7 +257,7 @@ public:
memset(haveLeaseDuration,0,sizeof(haveLeaseDuration));
if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: reusing previously reserved external port: %s"ZT_EOL_S,outport);
+ PM_TRACE("PortMapper: UPnP: reusing previously reserved external port: %s" ZT_EOL_S,outport);
#endif
Mutex::Lock sl(surface_l);
surface.clear();
@@ -246,7 +272,7 @@ public:
int mapResult = 0;
if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: reserved external port: %s"ZT_EOL_S,outport);
+ PM_TRACE("PortMapper: UPnP: reserved external port: %s" ZT_EOL_S,outport);
#endif
Mutex::Lock sl(surface_l);
surface.clear();
@@ -256,7 +282,7 @@ public:
break;
} else {
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d"ZT_EOL_S,outport,mapResult);
+ PM_TRACE("PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d" ZT_EOL_S,outport,mapResult);
#endif
Thread::sleep(1000);
}
@@ -265,13 +291,13 @@ public:
} else {
mode = 0;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode" ZT_EOL_S);
#endif
}
} else {
mode = 0;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode"ZT_EOL_S);
+ PM_TRACE("PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode" ZT_EOL_S);
#endif
}
@@ -280,14 +306,14 @@ public:
} else {
mode = 0;
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d"ZT_EOL_S,upnpError);
+ PM_TRACE("PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d" ZT_EOL_S,upnpError);
#endif
}
}
// ---------------------------------------------------------------------
#ifdef ZT_PORTMAPPER_TRACE
- fprintf(stderr,"UPNPClient: rescanning in %d ms"ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
+ PM_TRACE("UPNPClient: rescanning in %d ms" ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
#endif
Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY);
}
diff --git a/osdep/PortMapper.hpp b/osdep/PortMapper.hpp
index 0b8d15fc..fa3cdc31 100644
--- a/osdep/PortMapper.hpp
+++ b/osdep/PortMapper.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifdef ZT_USE_MINIUPNPC
diff --git a/osdep/TestEthernetTap.hpp b/osdep/TestEthernetTap.hpp
new file mode 100644
index 00000000..6ccf92f3
--- /dev/null
+++ b/osdep/TestEthernetTap.hpp
@@ -0,0 +1,161 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_TESTETHERNETTAP_HPP
+#define ZT_TESTETHERNETTAP_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <set>
+
+#include "../node/Constants.hpp"
+#include "../node/InetAddress.hpp"
+#include "../node/MulticastGroup.hpp"
+#include "../node/Mutex.hpp"
+#include "../node/Utils.hpp"
+#include "../osdep/OSUtils.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Dummy test Ethernet tap that does not actually open a device on the system
+ */
+class TestEthernetTap
+{
+public:
+ TestEthernetTap(
+ const char *homePath,
+ const MAC &mac,
+ unsigned int mtu,
+ unsigned int metric,
+ uint64_t nwid,
+ const char *friendlyName,
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void *arg) :
+ _nwid(nwid),
+ _dev("zt_test_"),
+ _enabled(true)
+ {
+ char tmp[32];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)_nwid);
+ _dev.append(tmp);
+#ifdef ZT_TEST_TAP_REPORT_TO
+ _reportTo.fromString(ZT_TEST_TAP_REPORT_TO);
+ if (_reportTo.ss_family == AF_INET)
+ _reportsock = socket(AF_INET,SOCK_DGRAM,0);
+ else if (_reportTo.ss_family == AF_INET6)
+ _reportsock = socket(AF_INET6,SOCK_DGRAM,0);
+ else _reportsock = -1;
+#endif
+ }
+
+ ~TestEthernetTap()
+ {
+#ifdef ZT_TEST_TAP_REPORT_TO
+ if (_reportsock >= 0)
+ close(_reportsock);
+#endif
+ }
+
+ inline void setEnabled(bool en) { _enabled = en; }
+ inline bool enabled() const { return _enabled; }
+
+ inline bool addIp(const InetAddress &ip)
+ {
+ Mutex::Lock _l(_lock);
+ _ips.insert(ip);
+ return true;
+ }
+
+ inline bool removeIp(const InetAddress &ip)
+ {
+ Mutex::Lock _l(_lock);
+ _ips.erase(ip);
+ return true;
+ }
+
+ inline std::vector<InetAddress> ips() const
+ {
+ Mutex::Lock _l(_lock);
+ return std::vector<InetAddress>(_ips.begin(),_ips.end());
+ }
+
+ inline void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
+ {
+#ifdef ZT_TEST_TAP_REPORT_TO
+ char tmp[10000];
+ if ((_reportsock >= 0)&&(len < (sizeof(tmp) - 22))) {
+ const uint64_t nwid2 = Utils::hton(_nwid);
+ memcpy(tmp,&nwid2,8);
+ from.copyTo(tmp + 8,6);
+ to.copyTo(tmp + 14,6);
+ const uint16_t etherType2 = Utils::hton((uint16_t)etherType);
+ memcpy(tmp + 20,&etherType2,2);
+ memcpy(tmp + 22,data,len);
+ sendto(_reportsock,tmp,len + 22,0,reinterpret_cast<const struct sockaddr *>(&_reportTo),(_reportTo.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+ }
+#endif
+ }
+
+ inline std::string deviceName() const
+ {
+ return _dev;
+ }
+
+ inline void setFriendlyName(const char *friendlyName)
+ {
+ }
+
+ inline void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
+ {
+ }
+
+ inline void setMtu(unsigned int mtu)
+ {
+ }
+
+private:
+ uint64_t _nwid;
+ std::string _dev;
+ std::set<InetAddress> _ips;
+ InetAddress _reportTo;
+#ifdef ZT_TEST_TAP_REPORT_TO
+ int _reportsock;
+#endif
+ bool _enabled;
+ Mutex _lock;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/osdep/Thread.hpp b/osdep/Thread.hpp
index 4f90dc0b..35ea5035 100644
--- a/osdep/Thread.hpp
+++ b/osdep/Thread.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_THREAD_HPP
@@ -28,6 +36,7 @@
#include <WinSock2.h>
#include <Windows.h>
#include <string.h>
+
#include "../node/Mutex.hpp"
namespace ZeroTier {
@@ -45,7 +54,6 @@ class Thread
{
public:
Thread()
- throw()
{
_th = NULL;
_tid = 0;
@@ -53,7 +61,6 @@ public:
template<typename C>
static inline Thread start(C *instance)
- throw(std::runtime_error)
{
Thread t;
t._th = CreateThread(NULL,0,&___zt_threadMain<C>,(LPVOID)instance,0,&t._tid);
@@ -83,11 +90,13 @@ public:
// Not available on *nix platforms
static inline void cancelIO(const Thread &t)
{
+#if !defined(__MINGW32__) && !defined(__MINGW64__) // CancelSynchronousIo not available in MSYS2
if (t._th != NULL)
CancelSynchronousIo(t._th);
+#endif
}
- inline operator bool() const throw() { return (_th != NULL); }
+ inline operator bool() const { return (_th != NULL); }
private:
HANDLE _th;
@@ -122,28 +131,18 @@ class Thread
{
public:
Thread()
- throw()
{
- memset(&_tid,0,sizeof(_tid));
- pthread_attr_init(&_tattr);
-#ifdef __LINUX__
- pthread_attr_setstacksize(&_tattr,8388608); // for MUSL libc and others, has no effect in normal glibc environments
-#endif
- _started = false;
+ memset(this,0,sizeof(Thread));
}
Thread(const Thread &t)
- throw()
{
- memcpy(&_tid,&(t._tid),sizeof(_tid));
- _started = t._started;
+ memcpy(this,&t,sizeof(Thread));
}
inline Thread &operator=(const Thread &t)
- throw()
{
- memcpy(&_tid,&(t._tid),sizeof(_tid));
- _started = t._started;
+ memcpy(this,&t,sizeof(Thread));
return *this;
}
@@ -157,12 +156,20 @@ public:
*/
template<typename C>
static inline Thread start(C *instance)
- throw(std::runtime_error)
{
Thread t;
- t._started = true;
- if (pthread_create(&t._tid,&t._tattr,&___zt_threadMain<C>,instance))
+ pthread_attr_t tattr;
+ pthread_attr_init(&tattr);
+ // This corrects for systems with abnormally small defaults (musl) and also
+ // shrinks the stack on systems with large defaults to save a bit of memory.
+ pthread_attr_setstacksize(&tattr,ZT_THREAD_MIN_STACK_SIZE);
+ if (pthread_create(&t._tid,&tattr,&___zt_threadMain<C>,instance)) {
+ pthread_attr_destroy(&tattr);
throw std::runtime_error("pthread_create() failed, unable to create thread");
+ } else {
+ t._started = true;
+ pthread_attr_destroy(&tattr);
+ }
return t;
}
@@ -184,11 +191,10 @@ public:
*/
static inline void sleep(unsigned long ms) { usleep(ms * 1000); }
- inline operator bool() const throw() { return (_started); }
+ inline operator bool() const { return (_started); }
private:
pthread_t _tid;
- pthread_attr_t _tattr;
volatile bool _started;
};
diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp
index 7e1a5a19..aa96d33a 100644
--- a/osdep/WindowsEthernetTap.cpp
+++ b/osdep/WindowsEthernetTap.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -456,13 +464,15 @@ WindowsEthernetTap::WindowsEthernetTap(
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg) :
_handler(handler),
_arg(arg),
_mac(mac),
_nwid(nwid),
+ _mtu(mtu),
_tap(INVALID_HANDLE_VALUE),
+ _friendlyName(friendlyName),
_injectSemaphore(INVALID_HANDLE_VALUE),
_pathToHelpers(hp),
_run(true),
@@ -473,13 +483,9 @@ WindowsEthernetTap::WindowsEthernetTap(
char subkeyClass[1024];
char data[1024];
char tag[24];
- std::string mySubkeyName;
-
- if (mtu > 2800)
- throw std::runtime_error("MTU too large.");
// We "tag" registry entries with the network ID to identify persistent devices
- Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
+ OSUtils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
Mutex::Lock _l(_systemTapInitLock);
@@ -522,7 +528,7 @@ WindowsEthernetTap::WindowsEthernetTap(
_netCfgInstanceId = instanceId;
_deviceInstanceId = instanceIdPath;
- mySubkeyName = subkeyName;
+ _mySubkeyName = subkeyName;
break; // found it!
}
}
@@ -565,7 +571,7 @@ WindowsEthernetTap::WindowsEthernetTap(
if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS)
_deviceInstanceId.assign(data,dataLen);
- mySubkeyName = subkeyName;
+ _mySubkeyName = subkeyName;
// Disable DHCP by default on new devices
HKEY tcpIpInterfaces;
@@ -596,25 +602,25 @@ WindowsEthernetTap::WindowsEthernetTap(
if (_netCfgInstanceId.length() > 0) {
char tmps[64];
- unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
- DWORD tmp = mtu;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"MTU",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
-
- tmp = 0;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+ unsigned int tmpsl = OSUtils::ztsnprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
+ tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu);
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl);
+
+ DWORD tmp = 0;
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"*NdisDeviceType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
tmp = IF_TYPE_ETHERNET_CSMACD;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
if (creatingNewDevice) {
// Vista/2008 does not set this
if (newDeviceInstanceId.length() > 0)
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length());
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"DeviceInstanceID",REG_SZ,newDeviceInstanceId.c_str(),(DWORD)newDeviceInstanceId.length());
// Set EnableDHCP to 0 by default on new devices
tmp = 0;
- RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
+ RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
}
RegCloseKey(nwAdapters);
} else {
@@ -640,7 +646,7 @@ WindowsEthernetTap::WindowsEthernetTap(
if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR)
throw std::runtime_error("unable to convert device interface GUID to LUID");
- _initialized = true;
+ //_initialized = true;
if (friendlyName)
setFriendlyName(friendlyName);
@@ -672,6 +678,7 @@ bool WindowsEthernetTap::addIp(const InetAddress &ip)
{
if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
return false;
+
Mutex::Lock _l(_assignedIps_m);
if (std::find(_assignedIps.begin(),_assignedIps.end(),ip) != _assignedIps.end())
return true;
@@ -682,6 +689,9 @@ bool WindowsEthernetTap::addIp(const InetAddress &ip)
bool WindowsEthernetTap::removeIp(const InetAddress &ip)
{
+ if (ip.isV6())
+ return true;
+
{
Mutex::Lock _l(_assignedIps_m);
std::vector<InetAddress>::iterator aip(std::find(_assignedIps.begin(),_assignedIps.end(),ip));
@@ -713,16 +723,19 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip)
DeleteUnicastIpAddressEntry(&(ipt->Table[i]));
FreeMibTable(ipt);
- std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
- std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
- std::string ipstr(ip.toIpString());
- for(std::vector<std::string>::iterator rip(regIps.begin()),rm(regSubnetMasks.begin());((rip!=regIps.end())&&(rm!=regSubnetMasks.end()));++rip,++rm) {
- if (*rip == ipstr) {
- regIps.erase(rip);
- regSubnetMasks.erase(rm);
- _setRegistryIPv4Value("IPAddress",regIps);
- _setRegistryIPv4Value("SubnetMask",regSubnetMasks);
- break;
+ if (ip.isV4()) {
+ std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
+ std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
+ char ipbuf[64];
+ std::string ipstr(ip.toIpString(ipbuf));
+ for (std::vector<std::string>::iterator rip(regIps.begin()), rm(regSubnetMasks.begin()); ((rip != regIps.end()) && (rm != regSubnetMasks.end())); ++rip, ++rm) {
+ if (*rip == ipstr) {
+ regIps.erase(rip);
+ regSubnetMasks.erase(rm);
+ _setRegistryIPv4Value("IPAddress", regIps);
+ _setRegistryIPv4Value("SubnetMask", regSubnetMasks);
+ break;
+ }
}
}
@@ -739,7 +752,7 @@ bool WindowsEthernetTap::removeIp(const InetAddress &ip)
std::vector<InetAddress> WindowsEthernetTap::ips() const
{
- static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
+ static const InetAddress linkLocalLoopback("fe80::1/64"); // what is this and why does Windows assign it?
std::vector<InetAddress> addrs;
if (!_initialized)
@@ -778,12 +791,13 @@ std::vector<InetAddress> WindowsEthernetTap::ips() const
void WindowsEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
- if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > (ZT_IF_MTU)))
+ if ((!_initialized)||(!_enabled)||(_tap == INVALID_HANDLE_VALUE)||(len > _mtu))
return;
Mutex::Lock _l(_injectPending_m);
- _injectPending.push( std::pair<Array<char,ZT_IF_MTU + 32>,unsigned int>(Array<char,ZT_IF_MTU + 32>(),len + 14) );
- char *d = _injectPending.back().first.data;
+ _injectPending.emplace();
+ _injectPending.back().len = len + 14;
+ char *const d = _injectPending.back().data;
to.copyTo(d,6);
from.copyTo(d + 6,6);
d[12] = (char)((etherType >> 8) & 0xff);
@@ -827,7 +841,7 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
// pretty much anything work... IPv4, IPv6, IPX, oldskool Netbios, who knows...
unsigned char mcastbuf[TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE];
DWORD bytesReturned = 0;
- if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)0,0,(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) {
+ if (DeviceIoControl(t,TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS,(LPVOID)mcastbuf,sizeof(mcastbuf),(LPVOID)mcastbuf,sizeof(mcastbuf),&bytesReturned,NULL)) {
if ((bytesReturned > 0)&&(bytesReturned <= TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE)) { // sanity check
MAC mac;
DWORD i = 0;
@@ -861,6 +875,20 @@ void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,
_multicastGroups.swap(newGroups);
}
+void WindowsEthernetTap::setMtu(unsigned int mtu)
+{
+ if (mtu != _mtu) {
+ _mtu = mtu;
+ HKEY nwAdapters;
+ if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ | KEY_WRITE, &nwAdapters) == ERROR_SUCCESS) {
+ char tmps[64];
+ unsigned int tmpsl = OSUtils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu);
+ RegSetKeyValueA(nwAdapters, _mySubkeyName.c_str(), "MTU", REG_SZ, tmps, tmpsl);
+ RegCloseKey(nwAdapters);
+ }
+ }
+}
+
NET_IFINDEX WindowsEthernetTap::interfaceIndex() const
{
NET_IFINDEX idx = -1;
@@ -872,12 +900,12 @@ NET_IFINDEX WindowsEthernetTap::interfaceIndex() const
void WindowsEthernetTap::threadMain()
throw()
{
- char tapReadBuf[ZT_IF_MTU + 32];
+ char tapReadBuf[ZT_MAX_MTU + 32];
char tapPath[128];
HANDLE wait4[3];
OVERLAPPED tapOvlRead,tapOvlWrite;
- Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
+ OSUtils::ztsnprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
try {
while (_run) {
@@ -1001,9 +1029,18 @@ void WindowsEthernetTap::threadMain()
ReadFile(_tap,tapReadBuf,sizeof(tapReadBuf),NULL,&tapOvlRead);
bool writeInProgress = false;
ULONGLONG timeOfLastBorkCheck = GetTickCount64();
+ _initialized = true;
+ unsigned int oldmtu = _mtu;
+
+ setFriendlyName(_friendlyName.c_str());
+
while (_run) {
DWORD waitResult = WaitForMultipleObjectsEx(writeInProgress ? 3 : 2,wait4,FALSE,2500,TRUE);
- if (!_run) break; // will also break outer while(_run)
+ if (!_run) break; // will also break outer while(_run) since _run is false
+
+ // Check for changes in MTU and break to restart tap device to reconfigure in this case
+ if (_mtu != oldmtu)
+ break;
// Check for issues with adapter and close/reopen if any are detected. This
// check fixes a while boatload of Windows adapter 'coma' issues after
@@ -1048,12 +1085,11 @@ void WindowsEthernetTap::threadMain()
MAC from(tapReadBuf + 6,6);
unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff);
try {
- // TODO: decode vlans
- _handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
+ _handler(_arg,(void *)0,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
} catch ( ... ) {} // handlers should not throw
}
}
- ReadFile(_tap,tapReadBuf,ZT_IF_MTU + 32,NULL,&tapOvlRead);
+ ReadFile(_tap,tapReadBuf,ZT_MAX_MTU + 32,NULL,&tapOvlRead);
}
if (writeInProgress) {
@@ -1065,7 +1101,7 @@ void WindowsEthernetTap::threadMain()
} else _injectPending_m.lock();
if (!_injectPending.empty()) {
- WriteFile(_tap,_injectPending.front().first.data,_injectPending.front().second,NULL,&tapOvlWrite);
+ WriteFile(_tap,_injectPending.front().data,_injectPending.front().len,NULL,&tapOvlWrite);
writeInProgress = true;
}
@@ -1195,14 +1231,17 @@ void WindowsEthernetTap::_syncIps()
CreateUnicastIpAddressEntry(&ipr);
}
- std::string ipStr(aip->toString());
- std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
- if (std::find(regIps.begin(),regIps.end(),ipStr) == regIps.end()) {
- std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
- regIps.push_back(ipStr);
- regSubnetMasks.push_back(aip->netmask().toIpString());
- _setRegistryIPv4Value("IPAddress",regIps);
- _setRegistryIPv4Value("SubnetMask",regSubnetMasks);
+ if (aip->isV4()) {
+ char ipbuf[64];
+ std::string ipStr(aip->toIpString(ipbuf));
+ std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
+ if (std::find(regIps.begin(), regIps.end(), ipStr) == regIps.end()) {
+ std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
+ regIps.push_back(ipStr);
+ regSubnetMasks.push_back(aip->netmask().toIpString(ipbuf));
+ _setRegistryIPv4Value("IPAddress", regIps);
+ _setRegistryIPv4Value("SubnetMask", regSubnetMasks);
+ }
}
}
}
diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp
index 0bbb17d8..1e36bdd8 100644
--- a/osdep/WindowsEthernetTap.hpp
+++ b/osdep/WindowsEthernetTap.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_WINDOWSETHERNETTAP_HPP
@@ -30,7 +38,6 @@
#include "../node/Constants.hpp"
#include "../node/Mutex.hpp"
-#include "../node/Array.hpp"
#include "../node/MulticastGroup.hpp"
#include "../node/InetAddress.hpp"
#include "../osdep/Thread.hpp"
@@ -87,7 +94,7 @@ public:
unsigned int metric,
uint64_t nwid,
const char *friendlyName,
- void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+ void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg);
~WindowsEthernetTap();
@@ -101,6 +108,7 @@ public:
std::string deviceName() const;
void setFriendlyName(const char *friendlyName);
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
+ void setMtu(unsigned int mtu);
inline const NET_LUID &luid() const { return _deviceLuid; }
inline const GUID &guid() const { return _deviceGuid; }
@@ -110,16 +118,19 @@ public:
void threadMain()
throw();
+ bool isInitialized() const { return _initialized; };
+
private:
NET_IFINDEX _getDeviceIndex(); // throws on failure
std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
void _syncIps();
- void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+ void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg;
MAC _mac;
uint64_t _nwid;
+ volatile unsigned int _mtu;
Thread _thread;
volatile HANDLE _tap;
@@ -129,13 +140,21 @@ private:
NET_LUID _deviceLuid;
std::string _netCfgInstanceId;
std::string _deviceInstanceId;
+ std::string _mySubkeyName;
+
+ std::string _friendlyName;
std::vector<InetAddress> _assignedIps; // IPs assigned with addIp
Mutex _assignedIps_m;
std::vector<MulticastGroup> _multicastGroups;
- std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
+ struct _InjectPending
+ {
+ unsigned int len;
+ char data[ZT_MAX_MTU + 32];
+ };
+ std::queue<_InjectPending> _injectPending;
Mutex _injectPending_m;
std::string _pathToHelpers;
diff --git a/rule-compiler/README.md b/rule-compiler/README.md
new file mode 100644
index 00000000..1ceeb713
--- /dev/null
+++ b/rule-compiler/README.md
@@ -0,0 +1,8 @@
+ZeroTier Rules Compiler
+======
+
+This script converts ZeroTier rules in human-readable format into rules suitable for import into a ZeroTier network controller. It's the script that is used in the rules editor on [ZeroTier Central](https://my.zerotier.com/).
+
+A command line interface is included that may be invoked as: `node cli.js <rules script>`.
+
+See the [manual](https://www.zerotier.com/manual.shtml) for information about the rules engine and rules script syntax.
diff --git a/rule-compiler/cli.js b/rule-compiler/cli.js
new file mode 100644
index 00000000..75235ac8
--- /dev/null
+++ b/rule-compiler/cli.js
@@ -0,0 +1,47 @@
+'use strict';
+
+var fs = require('fs');
+var RuleCompiler = require('./rule-compiler.js');
+
+if (process.argv.length < 3) {
+ console.log('Usage: node cli.js <rules script>');
+ process.exit(1);
+}
+
+var rules = [];
+var caps = {};
+var tags = {};
+var err = RuleCompiler.compile(fs.readFileSync(process.argv[2]).toString(),rules,caps,tags);
+
+if (err) {
+ console.error('ERROR parsing '+process.argv[2]+' line '+err[0]+' column '+err[1]+': '+err[2]);
+ process.exit(1);
+} else {
+ let capsArray = [];
+ let capabilitiesByName = {};
+ for(let n in caps) {
+ capsArray.push(caps[n]);
+ capabilitiesByName[n] = caps[n].id;
+ }
+ let tagsArray = [];
+ for(let n in tags) {
+ let t = tags[n];
+ let dfl = t['default'];
+ tagsArray.push({
+ 'id': t.id,
+ 'default': (((dfl)||(dfl === 0)) ? dfl : null)
+ });
+ }
+
+ console.log(JSON.stringify({
+ config: {
+ rules: rules,
+ capabilities: capsArray,
+ tags: tagsArray
+ },
+ capabilitiesByName: capabilitiesByName,
+ tagsByName: tags
+ },null,1));
+
+ process.exit(0);
+}
diff --git a/rule-compiler/examples/capabilities-and-tags.ztrules b/rule-compiler/examples/capabilities-and-tags.ztrules
new file mode 100644
index 00000000..9b35f28d
--- /dev/null
+++ b/rule-compiler/examples/capabilities-and-tags.ztrules
@@ -0,0 +1,40 @@
+# This is a default rule set that allows IPv4 and IPv6 traffic.
+# You can edit as needed. If your rule set gets large we recommend
+# cutting and pasting it somewhere to keep a backup.
+
+# Drop all Ethernet frame types that are not IPv4 or IPv6
+drop
+ not ethertype 0x0800 # IPv4
+ not ethertype 0x0806 # IPv4 ARP
+ not ethertype 0x86dd # IPv6
+;
+
+# Capability: outgoing SSH
+cap ssh
+ id 1000
+ accept
+ ipprotocol tcp
+ dport 22
+ ;
+;
+
+# A tag indicating which department people belong to
+tag department
+ id 1000
+ enum 100 sales
+ enum 200 marketing
+ enum 300 accounting
+ enum 400 engineering
+;
+
+# Accept all traffic between members of the same department
+accept
+ tdiff department 0
+;
+
+# You can insert other drop, tee, etc. rules here. This rule
+# set ends with a blanket accept, making it permissive by
+# default.
+
+accept;
+
diff --git a/rule-compiler/package.json b/rule-compiler/package.json
new file mode 100644
index 00000000..1db006b3
--- /dev/null
+++ b/rule-compiler/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "zerotier-rule-compiler",
+ "version": "1.2.2-2",
+ "description": "ZeroTier Rule Script Compiler",
+ "main": "cli.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/zerotier/ZeroTierOne/rule-compiler"
+ },
+ "keywords": [
+ "ZeroTier"
+ ],
+ "author": "ZeroTier, Inc. <contact@zerotier.com>",
+ "license": "GPL-2.0"
+}
diff --git a/rule-compiler/rule-compiler.js b/rule-compiler/rule-compiler.js
new file mode 100644
index 00000000..bd84824e
--- /dev/null
+++ b/rule-compiler/rule-compiler.js
@@ -0,0 +1,904 @@
+'use strict';
+
+// Names for bits in characteristics -- 0==LSB, 63==MSB
+const CHARACTERISTIC_BITS = {
+ 'inbound': 63,
+ 'multicast': 62,
+ 'broadcast': 61,
+ 'ipauth': 60,
+ 'macauth': 59,
+ 'tcp_fin': 0,
+ 'tcp_syn': 1,
+ 'tcp_rst': 2,
+ 'tcp_psh': 3,
+ 'tcp_ack': 4,
+ 'tcp_urg': 5,
+ 'tcp_ece': 6,
+ 'tcp_cwr': 7,
+ 'tcp_ns': 8,
+ 'tcp_rs2': 9,
+ 'tcp_rs1': 10,
+ 'tcp_rs0': 11
+};
+
+// Shorthand names for common ethernet types
+const ETHERTYPES = {
+ 'ipv4': 0x0800,
+ 'arp': 0x0806,
+ 'wol': 0x0842,
+ 'rarp': 0x8035,
+ 'ipv6': 0x86dd,
+ 'atalk': 0x809b,
+ 'aarp': 0x80f3,
+ 'ipx_a': 0x8137,
+ 'ipx_b': 0x8138
+};
+
+// Shorthand names for common IP protocols
+const IP_PROTOCOLS = {
+ 'icmp': 0x01,
+ 'icmp4': 0x01,
+ 'icmpv4': 0x01,
+ 'igmp': 0x02,
+ 'ipip': 0x04,
+ 'tcp': 0x06,
+ 'egp': 0x08,
+ 'igp': 0x09,
+ 'udp': 0x11,
+ 'rdp': 0x1b,
+ 'esp': 0x32,
+ 'ah': 0x33,
+ 'icmp6': 0x3a,
+ 'icmpv6': 0x3a,
+ 'l2tp': 0x73,
+ 'sctp': 0x84,
+ 'udplite': 0x88
+};
+
+// Keywords that open new blocks that must be terminated by a semicolon
+const OPEN_BLOCK_KEYWORDS = {
+ 'macro': true,
+ 'tag': true,
+ 'cap': true,
+ 'drop': true,
+ 'accept': true,
+ 'tee': true,
+ 'watch': true,
+ 'redirect': true,
+ 'break': true
+};
+
+// Reserved words that can't be used as tag, capability, or rule set names
+const RESERVED_WORDS = {
+ 'macro': true,
+ 'tag': true,
+ 'cap': true,
+ 'default': true,
+
+ 'drop': true,
+ 'accept': true,
+ 'tee': true,
+ 'watch': true,
+ 'redirect': true,
+ 'break': true,
+
+ 'ztsrc': true,
+ 'ztdest': true,
+ 'vlan': true,
+ 'vlanpcp': true,
+ 'vlandei': true,
+ 'ethertype': true,
+ 'macsrc': true,
+ 'macdest': true,
+ 'ipsrc': true,
+ 'ipdest': true,
+ 'iptos': true,
+ 'ipprotocol': true,
+ 'icmp': true,
+ 'sport': true,
+ 'dport': true,
+ 'chr': true,
+ 'framesize': true,
+ 'random': true,
+ 'tand': true,
+ 'tor': true,
+ 'txor': true,
+ 'tdiff': true,
+ 'teq': true,
+ 'tseq': true,
+ 'treq': true,
+
+ 'type': true,
+ 'enum': true,
+ 'class': true,
+ 'define': true,
+ 'import': true,
+ 'include': true,
+ 'log': true,
+ 'not': true,
+ 'xor': true,
+ 'or': true,
+ 'and': true,
+ 'set': true,
+ 'var': true,
+ 'let': true
+};
+
+const KEYWORD_TO_API_MAP = {
+ 'drop': 'ACTION_DROP',
+ 'accept': 'ACTION_ACCEPT',
+ 'tee': 'ACTION_TEE',
+ 'watch': 'ACTION_WATCH',
+ 'redirect': 'ACTION_REDIRECT',
+ 'break': 'ACTION_BREAK',
+
+ 'ztsrc': 'MATCH_SOURCE_ZEROTIER_ADDRESS',
+ 'ztdest': 'MATCH_DEST_ZEROTIER_ADDRESS',
+ 'vlan': 'MATCH_VLAN_ID',
+ 'vlanpcp': 'MATCH_VLAN_PCP',
+ 'vlandei': 'MATCH_VLAN_DEI',
+ 'ethertype': 'MATCH_ETHERTYPE',
+ 'macsrc': 'MATCH_MAC_SOURCE',
+ 'macdest': 'MATCH_MAC_DEST',
+ //'ipsrc': '', // special handling since we programmatically differentiate between V4 and V6
+ //'ipdest': '', // special handling
+ 'iptos': 'MATCH_IP_TOS',
+ 'ipprotocol': 'MATCH_IP_PROTOCOL',
+ 'icmp': 'MATCH_ICMP',
+ 'sport': 'MATCH_IP_SOURCE_PORT_RANGE',
+ 'dport': 'MATCH_IP_DEST_PORT_RANGE',
+ 'chr': 'MATCH_CHARACTERISTICS',
+ 'framesize': 'MATCH_FRAME_SIZE_RANGE',
+ 'random': 'MATCH_RANDOM',
+ 'tand': 'MATCH_TAGS_BITWISE_AND',
+ 'tor': 'MATCH_TAGS_BITWISE_OR',
+ 'txor': 'MATCH_TAGS_BITWISE_XOR',
+ 'tdiff': 'MATCH_TAGS_DIFFERENCE',
+ 'teq': 'MATCH_TAGS_EQUAL',
+ 'tseq': 'MATCH_TAG_SENDER',
+ 'treq': 'MATCH_TAG_RECEIVER'
+};
+
+// Number of args for each match
+const MATCH_ARG_COUNTS = {
+ 'ztsrc': 1,
+ 'ztdest': 1,
+ 'vlan': 1,
+ 'vlanpcp': 1,
+ 'vlandei': 1,
+ 'ethertype': 1,
+ 'macsrc': 1,
+ 'macdest': 1,
+ 'ipsrc': 1,
+ 'ipdest': 1,
+ 'iptos': 2,
+ 'ipprotocol': 1,
+ 'icmp': 2,
+ 'sport': 1,
+ 'dport': 1,
+ 'chr': 1,
+ 'framesize': 1,
+ 'random': 1,
+ 'tand': 2,
+ 'tor': 2,
+ 'txor': 2,
+ 'tdiff': 2,
+ 'teq': 2,
+ 'tseq': 2,
+ 'treq': 2
+};
+
+// Regex of all alphanumeric characters in Unicode
+const INTL_ALPHANUM_REGEX = new RegExp('[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]');
+
+// Checks whether something is a valid capability, tag, or macro name
+function _isValidName(n)
+{
+ if ((typeof n !== 'string')||(n.length === 0)) return false;
+ if ("0123456789".indexOf(n.charAt(0)) >= 0) return false;
+ for(let i=0;i<n.length;++i) {
+ let c = n.charAt(i);
+ if ((c !== '_')&&(!INTL_ALPHANUM_REGEX.test(c))) return false;
+ }
+ return true;
+}
+
+// Regexes for checking the basic syntactic validity of IP addresses
+const IPV6_REGEX = new RegExp('(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))');
+const IPV4_REGEX = new RegExp('((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])');
+
+function _parseNum(n)
+{
+ try {
+ if ((typeof n !== 'string')||(n.length === 0))
+ return -1;
+ n = n.toLowerCase();
+ if ((n.length > 2)&&(n.substr(0,2) === '0x'))
+ n = parseInt(n.substr(2),16);
+ else n = parseInt(n,10);
+ return (((typeof n === 'number')&&(n !== null)&&(!isNaN(n))) ? n : -1);
+ } catch (e) {
+ return -1;
+ }
+}
+
+function _cleanMac(m)
+{
+ m = m.toLowerCase();
+ var m2 = '';
+ for(let i=0;((i<m.length)&&(m2.length<17));++i) {
+ let c = m.charAt(i);
+ if ("0123456789abcdef".indexOf(c) >= 0) {
+ m2 += c;
+ if ((m2.length > 0)&&(m2.length !== 17)&&((m2.length & 1) === 0))
+ m2 += ':';
+ }
+ }
+ return m2;
+}
+
+function _cleanHex(m)
+{
+ m = m.toLowerCase();
+ var m2 = '';
+ for(let i=0;i<m.length;++i) {
+ let c = m.charAt(i);
+ if ("0123456789abcdef".indexOf(c) >= 0)
+ m2 += c;
+ }
+ return m2;
+}
+
+function _renderMatches(mtree,rules,macros,caps,tags,params)
+{
+ let not = false;
+ let or = false;
+ for(let k=0;k<mtree.length;++k) {
+ let match = (typeof mtree[k][0] === 'string') ? mtree[k][0].toLowerCase() : '';
+ if ((match.length === 0)||(match === 'and')) { // AND is the default
+ continue;
+ } else if (match === 'not') {
+ not = true;
+ } else if (match === 'or') {
+ or = true;
+ } else {
+ let args = [];
+ let argCount = MATCH_ARG_COUNTS[match];
+ if (!argCount)
+ return [ mtree[k][1],mtree[k][2],'Unrecognized match type "'+match+'".' ];
+ for(let i=0;i<argCount;++i) {
+ if (++k >= mtree.length)
+ return [ mtree[k - 1][1],mtree[k - 1][2],'Missing argument(s) to match.' ];
+ let arg = mtree[k][0];
+ if ((typeof arg !== 'string')||(arg in RESERVED_WORDS)||(arg.length === 0))
+ return [ mtree[k - 1][1],mtree[k - 1][2],'Missing argument(s) to match (invalid argument or argument is reserved word).' ];
+ if (arg.charAt(0) === '$') {
+ let tmp = params[arg];
+ if (typeof tmp === 'undefined')
+ return [ mtree[k][1],mtree[k][2],'Undefined variable name.' ];
+ args.push([ tmp,mtree[k][1],mtree[k][2] ]);
+ } else {
+ args.push(mtree[k]);
+ }
+ }
+
+ switch(match) {
+ case 'ztsrc':
+ case 'ztdest': {
+ let zt = _cleanHex(args[0][0]);
+ if (zt.length !== 10)
+ return [ args[0][1],args[0][2],'Invalid ZeroTier address.' ];
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[match],
+ 'not': not,
+ 'or': or,
+ 'zt': zt
+ });
+ } break;
+
+ case 'vlan':
+ case 'vlanpcp':
+ case 'vlandei':
+ case 'ethertype':
+ case 'ipprotocol': {
+ let num = null;
+ switch (match) {
+ case 'ethertype': num = ETHERTYPES[args[0][0]]; break;
+ case 'ipprotocol': num = IP_PROTOCOLS[args[0][0]]; break;
+ }
+ if (typeof num !== 'number')
+ num = _parseNum(args[0][0]);
+ if ((typeof num !== 'number')||(num < 0)||(num > 0xffffffff)||(num === null))
+ return [ args[0][1],args[0][2],'Invalid numeric value.' ];
+ let r = {
+ 'type': KEYWORD_TO_API_MAP[match],
+ 'not': not,
+ 'or': or
+ };
+ switch(match) {
+ case 'vlan': r['vlanId'] = num; break;
+ case 'vlanpcp': r['vlanPcp'] = num; break;
+ case 'vlandei': r['vlanDei'] = num; break;
+ case 'ethertype': r['etherType'] = num; break;
+ case 'ipprotocol': r['ipProtocol'] = num; break;
+ }
+ rules.push(r);
+ } break;
+
+ case 'random': {
+ let num = parseFloat(args[0][0])||0.0;
+ if (num < 0.0) num = 0.0;
+ if (num > 1.0) num = 1.0;
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[match],
+ 'not': not,
+ 'or': or,
+ 'probability': Math.floor(4294967295 * num)
+ });
+ } break;
+
+ case 'macsrc':
+ case 'macdest': {
+ let mac = _cleanMac(args[0][0]);
+ if (mac.length !== 17)
+ return [ args[0][1],args[0][2],'Invalid MAC address.' ];
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[match],
+ 'not': not,
+ 'or': or,
+ 'mac': mac
+ });
+ } break;
+
+ case 'ipsrc':
+ case 'ipdest': {
+ let ip = args[0][0];
+ let slashIdx = ip.indexOf('/');
+ if (slashIdx <= 0)
+ return [ args[0][1],args[0][2],'Missing /bits netmask length designation in IP.' ];
+ let ipOnly = ip.substr(0,slashIdx);
+ if (IPV6_REGEX.test(ipOnly)) {
+ rules.push({
+ 'type': ((match === 'ipsrc') ? 'MATCH_IPV6_SOURCE' : 'MATCH_IPV6_DEST'),
+ 'not': not,
+ 'or': or,
+ 'ip': ip
+ });
+ } else if (IPV4_REGEX.test(ipOnly)) {
+ rules.push({
+ 'type': ((match === 'ipsrc') ? 'MATCH_IPV4_SOURCE' : 'MATCH_IPV4_DEST'),
+ 'not': not,
+ 'or': or,
+ 'ip': ip
+ });
+ } else {
+ return [ args[0][1],args[0][2],'Invalid IP address (not valid IPv4 or IPv6).' ];
+ }
+ } break;
+
+ case 'icmp': {
+ let icmpType = _parseNum(args[0][0]);
+ if ((icmpType < 0)||(icmpType > 0xff))
+ return [ args[0][1],args[0][2],'Missing or invalid ICMP type.' ];
+ let icmpCode = _parseNum(args[1][0]); // -1 okay, indicates don't match code
+ if (icmpCode > 0xff)
+ return [ args[1][1],args[1][2],'Invalid ICMP code (use -1 for none).' ];
+ rules.push({
+ 'type': 'MATCH_ICMP',
+ 'not': not,
+ 'or': or,
+ 'icmpType': icmpType,
+ 'icmpCode': ((icmpCode < 0) ? null : icmpCode)
+ });
+ } break;
+
+ case 'sport':
+ case 'dport':
+ case 'framesize': {
+ let arg = args[0][0];
+ let fn = null;
+ let tn = null;
+ if (arg.indexOf('-') > 0) {
+ let asplit = arg.split('-');
+ if (asplit.length !== 2) {
+ return [ args[0][1],args[0][2],'Invalid numeric range.' ];
+ } else {
+ fn = _parseNum(asplit[0]);
+ tn = _parseNum(asplit[1]);
+ }
+ } else {
+ fn = _parseNum(arg);
+ tn = fn;
+ }
+ if ((fn < 0)||(fn > 0xffff)||(tn < 0)||(tn > 0xffff)||(tn < fn))
+ return [ args[0][1],args[0][2],'Invalid numeric range.' ];
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[match],
+ 'not': not,
+ 'or': or,
+ 'start': fn,
+ 'end': tn
+ });
+ } break;
+
+ case 'iptos': {
+ let mask = _parseNum(args[0][0]);
+ if ((typeof mask !== 'number')||(mask < 0)||(mask > 0xff)||(mask === null))
+ return [ args[0][1],args[0][2],'Invalid mask.' ];
+ let arg = args[1][0];
+ let fn = null;
+ let tn = null;
+ if (arg.indexOf('-') > 0) {
+ let asplit = arg.split('-');
+ if (asplit.length !== 2) {
+ return [ args[1][1],args[1][2],'Invalid value range.' ];
+ } else {
+ fn = _parseNum(asplit[0]);
+ tn = _parseNum(asplit[1]);
+ }
+ } else {
+ fn = _parseNum(arg);
+ tn = fn;
+ }
+ if ((fn < 0)||(fn > 0xff)||(tn < 0)||(tn > 0xff)||(tn < fn))
+ return [ args[1][1],args[1][2],'Invalid value range.' ];
+ rules.push({
+ 'type': 'MATCH_IP_TOS',
+ 'not': not,
+ 'or': or,
+ 'mask': mask,
+ 'start': fn,
+ 'end': tn
+ });
+ } break;
+
+ case 'chr': {
+ let chrb = args[0][0].split(/[,]+/);
+ let maskhi = 0;
+ let masklo = 0;
+ for(let i=0;i<chrb.length;++i) {
+ if (chrb[i].length > 0) {
+ let tmp = CHARACTERISTIC_BITS[chrb[i]];
+ let bit = (typeof tmp === 'number') ? tmp : _parseNum(chrb[i]);
+ if ((bit < 0)||(bit > 63))
+ return [ args[0][1],args[0][2],'Invalid bit index (range 0-63) or unrecognized name.' ];
+ if (bit >= 32)
+ maskhi |= Math.abs(1 << (bit - 32));
+ else masklo |= Math.abs(1 << bit);
+ }
+ }
+ maskhi = Math.abs(maskhi).toString(16);
+ while (maskhi.length < 8) maskhi = '0' + maskhi;
+ masklo = Math.abs(masklo).toString(16);
+ while (masklo.length < 8) masklo = '0' + masklo;
+ rules.push({
+ 'type': 'MATCH_CHARACTERISTICS',
+ 'not': not,
+ 'or': or,
+ 'mask': (maskhi + masklo)
+ });
+ } break;
+
+ case 'tand':
+ case 'tor':
+ case 'txor':
+ case 'tdiff':
+ case 'teq':
+ case 'tseq':
+ case 'treq': {
+ let tag = tags[args[0][0]];
+ let tagId = -1;
+ let tagValue = -1;
+ if (tag) {
+ tagId = tag.id;
+ tagValue = args[1][0];
+ if (tagValue in tag.flags)
+ tagValue = tag.flags[tagValue];
+ else if (tagValue in tag.enums)
+ tagValue = tag.enums[tagValue];
+ else tagValue = _parseNum(tagValue);
+ } else {
+ tagId = _parseNum(args[0][0]);
+ tagValue = _parseNum(args[1][0]);
+ }
+ if ((tagId < 0)||(tagId > 0xffffffff))
+ return [ args[0][1],args[0][2],'Undefined tag name and invalid tag value.' ];
+ if ((tagValue < 0)||(tagValue > 0xffffffff))
+ return [ args[1][1],args[1][2],'Invalid tag value or unrecognized flag/enum name.' ];
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[match],
+ 'not': not,
+ 'or': or,
+ 'id': tagId,
+ 'value': tagValue
+ });
+ } break;
+ }
+
+ not = false;
+ or = false;
+ }
+ }
+ return null;
+}
+
+function _renderActions(rtree,rules,macros,caps,tags,params)
+{
+ for(let k=0;k<rtree.length;++k) {
+ let action = (typeof rtree[k][0] === 'string') ? rtree[k][0].toLowerCase() : '';
+ if (action.length === 0) {
+ continue;
+ } else if (action === 'include') {
+ if ((k + 1) >= rtree.length)
+ return [ rtree[k][1],rtree[k][2],'Include directive is missing a macro name.' ];
+ let macroName = rtree[k + 1][0];
+ ++k;
+
+ let macroParamArray = [];
+ let parenIdx = macroName.indexOf('(');
+ if (parenIdx > 0) {
+ let pns = macroName.substr(parenIdx + 1).split(/[,)]+/);
+ for(let k=0;k<pns.length;++k) {
+ if (pns[k].length > 0)
+ macroParamArray.push(pns[k]);
+ }
+ macroName = macroName.substr(0,parenIdx);
+ }
+
+ let macro = macros[macroName];
+ if (!macro)
+ return [ rtree[k][1],rtree[k][2],'Macro name not found.' ];
+ let macroParams = {};
+ for(let param in macro.params) {
+ let pidx = macro.params[param];
+ if (pidx >= macroParamArray.length)
+ return [ rtree[k][1],rtree[k][2],'Missing one or more required macro parameter.' ];
+ macroParams[param] = macroParamArray[pidx];
+ }
+
+ let err = _renderActions(macro.rules,rules,macros,caps,tags,macroParams);
+ if (err !== null)
+ return err;
+ } else if ((action === 'drop')||(action === 'accept')||(action === 'break')) { // actions without arguments
+ if (((k + 1) < rtree.length)&&(Array.isArray(rtree[k + 1][0]))) {
+ let mtree = rtree[k + 1]; ++k;
+ let err = _renderMatches(mtree,rules,macros,caps,tags,params);
+ if (err !== null)
+ return err;
+ }
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[action]
+ });
+ } else if ((action === 'tee')||(action === 'watch')) { // actions with arguments (ZeroTier address)
+ if (((k + 1) < rtree.length)&&(Array.isArray(rtree[k + 1][0]))&&(rtree[k + 1][0].length >= 2)) {
+ let mtree = rtree[k + 1]; ++k;
+ let maxLength = _parseNum(mtree[0][0]);
+ if ((maxLength < -1)||(maxLength > 0xffff))
+ return [ mtree[0][1],mtree[1][2],'Tee/watch max packet length to forward invalid or out of range.' ];
+ let target = mtree[1][0];
+ if ((typeof target !== 'string')||(target.length !== 10))
+ return [ mtree[1][1],mtree[1][2],'Missing or invalid ZeroTier address target for tee/watch.' ];
+ let err = _renderMatches(mtree.slice(2),rules,macros,caps,tags,params);
+ if (err !== null)
+ return err;
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[action],
+ 'address': target,
+ 'length': maxLength
+ });
+ } else {
+ return [ rtree[k][1],rtree[k][2],'The tee and watch actions require two paremters (max length or 0 for all, target).' ];
+ }
+ } else if (action === 'redirect') {
+ if (((k + 1) < rtree.length)&&(Array.isArray(rtree[k + 1][0]))&&(rtree[k + 1][0].length >= 1)) {
+ let mtree = rtree[k + 1]; ++k;
+ let target = mtree[0][0];
+ if ((typeof target !== 'string')||(target.length !== 10))
+ return [ mtree[0][1],mtree[0][2],'Missing or invalid ZeroTier address target for redirect.' ];
+ let err = _renderMatches(mtree.slice(1),rules,macros,caps,tags,params);
+ if (err !== null)
+ return err;
+ rules.push({
+ 'type': KEYWORD_TO_API_MAP[action],
+ 'address': target
+ });
+ } else {
+ return [ rtree[k][1],rtree[k][2],'The redirect action requires a target parameter.' ];
+ }
+ } else {
+ return [ rtree[k][1],rtree[k][2],'Unrecognized action or directive in rule set.' ];
+ }
+ }
+
+ return null;
+}
+
+function compile(src,rules,caps,tags)
+{
+ try {
+ if (typeof src !== 'string')
+ return [ 0,0,'"src" parameter must be a string.' ];
+
+ // Pass 1: parse source into a tree of arrays of elements. Each element is a 3-item
+ // tuple consisting of string, line number, and character index in line to enable
+ // informative error messages to be returned.
+
+ var blockStack = [ [] ];
+ var curr = [ '',-1,-1 ];
+ var skipRestOfLine = false;
+ for(let idx=0,lineNo=1,lineIdx=0;idx<src.length;++idx,++lineIdx) {
+ let ch = src.charAt(idx);
+ if (skipRestOfLine) {
+ if ((ch === '\r')||(ch === '\n')) {
+ skipRestOfLine = false;
+ ++lineNo;
+ lineIdx = 0;
+ }
+ } else {
+ switch(ch) {
+ case '\n':
+ ++lineNo;
+ lineIdx = 0;
+ case '\r':
+ case '\t':
+ case ' ':
+ if (curr[0].length > 0) {
+ let endOfBlock = false;
+ if (curr[0].charAt(curr[0].length - 1) === ';') {
+ endOfBlock = true;
+ curr[0] = curr[0].substr(0,curr[0].length - 1);
+ }
+
+ if (curr[0].length > 0) {
+ blockStack[blockStack.length - 1].push(curr);
+ }
+ if ((endOfBlock)&&(blockStack.length > 1)&&(blockStack[blockStack.length - 1].length > 0)) {
+ blockStack[blockStack.length - 2].push(blockStack[blockStack.length - 1]);
+ blockStack.pop();
+ } else if (curr[0] in OPEN_BLOCK_KEYWORDS) {
+ blockStack.push([]);
+ }
+
+ curr = [ '',-1,-1 ];
+ }
+ break;
+ default:
+ if (curr[0].length === 0) {
+ if (ch === '#') {
+ skipRestOfLine = true;
+ continue;
+ } else {
+ curr[1] = lineNo;
+ curr[2] = lineIdx;
+ }
+ }
+ curr[0] += ch;
+ break;
+ }
+ }
+ }
+
+ if (curr[0].length > 0) {
+ if (curr[0].charAt(curr[0].length - 1) === ';')
+ curr[0] = curr[0].substr(0,curr[0].length - 1);
+ if (curr[0].length > 0)
+ blockStack[blockStack.length - 1].push(curr);
+ }
+ while ((blockStack.length > 1)&&(blockStack[blockStack.length - 1].length > 0)) {
+ blockStack[blockStack.length - 2].push(blockStack[blockStack.length - 1]);
+ blockStack.pop();
+ }
+ var parsed = blockStack[0];
+
+ // Pass 2: parse tree into capabilities, tags, rule sets, and document-level rules.
+
+ let baseRuleTree = [];
+ let macros = {};
+ for(let i=0;i<parsed.length;++i) {
+ let keyword = (typeof parsed[i][0] === 'string') ? parsed[i][0].toLowerCase() : null;
+ if (keyword === 'macro') {
+ // Define macros
+
+ if ( ((i + 1) >= parsed.length) || (!Array.isArray(parsed[i + 1])) || (parsed[i + 1].length < 1) || (!Array.isArray(parsed[i + 1][0])) )
+ return [ parsed[i][1],parsed[i][2],'Macro definition is missing name.' ];
+ let macro = parsed[++i];
+ let macroName = macro[0][0].toLowerCase();
+
+ let params = {};
+ let parenIdx = macroName.indexOf('(');
+ if (parenIdx > 0) {
+ let pns = macroName.substr(parenIdx + 1).split(/[,)]+/);
+ for(let k=0;k<pns.length;++k) {
+ if (pns[k].length > 0)
+ params[pns[k]] = k;
+ }
+ macroName = macroName.substr(0,parenIdx);
+ }
+
+ if (!_isValidName(macroName))
+ return [ macro[0][1],macro[0][2],'Invalid macro name.' ];
+ if (macroName in RESERVED_WORDS)
+ return [ macro[0][1],macro[0][2],'Macro name is a reserved word.' ];
+
+ if (macroName in macros)
+ return [ macro[0][1],macro[0][2],'Multiple definition of macro name.' ];
+
+ macros[macroName] = {
+ params: params,
+ rules: macro.slice(1)
+ };
+ } else if (keyword === 'tag') {
+ // Define tags
+
+ if ( ((i + 1) >= parsed.length) || (!Array.isArray(parsed[i + 1])) || (parsed[i + 1].length < 1) || (!Array.isArray(parsed[i + 1][0])) )
+ return [ parsed[i][1],parsed[i][2],'Tag definition is missing name.' ];
+ let tag = parsed[++i];
+ let tagName = tag[0][0].toLowerCase();
+
+ if (!_isValidName(tagName))
+ return [ tag[0][1],tag[0][2],'Invalid tag name.' ];
+ if (tagName in RESERVED_WORDS)
+ return [ tag[0][1],tag[0][2],'Tag name is a reserved word.' ];
+
+ if (tagName in tags)
+ return [ tag[0][1],tag[0][2],'Multiple definition of tag name.' ];
+
+ let flags = {};
+ let enums = {};
+ let id = -1;
+ let dfl = null;
+ for(let k=1;k<tag.length;++k) {
+ let tkeyword = tag[k][0].toLowerCase();
+ if (tkeyword === 'id') {
+ if (id >= 0)
+ return [ tag[k][1],tag[k][2],'Duplicate tag id definition.' ];
+ if ((k + 1) >= tag.length)
+ return [ tag[k][1],tag[k][2],'Missing numeric value for ID.' ];
+ id = _parseNum(tag[++k][0]);
+ if ((id < 0)||(id > 0xffffffff))
+ return [ tag[k][1],tag[k][2],'Invalid or out of range tag ID.' ];
+ } else if (tkeyword === 'default') {
+ if (dfl !== null)
+ return [ tag[k][1],tag[k][2],'Duplicate tag default directive.' ];
+ if ((k + 1) >= tag.length)
+ return [ tag[k][1],tag[k][2],'Missing value for default.' ];
+ dfl = tag[++k][0];
+ } else if (tkeyword === 'flag') {
+ if ((k + 2) >= tag.length)
+ return [ tag[k][1],tag[k][2],'Missing tag flag name or bit index.' ];
+ ++k;
+ let bits = tag[k][0].split(/[,]+/);
+ let mask = 0;
+ for(let j=0;j<bits.length;++j) {
+ let b = bits[j].toLowerCase();
+ if (b in flags) {
+ mask |= flags[b];
+ } else {
+ b = _parseNum(b);
+ if ((b < 0)||(b > 31))
+ return [ tag[k][1],tag[k][2],'Bit index invalid, out of range, or references an undefined flag name.' ];
+ mask |= (1 << b);
+ }
+ }
+ let flagName = tag[++k][0].toLowerCase();
+ if (!_isValidName(flagName))
+ return [ tag[k][1],tag[k][2],'Invalid or reserved flag name.' ];
+ if (flagName in flags)
+ return [ tag[k][1],tag[k][2],'Duplicate flag name in tag definition.' ];
+ flags[flagName] = mask;
+ } else if (tkeyword === 'enum') {
+ if ((k + 2) >= tag.length)
+ return [ tag[k][1],tag[k][2],'Missing tag enum name or value.' ];
+ ++k;
+ let value = _parseNum(tag[k][0]);
+ if ((value < 0)||(value > 0xffffffff))
+ return [ tag[k][1],tag[k][2],'Tag enum value invalid or out of range.' ];
+ let enumName = tag[++k][0].toLowerCase();
+ if (!_isValidName(enumName))
+ return [ tag[k][1],tag[k][2],'Invalid or reserved tag enum name.' ];
+ if (enumName in enums)
+ return [ tag[k][1],tag[k][2],'Duplicate enum name in tag definition.' ];
+ enums[enumName] = value;
+ } else {
+ return [ tag[k][1],tag[k][2],'Unrecognized keyword in tag definition.' ];
+ }
+ }
+ if (id < 0)
+ return [ tag[0][1],tag[0][2],'Tag definition is missing a numeric ID.' ];
+
+ if (typeof dfl === 'string') {
+ let dfl2 = enums[dfl];
+ if (typeof dfl2 === 'number') {
+ dfl = dfl2;
+ } else {
+ dfl2 = flags[dfl];
+ if (typeof dfl2 === 'number') {
+ dfl = dfl2;
+ } else {
+ dfl = Math.abs(parseInt(dfl)||0) & 0xffffffff;
+ }
+ }
+ } else if (typeof dfl === 'number') {
+ dfl = Math.abs(dfl) & 0xffffffff;
+ }
+
+ tags[tagName] = {
+ 'id': id,
+ 'default': dfl,
+ 'enums': enums,
+ 'flags': flags
+ };
+ } else if (keyword === 'cap') {
+ // Define capabilities
+
+ if ( ((i + 1) >= parsed.length) || (!Array.isArray(parsed[i + 1])) || (parsed[i + 1].length < 1) || (!Array.isArray(parsed[i + 1][0])) )
+ return [ parsed[i][1],parsed[i][2],'Capability definition is missing name.' ];
+ let cap = parsed[++i];
+ let capName = cap[0][0].toLowerCase();
+
+ if (!_isValidName(capName))
+ return [ cap[0][1],cap[0][2],'Invalid capability name.' ];
+ if (capName in RESERVED_WORDS)
+ return [ cap[0][1],cap[0][2],'Capability name is a reserved word.' ];
+
+ if (capName in caps)
+ return [ cap[0][1],cap[0][2],'Multiple definition of capability name.' ];
+
+ let capRules = [];
+ let id = -1;
+ let dfl = false;
+ for(let k=1;k<cap.length;++k) {
+ let dn = (typeof cap[k][0] === 'string') ? cap[k][0].toLowerCase() : null;
+ if (dn === 'id') {
+ if (id >= 0)
+ return [ cap[k][1],cap[k][2],'Duplicate id directive in capability definition.' ];
+ if ((k + 1) >= cap.length)
+ return [ cap[k][1],cap[k][2],'Missing value for ID.' ];
+ id = _parseNum(cap[++k][0]);
+ if ((id < 0)||(id > 0xffffffff))
+ return [ cap[k - 1][1],cap[k - 1][2],'Invalid or out of range capability ID.' ];
+ for(let cn in caps) {
+ if (caps[cn].id === id)
+ return [ cap[k - 1][1],cap[k - 1][2],'Duplicate capability ID.' ];
+ }
+ } else if (dn === 'default') {
+ dfl = true;
+ } else {
+ capRules.push(cap[k]);
+ }
+ }
+ if (id < 0)
+ return [ cap[0][1],cap[0][2],'Capability definition is missing a numeric ID.' ];
+
+ caps[capName] = {
+ 'id': id,
+ 'default': dfl,
+ 'rules': capRules
+ };
+ } else {
+ baseRuleTree.push(parsed[i]);
+ }
+ }
+
+ // Pass 3: render low-level ZeroTier rules arrays for capabilities and base.
+
+ for(let capName in caps) {
+ let r = [];
+ let err = _renderActions(caps[capName].rules,r,macros,caps,tags,{});
+ if (err !== null)
+ return err;
+ caps[capName].rules = r;
+ }
+
+ let err = _renderActions(baseRuleTree,rules,macros,caps,tags,{});
+ if (err !== null)
+ return err;
+
+ return null;
+ } catch (e) {
+ console.log(e.stack);
+ return [ 0,0,'Unexpected exception: '+e.toString() ];
+ }
+}
+
+exports.compile = compile;
diff --git a/selftest.cpp b/selftest.cpp
index f4232851..4008cd2a 100644
--- a/selftest.cpp
+++ b/selftest.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -25,6 +33,7 @@
#include <iostream>
#include <string>
#include <vector>
+#include <thread>
#include "node/Constants.hpp"
#include "node/Hashtable.hpp"
@@ -48,14 +57,15 @@
#include "osdep/OSUtils.hpp"
#include "osdep/Phy.hpp"
-#include "osdep/Http.hpp"
-#include "osdep/BackgroundResolver.hpp"
#include "osdep/PortMapper.hpp"
#include "osdep/Thread.hpp"
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
-#include "controller/SqliteNetworkController.hpp"
-#endif // ZT_ENABLE_NETWORK_CONTROLLER
+#ifdef ZT_USE_X64_ASM_SALSA2012
+#include "ext/x64-salsa2012-asm/salsa2012.h"
+#endif
+#ifdef ZT_USE_ARM32_NEON_ASM_SALSA2012
+#include "ext/arm32-neon-salsa2012-asm/salsa2012.h"
+#endif
#ifdef __WINDOWS__
#include <tchar.h>
@@ -137,16 +147,15 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
//////////////////////////////////////////////////////////////////////////////
-static unsigned char fuzzbuf[1048576];
-
static int testCrypto()
{
- unsigned char buf1[16384];
- unsigned char buf2[sizeof(buf1)],buf3[sizeof(buf1)];
+ static unsigned char buf1[16384];
+ static unsigned char buf2[sizeof(buf1)],buf3[sizeof(buf1)];
+ static char hexbuf[1024];
for(int i=0;i<3;++i) {
Utils::getSecureRandom(buf1,64);
- std::cout << "[crypto] getSecureRandom: " << Utils::hex(buf1,64) << std::endl;
+ std::cout << "[crypto] getSecureRandom: " << Utils::hex(buf1,64,hexbuf) << std::endl;
}
std::cout << "[crypto] Testing Salsa20... "; std::cout.flush();
@@ -156,27 +165,27 @@ static int testCrypto()
memset(buf2,0,sizeof(buf2));
memset(buf3,0,sizeof(buf3));
Salsa20 s20;
- s20.init("12345678123456781234567812345678",256,"12345678");
- s20.encrypt20(buf1,buf2,sizeof(buf1));
- s20.init("12345678123456781234567812345678",256,"12345678");
- s20.decrypt20(buf2,buf3,sizeof(buf2));
+ s20.init("12345678123456781234567812345678","12345678");
+ s20.crypt20(buf1,buf2,sizeof(buf1));
+ s20.init("12345678123456781234567812345678","12345678");
+ s20.crypt20(buf2,buf3,sizeof(buf2));
if (memcmp(buf1,buf3,sizeof(buf1))) {
std::cout << "FAIL (encrypt/decrypt test)" << std::endl;
return -1;
}
}
- Salsa20 s20(s20TV0Key,256,s20TV0Iv);
+ Salsa20 s20(s20TV0Key,s20TV0Iv);
memset(buf1,0,sizeof(buf1));
memset(buf2,0,sizeof(buf2));
- s20.encrypt20(buf1,buf2,64);
+ s20.crypt20(buf1,buf2,64);
if (memcmp(buf2,s20TV0Ks,64)) {
std::cout << "FAIL (test vector 0)" << std::endl;
return -1;
}
- s20.init(s2012TV0Key,256,s2012TV0Iv);
+ s20.init(s2012TV0Key,s2012TV0Iv);
memset(buf1,0,sizeof(buf1));
memset(buf2,0,sizeof(buf2));
- s20.encrypt12(buf1,buf2,64);
+ s20.crypt12(buf1,buf2,64);
if (memcmp(buf2,s2012TV0Ks,64)) {
std::cout << "FAIL (test vector 1)" << std::endl;
return -1;
@@ -194,34 +203,68 @@ static int testCrypto()
unsigned char *bb = (unsigned char *)::malloc(1234567);
for(unsigned int i=0;i<1234567;++i)
bb[i] = (unsigned char)i;
- Salsa20 s20(s20TV0Key,256,s20TV0Iv);
- double bytes = 0.0;
+ Salsa20 s20(s20TV0Key,s20TV0Iv);
+ long double bytes = 0.0;
uint64_t start = OSUtils::now();
for(unsigned int i=0;i<200;++i) {
- s20.encrypt12(bb,bb,1234567);
+ s20.crypt12(bb,bb,1234567);
bytes += 1234567.0;
}
uint64_t end = OSUtils::now();
SHA512::hash(buf1,bb,1234567);
- std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16,hexbuf) << ')' << std::endl;
::free((void *)bb);
}
+#ifdef ZT_USE_X64_ASM_SALSA2012
+ std::cout << "[crypto] Benchmarking Salsa20/12 fast x64 ASM... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<200;++i) {
+ zt_salsa2012_amd64_xmm6(bb,1234567,s20TV0Iv,s20TV0Key);
+ bytes += 1234567.0;
+ }
+ uint64_t end = OSUtils::now();
+ std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1024.0)) << " MiB/second" << std::endl;
+ ::free((void *)bb);
+ }
+#endif
+
+#ifdef ZT_USE_ARM32_NEON_ASM_SALSA2012
+ if (zt_arm_has_neon()) {
+ std::cout << "[crypto] Benchmarking Salsa20/12 fast arm32/neon ASM... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<200;++i) {
+ zt_salsa2012_armneon3_xor(bb,(const unsigned char *)0,1234567,s20TV0Iv,s20TV0Key);
+ bytes += 1234567.0;
+ }
+ uint64_t end = OSUtils::now();
+ std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1024.0)) << " MiB/second" << std::endl;
+ ::free((void *)bb);
+ }
+ }
+#endif
+
std::cout << "[crypto] Benchmarking Salsa20/20... "; std::cout.flush();
{
unsigned char *bb = (unsigned char *)::malloc(1234567);
for(unsigned int i=0;i<1234567;++i)
bb[i] = (unsigned char)i;
- Salsa20 s20(s20TV0Key,256,s20TV0Iv);
- double bytes = 0.0;
+ Salsa20 s20(s20TV0Key,s20TV0Iv);
+ long double bytes = 0.0;
uint64_t start = OSUtils::now();
for(unsigned int i=0;i<200;++i) {
- s20.encrypt20(bb,bb,1234567);
+ s20.crypt20(bb,bb,1234567);
bytes += 1234567.0;
}
uint64_t end = OSUtils::now();
SHA512::hash(buf1,bb,1234567);
- std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16,hexbuf) << ')' << std::endl;
::free((void *)bb);
}
@@ -251,14 +294,14 @@ static int testCrypto()
unsigned char *bb = (unsigned char *)::malloc(1234567);
for(unsigned int i=0;i<1234567;++i)
bb[i] = (unsigned char)i;
- double bytes = 0.0;
+ long double bytes = 0.0;
uint64_t start = OSUtils::now();
for(unsigned int i=0;i<200;++i) {
Poly1305::compute(buf1,bb,1234567,poly1305TV0Key);
bytes += 1234567.0;
}
uint64_t end = OSUtils::now();
- std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second" << std::endl;
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1000.0)) << " MiB/second" << std::endl;
::free((void *)bb);
}
@@ -278,10 +321,10 @@ static int testCrypto()
std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush();
for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
C25519::Pair p1,p2;
- memcpy(p1.pub.data,C25519_TEST_VECTORS[k].pub1,p1.pub.size());
- memcpy(p1.priv.data,C25519_TEST_VECTORS[k].priv1,p1.priv.size());
- memcpy(p2.pub.data,C25519_TEST_VECTORS[k].pub2,p2.pub.size());
- memcpy(p2.priv.data,C25519_TEST_VECTORS[k].priv2,p2.priv.size());
+ memcpy(p1.pub.data,C25519_TEST_VECTORS[k].pub1,ZT_C25519_PUBLIC_KEY_LEN);
+ memcpy(p1.priv.data,C25519_TEST_VECTORS[k].priv1,ZT_C25519_PRIVATE_KEY_LEN);
+ memcpy(p2.pub.data,C25519_TEST_VECTORS[k].pub2,ZT_C25519_PUBLIC_KEY_LEN);
+ memcpy(p2.priv.data,C25519_TEST_VECTORS[k].priv2,ZT_C25519_PRIVATE_KEY_LEN);
C25519::agree(p1,p2.pub,buf1,64);
C25519::agree(p2,p1.pub,buf2,64);
if (memcmp(buf1,buf2,64)) {
@@ -329,6 +372,17 @@ static int testCrypto()
}
std::cout << "PASS" << std::endl;
+ std::cout << "[crypto] Benchmarking C25519 ECC key agreement... "; std::cout.flush();
+ C25519::Pair bp[8];
+ for(int k=0;k<8;++k)
+ bp[k] = C25519::generate();
+ uint64_t st = OSUtils::now();
+ for(unsigned int k=0;k<50;++k) {
+ C25519::agree(bp[~k & 7],bp[k & 7].pub,buf1,64);
+ }
+ uint64_t et = OSUtils::now();
+ std::cout << ((double)(et - st) / 50.0) << "ms per agreement." << std::endl;
+
std::cout << "[crypto] Testing Ed25519 ECC signatures... "; std::cout.flush();
C25519::Pair didntSign = C25519::generate();
for(unsigned int i=0;i<10;++i) {
@@ -356,7 +410,7 @@ static int testCrypto()
}
for(unsigned int k=0;k<64;++k) {
C25519::Signature sig2(sig);
- sig2.data[rand() % sig2.size()] ^= (unsigned char)(1 << (rand() & 7));
+ sig2.data[rand() % ZT_C25519_SIGNATURE_LEN] ^= (unsigned char)(1 << (rand() & 7));
if (C25519::verify(p1.pub,buf1,sizeof(buf1),sig2)) {
std::cout << "FAIL (5)" << std::endl;
return -1;
@@ -365,6 +419,15 @@ static int testCrypto()
}
std::cout << "PASS" << std::endl;
+ std::cout << "[crypto] Benchmarking Ed25519 ECC signatures... "; std::cout.flush();
+ st = OSUtils::now();
+ for(int k=0;k<1000;++k) {
+ C25519::Signature sig;
+ C25519::sign(didntSign.priv,didntSign.pub,buf1,sizeof(buf1),sig.data);
+ }
+ et = OSUtils::now();
+ std::cout << ((double)(et - st) / 50.0) << "ms per signature." << std::endl;
+
return 0;
}
@@ -372,17 +435,22 @@ static int testIdentity()
{
Identity id;
Buffer<512> buf;
+ char buf2[1024];
std::cout << "[identity] Validate known-good identity... "; std::cout.flush();
if (!id.fromString(KNOWN_GOOD_IDENTITY)) {
std::cout << "FAIL (1)" << std::endl;
return -1;
}
- if (!id.locallyValidate()) {
- std::cout << "FAIL (2)" << std::endl;
- return -1;
+ const uint64_t vst = OSUtils::now();
+ for(int k=0;k<10;++k) {
+ if (!id.locallyValidate()) {
+ std::cout << "FAIL (2)" << std::endl;
+ return -1;
+ }
}
- std::cout << "PASS" << std::endl;
+ const uint64_t vet = OSUtils::now();
+ std::cout << "PASS (" << ((double)(vet - vst) / 10.0) << "ms per validation)" << std::endl;
std::cout << "[identity] Validate known-bad identity... "; std::cout.flush();
if (!id.fromString(KNOWN_BAD_IDENTITY)) {
@@ -400,7 +468,7 @@ static int testIdentity()
uint64_t genstart = OSUtils::now();
id.generate();
uint64_t genend = OSUtils::now();
- std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true) << std::endl;
+ std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true,buf2) << std::endl;
std::cout << "[identity] Locally validate identity: ";
if (id.locallyValidate()) {
std::cout << "PASS" << std::endl;
@@ -440,7 +508,7 @@ static int testIdentity()
{
Identity id2;
- id2.fromString(id.toString(true).c_str());
+ id2.fromString(id.toString(true,buf2));
std::cout << "[identity] Serialize and deserialize (ASCII w/private): ";
if ((id == id2)&&(id2.locallyValidate())) {
std::cout << "PASS" << std::endl;
@@ -452,7 +520,7 @@ static int testIdentity()
{
Identity id2;
- id2.fromString(id.toString(false).c_str());
+ id2.fromString(id.toString(false,buf2));
std::cout << "[identity] Serialize and deserialize (ASCII no private): ";
if ((id == id2)&&(id2.locallyValidate())) {
std::cout << "PASS" << std::endl;
@@ -467,16 +535,18 @@ static int testIdentity()
static int testCertificate()
{
+ char buf[4096];
+
Identity authority;
std::cout << "[certificate] Generating identity to act as authority... "; std::cout.flush();
authority.generate();
- std::cout << authority.address().toString() << std::endl;
+ std::cout << authority.address().toString(buf) << std::endl;
Identity idA,idB;
std::cout << "[certificate] Generating identities A and B... "; std::cout.flush();
idA.generate();
idB.generate();
- std::cout << idA.address().toString() << ", " << idB.address().toString() << std::endl;
+ std::cout << idA.address().toString(buf) << ", " << idB.address().toString(buf) << std::endl;
std::cout << "[certificate] Generating certificates A and B...";
CertificateOfMembership cA(10000,100,1,idA.address());
@@ -505,19 +575,6 @@ static int testCertificate()
return -1;
}
- std::cout << "[certificate] Testing string serialization... ";
- CertificateOfMembership copyA(cA.toString());
- CertificateOfMembership copyB(cB.toString());
- if (copyA != cA) {
- std::cout << "FAIL" << std::endl;
- return -1;
- }
- if (copyB != cB) {
- std::cout << "FAIL" << std::endl;
- return -1;
- }
- std::cout << "PASS" << std::endl;
-
std::cout << "[certificate] Generating two certificates that should not agree...";
cA = CertificateOfMembership(10000,100,1,idA.address());
cB = CertificateOfMembership(10101,100,1,idB.address());
@@ -585,6 +642,80 @@ static int testPacket()
static int testOther()
{
+ char buf[1024];
+ char buf2[4096];
+ char buf3[1024];
+
+ std::cout << "[other] Testing hex/unhex... "; std::cout.flush();
+ Utils::getSecureRandom(buf,(unsigned int)sizeof(buf));
+ Utils::hex(buf,(unsigned int)sizeof(buf),buf2);
+ Utils::unhex(buf2,buf3,(unsigned int)sizeof(buf3));
+ if (memcmp(buf,buf3,sizeof(buf)) == 0) {
+ std::cout << "PASS" << std::endl;
+ } else {
+ std::cout << "FAIL!" << std::endl;
+ buf2[78] = 0;
+ std::cout << buf2 << std::endl;
+ Utils::hex(buf3,(unsigned int)sizeof(buf3),buf2);
+ buf2[78] = 0;
+ std::cout << buf2 << std::endl;
+ return -1;
+ }
+
+ std::cout << "[other] Testing InetAddress encode/decode..."; std::cout.flush();
+ std::cout << " " << InetAddress("127.0.0.1/9993").toString(buf);
+ std::cout << " " << InetAddress("feed:dead:babe:dead:beef:f00d:1234:5678/12345").toString(buf);
+ std::cout << " " << InetAddress("0/9993").toString(buf);
+ std::cout << " " << InetAddress("").toString(buf);
+ std::cout << std::endl;
+
+#if 0
+ std::cout << "[other] Benchmarking memcpy... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ unsigned char *cc = (unsigned char *)::malloc(1234567);
+ for(unsigned int i=0;i<1234567;++i)
+ bb[i] = (unsigned char)i;
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<20000;++i) {
+ ++bb[i];
+ ++bb[i+1];
+ memcpy(cc,bb,1234567);
+ bytes += 1234567.0;
+ }
+ if (cc[0] != bb[0])
+ abort();
+ uint64_t end = OSUtils::now();
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second" << std::endl;
+ ::free((void *)bb);
+ ::free((void *)cc);
+ }
+#endif
+
+ std::cout << "[other] Benchmarking ZT_FAST_MEMCPY... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ unsigned char *cc = (unsigned char *)::malloc(1234567);
+ for(unsigned int i=0;i<1234567;++i)
+ bb[i] = (unsigned char)i;
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<20000;++i) {
+ ++bb[0];
+ ++bb[1234566];
+ ZT_FAST_MEMCPY(cc,bb,1234567);
+ bytes += 1234567.0;
+ }
+ if (cc[0] != bb[0])
+ abort();
+ uint64_t end = OSUtils::now();
+ std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second" << std::endl;
+ ::free((void *)bb);
+ ::free((void *)cc);
+ }
+
+#if 0
std::cout << "[other] Testing Hashtable... "; std::cout.flush();
{
Hashtable<uint64_t,std::string> ht;
@@ -748,42 +879,29 @@ static int testOther()
}
}
std::cout << "PASS" << std::endl;
-
- std::cout << "[other] Testing hex encode/decode... "; std::cout.flush();
- for(unsigned int k=0;k<1000;++k) {
- unsigned int flen = (rand() % 8194) + 1;
- for(unsigned int i=0;i<flen;++i)
- fuzzbuf[i] = (unsigned char)(rand() & 0xff);
- std::string dec = Utils::unhex(Utils::hex(fuzzbuf,flen).c_str());
- if ((dec.length() != flen)||(memcmp(dec.data(),fuzzbuf,dec.length()))) {
- std::cout << "FAILED!" << std::endl;
- std::cout << Utils::hex(fuzzbuf,flen) << std::endl;
- std::cout << Utils::hex(dec.data(),(unsigned int)dec.length()) << std::endl;
- return -1;
- }
- }
- std::cout << "PASS" << std::endl;
+#endif
std::cout << "[other] Testing/fuzzing Dictionary... "; std::cout.flush();
for(int k=0;k<1000;++k) {
- Dictionary<8194> test;
+ Dictionary<8194> *test = new Dictionary<8194>();
char key[32][16];
char value[32][128];
+ memset(key, 0, sizeof(key));
+ memset(value, 0, sizeof(value));
for(unsigned int q=0;q<32;++q) {
- Utils::snprintf(key[q],16,"%.8lx",(unsigned long)rand());
+ Utils::hex((uint32_t)((rand() % 1000) + (q * 1000)),key[q]);
int r = rand() % 128;
for(int x=0;x<r;++x)
value[q][x] = ("0123456789\0\t\r\n= ")[rand() % 16];
value[q][r] = (char)0;
- test.add(key[q],value[q],r);
+ test->add(key[q],value[q],r);
}
for(unsigned int q=0;q<1024;++q) {
- //int r = rand() % 128;
- int r = 31;
+ int r = rand() % 32;
char tmp[128];
- if (test.get(key[r],tmp,sizeof(tmp)) >= 0) {
+ if (test->get(key[r],tmp,sizeof(tmp)) >= 0) {
if (strcmp(value[r],tmp)) {
- std::cout << "FAILED (invalid value)!" << std::endl;
+ std::cout << "FAILED (invalid value '" << value[r] << "' != '" << tmp << "')!" << std::endl;
return -1;
}
} else {
@@ -791,36 +909,27 @@ static int testOther()
return -1;
}
}
- for(unsigned int q=0;q<31;++q) {
- char tmp[128];
- test.erase(key[q]);
- if (test.get(key[q],tmp,sizeof(tmp)) >= 0) {
- std::cout << "FAILED (key should have been erased)!" << std::endl;
- return -1;
- }
- if (test.get(key[q+1],tmp,sizeof(tmp)) < 0) {
- std::cout << "FAILED (key should NOT have been erased)!" << std::endl;
- return -1;
- }
- }
+ delete test;
}
int foo = 0;
volatile int *volatile bar = &foo; // force compiler not to optimize out test.get() below
for(int k=0;k<200;++k) {
int r = rand() % 8194;
- unsigned char tmp[8194];
+ unsigned char *tmp = new unsigned char[8194];
for(int q=0;q<r;++q)
tmp[q] = (unsigned char)((rand() % 254) + 1); // don't put nulls since those will always just terminate scan
tmp[r] = (r % 32) ? (char)(rand() & 0xff) : (char)0; // every 32nd iteration don't terminate the string maybe...
- Dictionary<8194> test((const char *)tmp);
+ Dictionary<8194> *test = new Dictionary<8194>((const char *)tmp);
for(unsigned int q=0;q<100;++q) {
char tmp[128];
for(unsigned int x=0;x<128;++x)
tmp[x] = (char)(rand() & 0xff);
tmp[127] = (char)0;
char value[8194];
- *bar += test.get(tmp,value,sizeof(value));
+ *bar += test->get(tmp,value,sizeof(value));
}
+ delete test;
+ delete[] tmp;
}
std::cout << "PASS (junk value to prevent optimization-out of test: " << foo << ")" << std::endl;
@@ -975,70 +1084,8 @@ static int testPhy()
return 0;
}
-static int testResolver()
-{
- std::cout << "[resolver] Testing BackgroundResolver..."; std::cout.flush();
-
- BackgroundResolver r("tcp-fallback.zerotier.com");
- r.resolveNow();
- r.wait();
-
- std::vector<InetAddress> ips(r.get());
- for(std::vector<InetAddress>::const_iterator ip(ips.begin());ip!=ips.end();++ip) {
- std::cout << ' ' << ip->toString();
- }
- std::cout << std::endl;
-
- return 0;
-}
-
-/*
-static int testHttp()
-{
- std::map<std::string,std::string> requestHeaders,responseHeaders;
- std::string responseBody;
-
- InetAddress downloadZerotierDotCom;
- std::vector<InetAddress> rr(OSUtils::resolve("download.zerotier.com"));
- if (rr.empty()) {
- std::cout << "[http] Resolve of download.zerotier.com failed, skipping." << std::endl;
- return 0;
- } else {
- for(std::vector<InetAddress>::iterator r(rr.begin());r!=rr.end();++r) {
- std::cout << "[http] download.zerotier.com: " << r->toString() << std::endl;
- if (r->isV4())
- downloadZerotierDotCom = *r;
- }
- }
- downloadZerotierDotCom.setPort(80);
-
- std::cout << "[http] GET http://download.zerotier.com/dev/1k @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush();
- requestHeaders["Host"] = "download.zerotier.com";
- unsigned int sc = Http::GET(1024 * 1024 * 16,60000,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/1k",requestHeaders,responseHeaders,responseBody);
- std::cout << sc << " " << responseBody.length() << " bytes ";
- if (sc == 0)
- std::cout << "ERROR: " << responseBody << std::endl;
- else std::cout << "DONE" << std::endl;
-
- std::cout << "[http] GET http://download.zerotier.com/dev/4m @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush();
- requestHeaders["Host"] = "download.zerotier.com";
- sc = Http::GET(1024 * 1024 * 16,60000,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/4m",requestHeaders,responseHeaders,responseBody);
- std::cout << sc << " " << responseBody.length() << " bytes ";
- if (sc == 0)
- std::cout << "ERROR: " << responseBody << std::endl;
- else std::cout << "DONE" << std::endl;
-
- downloadZerotierDotCom = InetAddress("1.0.0.1/1234");
- std::cout << "[http] GET @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush();
- sc = Http::GET(1024 * 1024 * 16,2500,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/4m",requestHeaders,responseHeaders,responseBody);
- std::cout << sc << " (should be 0, time out)" << std::endl;
-
- return 0;
-}
-*/
-
#ifdef __WINDOWS__
-int _tmain(int argc, _TCHAR* argv[])
+int __cdecl _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc,char **argv)
#endif
@@ -1088,6 +1135,8 @@ int main(int argc,char **argv)
*/
std::cout << "[info] sizeof(void *) == " << sizeof(void *) << std::endl;
+ std::cout << "[info] OSUtils::now() == " << OSUtils::now() << std::endl;
+ std::cout << "[info] hardware concurrency == " << std::thread::hardware_concurrency() << std::endl;
std::cout << "[info] sizeof(NetworkConfig) == " << sizeof(ZeroTier::NetworkConfig) << std::endl;
srand((unsigned int)time(0));
@@ -1099,8 +1148,6 @@ int main(int argc,char **argv)
r |= testIdentity();
r |= testCertificate();
r |= testPhy();
- r |= testResolver();
- //r |= testHttp();
//*/
if (r)
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
deleted file mode 100644
index a10697a9..00000000
--- a/service/ControlPlane.cpp
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "ControlPlane.hpp"
-#include "OneService.hpp"
-
-#include "../version.h"
-#include "../include/ZeroTierOne.h"
-
-#ifdef ZT_USE_SYSTEM_HTTP_PARSER
-#include <http_parser.h>
-#else
-#include "../ext/http-parser/http_parser.h"
-#endif
-
-#ifdef ZT_USE_SYSTEM_JSON_PARSER
-#include <json-parser/json.h>
-#else
-#include "../ext/json-parser/json.h"
-#endif
-
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
-#include "../controller/SqliteNetworkController.hpp"
-#endif
-
-#include "../node/InetAddress.hpp"
-#include "../node/Node.hpp"
-#include "../node/Utils.hpp"
-#include "../osdep/OSUtils.hpp"
-
-namespace ZeroTier {
-
-static std::string _jsonEscape(const char *s)
-{
- std::string buf;
- for(const char *p=s;(*p);++p) {
- switch(*p) {
- case '\t': buf.append("\\t"); break;
- case '\b': buf.append("\\b"); break;
- case '\r': buf.append("\\r"); break;
- case '\n': buf.append("\\n"); break;
- case '\f': buf.append("\\f"); break;
- case '"': buf.append("\\\""); break;
- case '\\': buf.append("\\\\"); break;
- case '/': buf.append("\\/"); break;
- default: buf.push_back(*p); break;
- }
- }
- return buf;
-}
-static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
-
-static std::string _jsonEnumerate(const struct sockaddr_storage *ss,unsigned int count)
-{
- std::string buf;
- buf.push_back('[');
- for(unsigned int i=0;i<count;++i) {
- if (i > 0)
- buf.push_back(',');
- buf.push_back('"');
- buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(ss[i]))->toString()));
- buf.push_back('"');
- }
- buf.push_back(']');
- return buf;
-}
-static std::string _jsonEnumerate(const ZT_VirtualNetworkRoute *routes,unsigned int count)
-{
- std::string buf;
- buf.push_back('[');
- for(unsigned int i=0;i<count;++i) {
- if (i > 0)
- buf.push_back(',');
- buf.append("{\"target\":\"");
- buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString()));
- buf.append("\",\"via\":");
- if (routes[i].via.ss_family == routes[i].target.ss_family) {
- buf.push_back('"');
- buf.append(_jsonEscape(reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString()));
- buf.append("\",");
- } else buf.append("null,");
- char tmp[1024];
- Utils::snprintf(tmp,sizeof(tmp),"\"flags\":%u,\"metric\":%u}",(unsigned int)routes[i].flags,(unsigned int)routes[i].metric);
- buf.append(tmp);
- }
- buf.push_back(']');
- return buf;
-}
-
-static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings)
-{
- char json[4096];
- char prefix[32];
-
- if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
- return;
- for(unsigned int i=0;i<depth;++i)
- prefix[i] = '\t';
- prefix[depth] = '\0';
-
- const char *nstatus = "",*ntype = "";
- switch(nc->status) {
- case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break;
- case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break;
- case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break;
- case ZT_NETWORK_STATUS_NOT_FOUND: nstatus = "NOT_FOUND"; break;
- case ZT_NETWORK_STATUS_PORT_ERROR: nstatus = "PORT_ERROR"; break;
- case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break;
- }
- switch(nc->type) {
- case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break;
- case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
- }
-
- Utils::snprintf(json,sizeof(json),
- "%s{\n"
- "%s\t\"nwid\": \"%.16llx\",\n"
- "%s\t\"mac\": \"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\",\n"
- "%s\t\"name\": \"%s\",\n"
- "%s\t\"status\": \"%s\",\n"
- "%s\t\"type\": \"%s\",\n"
- "%s\t\"mtu\": %u,\n"
- "%s\t\"dhcp\": %s,\n"
- "%s\t\"bridge\": %s,\n"
- "%s\t\"broadcastEnabled\": %s,\n"
- "%s\t\"portError\": %d,\n"
- "%s\t\"netconfRevision\": %lu,\n"
- "%s\t\"assignedAddresses\": %s,\n"
- "%s\t\"routes\": %s,\n"
- "%s\t\"portDeviceName\": \"%s\",\n"
- "%s\t\"allowManaged\": %s,\n"
- "%s\t\"allowGlobal\": %s,\n"
- "%s\t\"allowDefault\": %s\n"
- "%s}",
- prefix,
- prefix,nc->nwid,
- prefix,(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff),
- prefix,_jsonEscape(nc->name).c_str(),
- prefix,nstatus,
- prefix,ntype,
- prefix,nc->mtu,
- prefix,(nc->dhcp == 0) ? "false" : "true",
- prefix,(nc->bridge == 0) ? "false" : "true",
- prefix,(nc->broadcastEnabled == 0) ? "false" : "true",
- prefix,nc->portError,
- prefix,nc->netconfRevision,
- prefix,_jsonEnumerate(nc->assignedAddresses,nc->assignedAddressCount).c_str(),
- prefix,_jsonEnumerate(nc->routes,nc->routeCount).c_str(),
- prefix,_jsonEscape(portDeviceName).c_str(),
- prefix,(localSettings.allowManaged) ? "true" : "false",
- prefix,(localSettings.allowGlobal) ? "true" : "false",
- prefix,(localSettings.allowDefault) ? "true" : "false",
- prefix);
- buf.append(json);
-}
-
-static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *pp,unsigned int count)
-{
- char json[1024];
- char prefix[32];
-
- if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
- return std::string();
- for(unsigned int i=0;i<depth;++i)
- prefix[i] = '\t';
- prefix[depth] = '\0';
-
- std::string buf;
- for(unsigned int i=0;i<count;++i) {
- if (i > 0)
- buf.push_back(',');
- Utils::snprintf(json,sizeof(json),
- "{\n"
- "%s\t\"address\": \"%s\",\n"
- "%s\t\"lastSend\": %llu,\n"
- "%s\t\"lastReceive\": %llu,\n"
- "%s\t\"active\": %s,\n"
- "%s\t\"preferred\": %s,\n"
- "%s\t\"trustedPathId\": %llu\n"
- "%s}",
- prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
- prefix,pp[i].lastSend,
- prefix,pp[i].lastReceive,
- prefix,(pp[i].active == 0) ? "false" : "true",
- prefix,(pp[i].preferred == 0) ? "false" : "true",
- prefix,pp[i].trustedPathId,
- prefix);
- buf.append(json);
- }
- return buf;
-}
-
-static void _jsonAppend(unsigned int depth,std::string &buf,const ZT_Peer *peer)
-{
- char json[1024];
- char prefix[32];
-
- if (depth >= sizeof(prefix)) // sanity check -- shouldn't be possible
- return;
- for(unsigned int i=0;i<depth;++i)
- prefix[i] = '\t';
- prefix[depth] = '\0';
-
- const char *prole = "";
- switch(peer->role) {
- case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
- case ZT_PEER_ROLE_RELAY: prole = "RELAY"; break;
- case ZT_PEER_ROLE_ROOT: prole = "ROOT"; break;
- }
-
- Utils::snprintf(json,sizeof(json),
- "%s{\n"
- "%s\t\"address\": \"%.10llx\",\n"
- "%s\t\"lastUnicastFrame\": %llu,\n"
- "%s\t\"lastMulticastFrame\": %llu,\n"
- "%s\t\"versionMajor\": %d,\n"
- "%s\t\"versionMinor\": %d,\n"
- "%s\t\"versionRev\": %d,\n"
- "%s\t\"version\": \"%d.%d.%d\",\n"
- "%s\t\"latency\": %u,\n"
- "%s\t\"role\": \"%s\",\n"
- "%s\t\"paths\": [%s]\n"
- "%s}",
- prefix,
- prefix,peer->address,
- prefix,peer->lastUnicastFrame,
- prefix,peer->lastMulticastFrame,
- prefix,peer->versionMajor,
- prefix,peer->versionMinor,
- prefix,peer->versionRev,
- prefix,peer->versionMajor,peer->versionMinor,peer->versionRev,
- prefix,peer->latency,
- prefix,prole,
- prefix,_jsonEnumerate(depth+1,peer->paths,peer->pathCount).c_str(),
- prefix);
- buf.append(json);
-}
-
-ControlPlane::ControlPlane(OneService *svc,Node *n,const char *uiStaticPath) :
- _svc(svc),
- _node(n),
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- _controller((SqliteNetworkController *)0),
-#endif
- _uiStaticPath((uiStaticPath) ? uiStaticPath : "")
-{
-}
-
-ControlPlane::~ControlPlane()
-{
-}
-
-unsigned int ControlPlane::handleRequest(
- const InetAddress &fromAddress,
- unsigned int httpMethod,
- const std::string &path,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType)
-{
- char json[8194];
- unsigned int scode = 404;
- std::vector<std::string> ps(Utils::split(path.c_str(),"/","",""));
- std::map<std::string,std::string> urlArgs;
- Mutex::Lock _l(_lock);
-
- if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6))))
- return 403; // Forbidden: we only allow access from localhost right now
-
- /* Note: this is kind of restricted in what it'll take. It does not support
- * URL encoding, and /'s in URL args will screw it up. But the only URL args
- * it really uses in ?jsonp=funcionName, and otherwise it just takes simple
- * paths to simply-named resources. */
- if (ps.size() > 0) {
- std::size_t qpos = ps[ps.size() - 1].find('?');
- if (qpos != std::string::npos) {
- std::string args(ps[ps.size() - 1].substr(qpos + 1));
- ps[ps.size() - 1] = ps[ps.size() - 1].substr(0,qpos);
- std::vector<std::string> asplit(Utils::split(args.c_str(),"&","",""));
- for(std::vector<std::string>::iterator a(asplit.begin());a!=asplit.end();++a) {
- std::size_t eqpos = a->find('=');
- if (eqpos == std::string::npos)
- urlArgs[*a] = "";
- else urlArgs[a->substr(0,eqpos)] = a->substr(eqpos + 1);
- }
- }
- } else {
- ps.push_back(std::string("index.html"));
- }
-
- bool isAuth = false;
- {
- std::map<std::string,std::string>::const_iterator ah(headers.find("x-zt1-auth"));
- if ((ah != headers.end())&&(_authTokens.count(ah->second) > 0)) {
- isAuth = true;
- } else {
- ah = urlArgs.find("auth");
- if ((ah != urlArgs.end())&&(_authTokens.count(ah->second) > 0))
- isAuth = true;
- }
- }
-
- if (httpMethod == HTTP_GET) {
-
- std::string ext;
- std::size_t dotIdx = ps[0].find_last_of('.');
- if (dotIdx != std::string::npos)
- ext = ps[0].substr(dotIdx);
-
- if ((ps.size() == 1)&&(ext.length() >= 2)&&(ext[0] == '.')) {
- /* Static web pages can be served without authentication to enable a simple web
- * UI. This is still only allowed from approved IP addresses. Anything with a
- * dot in the first path element (e.g. foo.html) is considered a static page,
- * as nothing in the API is so named. */
-
- if (_uiStaticPath.length() > 0) {
- if (ext == ".html")
- responseContentType = "text/html";
- else if (ext == ".js")
- responseContentType = "application/javascript";
- else if (ext == ".jsx")
- responseContentType = "text/jsx";
- else if (ext == ".json")
- responseContentType = "application/json";
- else if (ext == ".css")
- responseContentType = "text/css";
- else if (ext == ".png")
- responseContentType = "image/png";
- else if (ext == ".jpg")
- responseContentType = "image/jpeg";
- else if (ext == ".gif")
- responseContentType = "image/gif";
- else if (ext == ".txt")
- responseContentType = "text/plain";
- else if (ext == ".xml")
- responseContentType = "text/xml";
- else if (ext == ".svg")
- responseContentType = "image/svg+xml";
- else responseContentType = "application/octet-stream";
- scode = OSUtils::readFile((_uiStaticPath + ZT_PATH_SEPARATOR_S + ps[0]).c_str(),responseBody) ? 200 : 404;
- } else {
- scode = 404;
- }
-
- } else if (isAuth) {
- /* Things that require authentication -- a.k.a. everything but static web app pages. */
-
- if (ps[0] == "status") {
- responseContentType = "application/json";
-
- ZT_NodeStatus status;
- _node->status(&status);
-
- std::string clusterJson;
-#ifdef ZT_ENABLE_CLUSTER
- {
- ZT_ClusterStatus cs;
- _node->clusterStatus(&cs);
-
- if (cs.clusterSize >= 1) {
- char t[1024];
- Utils::snprintf(t,sizeof(t),"{\n\t\t\"myId\": %u,\n\t\t\"clusterSize\": %u,\n\t\t\"members\": [",cs.myId,cs.clusterSize);
- clusterJson.append(t);
- for(unsigned int i=0;i<cs.clusterSize;++i) {
- Utils::snprintf(t,sizeof(t),"%s\t\t\t{\n\t\t\t\t\"id\": %u,\n\t\t\t\t\"msSinceLastHeartbeat\": %u,\n\t\t\t\t\"alive\": %s,\n\t\t\t\t\"x\": %d,\n\t\t\t\t\"y\": %d,\n\t\t\t\t\"z\": %d,\n\t\t\t\t\"load\": %llu,\n\t\t\t\t\"peers\": %llu\n\t\t\t}",
- ((i == 0) ? "\n" : ",\n"),
- cs.members[i].id,
- cs.members[i].msSinceLastHeartbeat,
- (cs.members[i].alive != 0) ? "true" : "false",
- cs.members[i].x,
- cs.members[i].y,
- cs.members[i].z,
- cs.members[i].load,
- cs.members[i].peers);
- clusterJson.append(t);
- }
- clusterJson.append(" ]\n\t\t}");
- }
- }
-#endif
-
- Utils::snprintf(json,sizeof(json),
- "{\n"
- "\t\"address\": \"%.10llx\",\n"
- "\t\"publicIdentity\": \"%s\",\n"
- "\t\"worldId\": %llu,\n"
- "\t\"worldTimestamp\": %llu,\n"
- "\t\"online\": %s,\n"
- "\t\"tcpFallbackActive\": %s,\n"
- "\t\"versionMajor\": %d,\n"
- "\t\"versionMinor\": %d,\n"
- "\t\"versionRev\": %d,\n"
- "\t\"version\": \"%d.%d.%d\",\n"
- "\t\"clock\": %llu,\n"
- "\t\"cluster\": %s\n"
- "}\n",
- status.address,
- status.publicIdentity,
- status.worldId,
- status.worldTimestamp,
- (status.online) ? "true" : "false",
- (_svc->tcpFallbackActive()) ? "true" : "false",
- ZEROTIER_ONE_VERSION_MAJOR,
- ZEROTIER_ONE_VERSION_MINOR,
- ZEROTIER_ONE_VERSION_REVISION,
- ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,
- (unsigned long long)OSUtils::now(),
- ((clusterJson.length() > 0) ? clusterJson.c_str() : "null"));
- responseBody = json;
- scode = 200;
- } else if (ps[0] == "config") {
- responseContentType = "application/json";
- responseBody = "{}"; // TODO
- scode = 200;
- } else if (ps[0] == "network") {
- ZT_VirtualNetworkList *nws = _node->networks();
- if (nws) {
- if (ps.size() == 1) {
- // Return [array] of all networks
- responseContentType = "application/json";
- responseBody = "[\n";
- for(unsigned long i=0;i<nws->networkCount;++i) {
- if (i > 0)
- responseBody.append(",");
- OneService::NetworkSettings localSettings;
- _svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
- _jsonAppend(1,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
- }
- responseBody.append("\n]\n");
- scode = 200;
- } else if (ps.size() == 2) {
- // Return a single network by ID or 404 if not found
- uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
- for(unsigned long i=0;i<nws->networkCount;++i) {
- if (nws->networks[i].nwid == wantnw) {
- responseContentType = "application/json";
- OneService::NetworkSettings localSettings;
- _svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
- _jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
- responseBody.push_back('\n');
- scode = 200;
- break;
- }
- }
- } // else 404
- _node->freeQueryResult((void *)nws);
- } else scode = 500;
- } else if (ps[0] == "peer") {
- ZT_PeerList *pl = _node->peers();
- if (pl) {
- if (ps.size() == 1) {
- // Return [array] of all peers
- responseContentType = "application/json";
- responseBody = "[\n";
- for(unsigned long i=0;i<pl->peerCount;++i) {
- if (i > 0)
- responseBody.append(",\n");
- _jsonAppend(1,responseBody,&(pl->peers[i]));
- }
- responseBody.append("\n]\n");
- scode = 200;
- } else if (ps.size() == 2) {
- // Return a single peer by ID or 404 if not found
- uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
- for(unsigned long i=0;i<pl->peerCount;++i) {
- if (pl->peers[i].address == wantp) {
- responseContentType = "application/json";
- _jsonAppend(0,responseBody,&(pl->peers[i]));
- responseBody.push_back('\n');
- scode = 200;
- break;
- }
- }
- } // else 404
- _node->freeQueryResult((void *)pl);
- } else scode = 500;
- } else if (ps[0] == "newIdentity") {
- // Return a newly generated ZeroTier identity -- this is primarily for debugging
- // and testing to make it easy for automated test scripts to generate test IDs.
- Identity newid;
- newid.generate();
- responseBody = newid.toString(true);
- responseContentType = "text/plain";
- scode = 200;
- } else {
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- if (_controller)
- scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
- else scode = 404;
-#else
- scode = 404;
-#endif
- }
-
- } else scode = 401; // isAuth == false
-
- } else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
-
- if (isAuth) {
-
- if (ps[0] == "config") {
- // TODO
- } else if (ps[0] == "network") {
- if (ps.size() == 2) {
- uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
- _node->join(wantnw,(void *)0); // does nothing if we are a member
- ZT_VirtualNetworkList *nws = _node->networks();
- if (nws) {
- for(unsigned long i=0;i<nws->networkCount;++i) {
- if (nws->networks[i].nwid == wantnw) {
- OneService::NetworkSettings localSettings;
- _svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
-
- json_value *j = json_parse(body.c_str(),body.length());
- if (j) {
- if (j->type == json_object) {
- for(unsigned int k=0;k<j->u.object.length;++k) {
- if (!strcmp(j->u.object.values[k].name,"allowManaged")) {
- if (j->u.object.values[k].value->type == json_boolean)
- localSettings.allowManaged = (j->u.object.values[k].value->u.boolean != 0);
- } else if (!strcmp(j->u.object.values[k].name,"allowGlobal")) {
- if (j->u.object.values[k].value->type == json_boolean)
- localSettings.allowGlobal = (j->u.object.values[k].value->u.boolean != 0);
- } else if (!strcmp(j->u.object.values[k].name,"allowDefault")) {
- if (j->u.object.values[k].value->type == json_boolean)
- localSettings.allowDefault = (j->u.object.values[k].value->u.boolean != 0);
- }
- }
- }
- json_value_free(j);
- }
-
- _svc->setNetworkSettings(nws->networks[i].nwid,localSettings);
-
- responseContentType = "application/json";
- _jsonAppend(0,responseBody,&(nws->networks[i]),_svc->portDeviceName(nws->networks[i].nwid),localSettings);
- responseBody.push_back('\n');
- scode = 200;
- break;
- }
- }
- _node->freeQueryResult((void *)nws);
- } else scode = 500;
- }
- } else {
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- if (_controller)
- scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
- else scode = 404;
-#else
- scode = 404;
-#endif
- }
-
- } else scode = 401; // isAuth == false
-
- } else if (httpMethod == HTTP_DELETE) {
-
- if (isAuth) {
-
- if (ps[0] == "config") {
- // TODO
- } else if (ps[0] == "network") {
- ZT_VirtualNetworkList *nws = _node->networks();
- if (nws) {
- if (ps.size() == 2) {
- uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
- for(unsigned long i=0;i<nws->networkCount;++i) {
- if (nws->networks[i].nwid == wantnw) {
- _node->leave(wantnw,(void **)0);
- responseBody = "true";
- responseContentType = "application/json";
- scode = 200;
- break;
- }
- }
- } // else 404
- _node->freeQueryResult((void *)nws);
- } else scode = 500;
- } else {
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- if (_controller)
- scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
- else scode = 404;
-#else
- scode = 404;
-#endif
- }
-
- } else {
- scode = 401; // isAuth = false
- }
-
- } else {
- scode = 400;
- responseBody = "Method not supported.";
- }
-
- // Wrap result in jsonp function call if the user included a jsonp= url argument.
- // Also double-check isAuth since forbidding this without auth feels safer.
- std::map<std::string,std::string>::const_iterator jsonp(urlArgs.find("jsonp"));
- if ((isAuth)&&(jsonp != urlArgs.end())&&(responseContentType == "application/json")) {
- if (responseBody.length() > 0)
- responseBody = jsonp->second + "(" + responseBody + ");";
- else responseBody = jsonp->second + "(null);";
- responseContentType = "application/javascript";
- }
-
- return scode;
-}
-
-} // namespace ZeroTier
diff --git a/service/ControlPlane.hpp b/service/ControlPlane.hpp
deleted file mode 100644
index 08a9d6e0..00000000
--- a/service/ControlPlane.hpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZT_ONE_CONTROLPLANE_HPP
-#define ZT_ONE_CONTROLPLANE_HPP
-
-#include <string>
-#include <map>
-#include <set>
-
-#include "../include/ZeroTierOne.h"
-
-#include "../node/Mutex.hpp"
-
-namespace ZeroTier {
-
-class OneService;
-class Node;
-class SqliteNetworkController;
-struct InetAddress;
-
-/**
- * HTTP control plane and static web server
- */
-class ControlPlane
-{
-public:
- ControlPlane(OneService *svc,Node *n,const char *uiStaticPath);
- ~ControlPlane();
-
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- /**
- * Set controller, which will be available under /controller
- *
- * @param c Network controller instance
- */
- inline void setController(SqliteNetworkController *c)
- {
- Mutex::Lock _l(_lock);
- _controller = c;
- }
-#endif
-
- /**
- * Add an authentication token for API access
- */
- inline void addAuthToken(const char *tok)
- {
- Mutex::Lock _l(_lock);
- _authTokens.insert(std::string(tok));
- }
-
- /**
- * Handle HTTP request
- *
- * @param fromAddress Originating IP address of request
- * @param httpMethod HTTP method (as defined in ext/http-parser/http_parser.h)
- * @param path Request path
- * @param headers Request headers
- * @param body Request body
- * @param responseBody Result parameter: fill with response data
- * @param responseContentType Result parameter: fill with content type
- * @return HTTP response code
- */
- unsigned int handleRequest(
- const InetAddress &fromAddress,
- unsigned int httpMethod,
- const std::string &path,
- const std::map<std::string,std::string> &headers,
- const std::string &body,
- std::string &responseBody,
- std::string &responseContentType);
-
-private:
- OneService *const _svc;
- Node *const _node;
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- SqliteNetworkController *_controller;
-#endif
- std::string _uiStaticPath;
- std::set<std::string> _authTokens;
- Mutex _lock;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 4dd73c19..1d271b21 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#include <stdio.h>
@@ -23,7 +31,6 @@
#include <string>
#include <map>
-#include <set>
#include <vector>
#include <algorithm>
#include <list>
@@ -31,12 +38,6 @@
#include "../version.h"
#include "../include/ZeroTierOne.h"
-#ifdef ZT_USE_SYSTEM_HTTP_PARSER
-#include <http_parser.h>
-#else
-#include "../ext/http-parser/http_parser.h"
-#endif
-
#include "../node/Constants.hpp"
#include "../node/Mutex.hpp"
#include "../node/Node.hpp"
@@ -44,36 +45,21 @@
#include "../node/InetAddress.hpp"
#include "../node/MAC.hpp"
#include "../node/Identity.hpp"
+#include "../node/World.hpp"
+#include "../node/Salsa20.hpp"
+#include "../node/Poly1305.hpp"
+#include "../node/SHA512.hpp"
#include "../osdep/Phy.hpp"
#include "../osdep/Thread.hpp"
#include "../osdep/OSUtils.hpp"
#include "../osdep/Http.hpp"
-#include "../osdep/BackgroundResolver.hpp"
#include "../osdep/PortMapper.hpp"
#include "../osdep/Binder.hpp"
#include "../osdep/ManagedRoute.hpp"
#include "OneService.hpp"
-#include "ControlPlane.hpp"
-#include "ClusterGeoIpService.hpp"
-#include "ClusterDefinition.hpp"
-
-/**
- * Uncomment to enable UDP breakage switch
- *
- * If this is defined, the presence of a file called /tmp/ZT_BREAK_UDP
- * will cause direct UDP TX/RX to stop working. This can be used to
- * test TCP tunneling fallback and other robustness features. Deleting
- * this file will cause it to start working again.
- */
-//#define ZT_BREAK_UDP
-
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
-#include "../controller/SqliteNetworkController.hpp"
-#else
-class SqliteNetworkController;
-#endif // ZT_ENABLE_NETWORK_CONTROLLER
+#include "SoftwareUpdater.hpp"
#ifdef __WINDOWS__
#include <WinSock2.h>
@@ -89,14 +75,34 @@ class SqliteNetworkController;
#include <ifaddrs.h>
#endif
-// Include the right tap device driver for this platform -- add new platforms here
-#ifdef ZT_SERVICE_NETCON
+#ifdef ZT_USE_SYSTEM_HTTP_PARSER
+#include <http_parser.h>
+#else
+#include "../ext/http-parser/http_parser.h"
+#endif
-// In network containers builds, use the virtual netcon endpoint instead of a tun/tap port driver
-#include "../netcon/NetconEthernetTap.hpp"
-namespace ZeroTier { typedef NetconEthernetTap EthernetTap; }
+#include "../ext/json/json.hpp"
-#else // not ZT_SERVICE_NETCON so pick a tap driver
+using json = nlohmann::json;
+
+#include "../controller/EmbeddedNetworkController.hpp"
+
+#ifdef ZT_USE_TEST_TAP
+
+#include "../osdep/TestEthernetTap.hpp"
+namespace ZeroTier { typedef TestEthernetTap EthernetTap; }
+
+#else
+
+#ifdef ZT_SDK
+
+#include "../controller/EmbeddedNetworkController.hpp"
+#include "../node/Node.hpp"
+// Use the virtual netcon endpoint instead of a tun/tap port driver
+#include "../include/VirtualTap.h"
+namespace ZeroTier { typedef VirtualTap EthernetTap; }
+
+#else
#ifdef __APPLE__
#include "../osdep/OSXEthernetTap.hpp"
@@ -117,13 +123,19 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
#ifdef __NetBSD__
#include "../osdep/NetBSDEthernetTap.hpp"
namespace ZeroTier { typedef NetBSDEthernetTap EthernetTap; }
-#endif // __FreeBSD__
+#endif // __NetBSD__
+#ifdef __OpenBSD__
+#include "../osdep/BSDEthernetTap.hpp"
+namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
+#endif // __OpenBSD__
#endif // ZT_SERVICE_NETCON
+#endif // ZT_USE_TEST_TAP
+
// Sanity limits for HTTP
#define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64)
-#define ZT_MAX_HTTP_CONNECTIONS 64
+#define ZT_MAX_HTTP_CONNECTIONS 65536
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also
// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi.
@@ -132,12 +144,8 @@ namespace ZeroTier { typedef NetBSDEthernetTap EthernetTap; }
// How often to check for new multicast subscriptions on a tap device
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
-// Path under ZT1 home for controller database if controller is enabled
-#define ZT_CONTROLLER_DB_PATH "controller.db"
-
-// TCP fallback relay host -- geo-distributed using Amazon Route53 geo-aware DNS
-#define ZT_TCP_FALLBACK_RELAY "tcp-fallback.zerotier.com"
-#define ZT_TCP_FALLBACK_RELAY_PORT 443
+// TCP fallback relay (run by ZeroTier, Inc. -- this will eventually go away)
+#define ZT_TCP_FALLBACK_RELAY "204.80.128.1/443"
// Frequency at which we re-resolve the TCP fallback relay
#define ZT_TCP_FALLBACK_RERESOLVE_DELAY 86400000
@@ -148,238 +156,18 @@ namespace ZeroTier { typedef NetBSDEthernetTap EthernetTap; }
// How often to check for local interface addresses
#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000
-namespace ZeroTier {
-
-namespace {
-
-#ifdef ZT_AUTO_UPDATE
-#define ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE (1024 * 1024 * 64)
-#define ZT_AUTO_UPDATE_CHECK_PERIOD 21600000
-class BackgroundSoftwareUpdateChecker
-{
-public:
- bool isValidSigningIdentity(const Identity &id)
- {
- return (
- /* 0001 - 0004 : obsolete, used in old versions */
- /* 0005 */ (id == Identity("ba57ea350e:0:9d4be6d7f86c5660d5ee1951a3d759aa6e12a84fc0c0b74639500f1dbc1a8c566622e7d1c531967ebceb1e9d1761342f88324a8ba520c93c35f92f35080fa23f"))
- /* 0006 */ ||(id == Identity("5067b21b83:0:8af477730f5055c48135b84bed6720a35bca4c0e34be4060a4c636288b1ec22217eb22709d610c66ed464c643130c51411bbb0294eef12fbe8ecc1a1e2c63a7a"))
- /* 0007 */ ||(id == Identity("4f5e97a8f1:0:57880d056d7baeb04bbc057d6f16e6cb41388570e87f01492fce882485f65a798648595610a3ad49885604e7fb1db2dd3c2c534b75e42c3c0b110ad07b4bb138"))
- /* 0008 */ ||(id == Identity("580bbb8e15:0:ad5ef31155bebc6bc413991992387e083fed26d699997ef76e7c947781edd47d1997161fa56ba337b1a2b44b129fd7c7197ce5185382f06011bc88d1363b4ddd"))
- );
- }
-
- void doUpdateCheck()
- {
- std::string url(OneService::autoUpdateUrl());
- if ((url.length() <= 7)||(url.substr(0,7) != "http://"))
- return;
-
- std::string httpHost;
- std::string httpPath;
- {
- std::size_t slashIdx = url.substr(7).find_first_of('/');
- if (slashIdx == std::string::npos) {
- httpHost = url.substr(7);
- httpPath = "/";
- } else {
- httpHost = url.substr(7,slashIdx);
- httpPath = url.substr(slashIdx + 7);
- }
- }
- if (httpHost.length() == 0)
- return;
-
- std::vector<InetAddress> ips(OSUtils::resolve(httpHost.c_str()));
- for(std::vector<InetAddress>::iterator ip(ips.begin());ip!=ips.end();++ip) {
- if (!ip->port())
- ip->setPort(80);
- std::string nfoPath = httpPath + "LATEST.nfo";
- std::map<std::string,std::string> requestHeaders,responseHeaders;
- std::string body;
- requestHeaders["Host"] = httpHost;
- unsigned int scode = Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),nfoPath.c_str(),requestHeaders,responseHeaders,body);
- //fprintf(stderr,"UPDATE %s %s %u %lu\n",ip->toString().c_str(),nfoPath.c_str(),scode,body.length());
- if ((scode == 200)&&(body.length() > 0)) {
- /* NFO fields:
- *
- * file=<filename>
- * signedBy=<signing identity>
- * ed25519=<ed25519 ECC signature of archive in hex>
- * vMajor=<major version>
- * vMinor=<minor version>
- * vRevision=<revision> */
- Dictionary<4096> nfo(body.c_str());
- char tmp[2048];
-
- if (nfo.get("vMajor",tmp,sizeof(tmp)) <= 0) return;
- const unsigned int vMajor = Utils::strToUInt(tmp);
- if (nfo.get("vMinor",tmp,sizeof(tmp)) <= 0) return;
- const unsigned int vMinor = Utils::strToUInt(tmp);
- if (nfo.get("vRevision",tmp,sizeof(tmp)) <= 0) return;
- const unsigned int vRevision = Utils::strToUInt(tmp);
- if (Utils::compareVersion(vMajor,vMinor,vRevision,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION) <= 0) {
- //fprintf(stderr,"UPDATE %u.%u.%u is not newer than our version\n",vMajor,vMinor,vRevision);
- return;
- }
-
- if (nfo.get("signedBy",tmp,sizeof(tmp)) <= 0) return;
- Identity signedBy;
- if ((!signedBy.fromString(tmp))||(!isValidSigningIdentity(signedBy))) {
- //fprintf(stderr,"UPDATE invalid signedBy or not authorized signing identity.\n");
- return;
- }
-
- if (nfo.get("file",tmp,sizeof(tmp)) <= 0) return;
- std::string filePath(tmp);
- if ((!filePath.length())||(filePath.find("..") != std::string::npos))
- return;
- filePath = httpPath + filePath;
-
- std::string fileData;
- if (Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),filePath.c_str(),requestHeaders,responseHeaders,fileData) != 200) {
- //fprintf(stderr,"UPDATE GET %s failed\n",filePath.c_str());
- return;
- }
-
- if (nfo.get("ed25519",tmp,sizeof(tmp)) <= 0) return;
- std::string ed25519(Utils::unhex(tmp));
- if ((ed25519.length() == 0)||(!signedBy.verify(fileData.data(),(unsigned int)fileData.length(),ed25519.data(),(unsigned int)ed25519.length()))) {
- //fprintf(stderr,"UPDATE %s failed signature check!\n",filePath.c_str());
- return;
- }
-
- /* --------------------------------------------------------------- */
- /* We made it! Begin OS-specific installation code. */
-
-#ifdef __APPLE__
- /* OSX version is in the form of a MacOSX .pkg file, so we will
- * launch installer (normally in /usr/sbin) to install it. It will
- * then turn around and shut down the service, update files, and
- * relaunch. */
- {
- char bashp[128],pkgp[128];
- Utils::snprintf(bashp,sizeof(bashp),"/tmp/ZeroTierOne-update-%u.%u.%u.sh",vMajor,vMinor,vRevision);
- Utils::snprintf(pkgp,sizeof(pkgp),"/tmp/ZeroTierOne-update-%u.%u.%u.pkg",vMajor,vMinor,vRevision);
- FILE *pkg = fopen(pkgp,"w");
- if ((!pkg)||(fwrite(fileData.data(),fileData.length(),1,pkg) != 1)) {
- fclose(pkg);
- unlink(bashp);
- unlink(pkgp);
- fprintf(stderr,"UPDATE error writing %s\n",pkgp);
- return;
- }
- fclose(pkg);
- FILE *bash = fopen(bashp,"w");
- if (!bash) {
- fclose(pkg);
- unlink(bashp);
- unlink(pkgp);
- fprintf(stderr,"UPDATE error writing %s\n",bashp);
- return;
- }
- fprintf(bash,
- "#!/bin/bash\n"
- "export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin\n"
- "sleep 1\n"
- "installer -pkg \"%s\" -target /\n"
- "sleep 1\n"
- "rm -f \"%s\" \"%s\"\n"
- "exit 0\n",
- pkgp,
- pkgp,
- bashp);
- fclose(bash);
- long pid = (long)vfork();
- if (pid == 0) {
- setsid(); // detach from parent so that shell isn't killed when parent is killed
- signal(SIGHUP,SIG_IGN);
- signal(SIGTERM,SIG_IGN);
- signal(SIGQUIT,SIG_IGN);
- execl("/bin/bash","/bin/bash",bashp,(char *)0);
- exit(0);
- }
- }
-#endif // __APPLE__
-
-#ifdef __WINDOWS__
- /* Windows version comes in the form of .MSI package that
- * takes care of everything. */
- {
- char tempp[512],batp[512],msip[512],cmdline[512];
- if (GetTempPathA(sizeof(tempp),tempp) <= 0)
- return;
- CreateDirectoryA(tempp,(LPSECURITY_ATTRIBUTES)0);
- Utils::snprintf(batp,sizeof(batp),"%s\\ZeroTierOne-update-%u.%u.%u.bat",tempp,vMajor,vMinor,vRevision);
- Utils::snprintf(msip,sizeof(msip),"%s\\ZeroTierOne-update-%u.%u.%u.msi",tempp,vMajor,vMinor,vRevision);
- FILE *msi = fopen(msip,"wb");
- if ((!msi)||(fwrite(fileData.data(),(size_t)fileData.length(),1,msi) != 1)) {
- fclose(msi);
- return;
- }
- fclose(msi);
- FILE *bat = fopen(batp,"wb");
- if (!bat)
- return;
- fprintf(bat,
- "TIMEOUT.EXE /T 1 /NOBREAK\r\n"
- "NET.EXE STOP \"ZeroTierOneService\"\r\n"
- "TIMEOUT.EXE /T 1 /NOBREAK\r\n"
- "MSIEXEC.EXE /i \"%s\" /qn\r\n"
- "TIMEOUT.EXE /T 1 /NOBREAK\r\n"
- "NET.EXE START \"ZeroTierOneService\"\r\n"
- "DEL \"%s\"\r\n"
- "DEL \"%s\"\r\n",
- msip,
- msip,
- batp);
- fclose(bat);
- STARTUPINFOA si;
- PROCESS_INFORMATION pi;
- memset(&si,0,sizeof(si));
- memset(&pi,0,sizeof(pi));
- Utils::snprintf(cmdline,sizeof(cmdline),"CMD.EXE /c \"%s\"",batp);
- CreateProcessA(NULL,cmdline,NULL,NULL,FALSE,CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi);
- }
-#endif // __WINDOWS__
-
- /* --------------------------------------------------------------- */
+// Maximum write buffer size for outgoing TCP connections (sanity limit)
+#define ZT_TCP_MAX_WRITEQ_SIZE 33554432
- return;
- } // else try to fetch from next IP address
- }
- }
+// TCP activity timeout
+#define ZT_TCP_ACTIVITY_TIMEOUT 60000
- void threadMain()
- throw()
- {
- try {
- this->doUpdateCheck();
- } catch ( ... ) {}
- }
-};
-static BackgroundSoftwareUpdateChecker backgroundSoftwareUpdateChecker;
-#endif // ZT_AUTO_UPDATE
-
-static bool isBlacklistedLocalInterfaceForZeroTierTraffic(const char *ifn)
-{
-#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
- if ((ifn[0] == 'l')&&(ifn[1] == 'o')) return true; // loopback
- if ((ifn[0] == 'z')&&(ifn[1] == 't')) return true; // sanity check: zt#
- if ((ifn[0] == 't')&&(ifn[1] == 'u')&&(ifn[2] == 'n')) return true; // tun# is probably an OpenVPN tunnel or similar
- if ((ifn[0] == 't')&&(ifn[1] == 'a')&&(ifn[2] == 'p')) return true; // tap# is probably an OpenVPN tunnel or similar
-#endif
+namespace ZeroTier {
-#ifdef __APPLE__
- if ((ifn[0] == 'l')&&(ifn[1] == 'o')) return true; // loopback
- if ((ifn[0] == 'z')&&(ifn[1] == 't')) return true; // sanity check: zt#
- if ((ifn[0] == 't')&&(ifn[1] == 'u')&&(ifn[2] == 'n')) return true; // tun# is probably an OpenVPN tunnel or similar
- if ((ifn[0] == 't')&&(ifn[1] == 'a')&&(ifn[2] == 'p')) return true; // tap# is probably an OpenVPN tunnel or similar
- if ((ifn[0] == 'u')&&(ifn[1] == 't')&&(ifn[2] == 'u')&&(ifn[3] == 'n')) return true; // ... as is utun#
-#endif
+namespace {
- return false;
-}
+// Fake TLS hello for TCP tunnel outgoing connections (TUNNELED mode)
+static const char ZT_TCP_TUNNEL_HELLO[9] = { 0x17,0x03,0x03,0x00,0x04,(char)ZEROTIER_ONE_VERSION_MAJOR,(char)ZEROTIER_ONE_VERSION_MINOR,(char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff),(char)(ZEROTIER_ONE_VERSION_REVISION & 0xff) };
static std::string _trimString(const std::string &s)
{
@@ -400,22 +188,134 @@ static std::string _trimString(const std::string &s)
return s.substr(start,end - start);
}
-class OneServiceImpl;
+static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,const std::string &portDeviceName,const OneService::NetworkSettings &localSettings)
+{
+ char tmp[256];
+
+ const char *nstatus = "",*ntype = "";
+ switch(nc->status) {
+ case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: nstatus = "REQUESTING_CONFIGURATION"; break;
+ case ZT_NETWORK_STATUS_OK: nstatus = "OK"; break;
+ case ZT_NETWORK_STATUS_ACCESS_DENIED: nstatus = "ACCESS_DENIED"; break;
+ case ZT_NETWORK_STATUS_NOT_FOUND: nstatus = "NOT_FOUND"; break;
+ case ZT_NETWORK_STATUS_PORT_ERROR: nstatus = "PORT_ERROR"; break;
+ case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: nstatus = "CLIENT_TOO_OLD"; break;
+ }
+ switch(nc->type) {
+ case ZT_NETWORK_TYPE_PRIVATE: ntype = "PRIVATE"; break;
+ case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
+ }
-static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
-static void SnodeEventCallback(ZT_Node *node,void *uptr,enum ZT_Event event,const void *metaData);
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize);
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure);
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
-static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
-
-#ifdef ZT_ENABLE_CLUSTER
-static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
-static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z);
-#endif
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid);
+ nj["id"] = tmp;
+ nj["nwid"] = tmp;
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff));
+ nj["mac"] = tmp;
+ nj["name"] = nc->name;
+ nj["status"] = nstatus;
+ nj["type"] = ntype;
+ nj["mtu"] = nc->mtu;
+ nj["dhcp"] = (bool)(nc->dhcp != 0);
+ nj["bridge"] = (bool)(nc->bridge != 0);
+ nj["broadcastEnabled"] = (bool)(nc->broadcastEnabled != 0);
+ nj["portError"] = nc->portError;
+ nj["netconfRevision"] = nc->netconfRevision;
+ nj["portDeviceName"] = portDeviceName;
+ nj["allowManaged"] = localSettings.allowManaged;
+ nj["allowGlobal"] = localSettings.allowGlobal;
+ nj["allowDefault"] = localSettings.allowDefault;
+
+ nlohmann::json aa = nlohmann::json::array();
+ for(unsigned int i=0;i<nc->assignedAddressCount;++i) {
+ aa.push_back(reinterpret_cast<const InetAddress *>(&(nc->assignedAddresses[i]))->toString(tmp));
+ }
+ nj["assignedAddresses"] = aa;
+
+ nlohmann::json ra = nlohmann::json::array();
+ for(unsigned int i=0;i<nc->routeCount;++i) {
+ nlohmann::json rj;
+ rj["target"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].target))->toString(tmp);
+ if (nc->routes[i].via.ss_family == nc->routes[i].target.ss_family)
+ rj["via"] = reinterpret_cast<const InetAddress *>(&(nc->routes[i].via))->toIpString(tmp);
+ else rj["via"] = nlohmann::json();
+ rj["flags"] = (int)nc->routes[i].flags;
+ rj["metric"] = (int)nc->routes[i].metric;
+ ra.push_back(rj);
+ }
+ nj["routes"] = ra;
+}
+
+static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
+{
+ char tmp[256];
-static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
+ const char *prole = "";
+ switch(peer->role) {
+ case ZT_PEER_ROLE_LEAF: prole = "LEAF"; break;
+ case ZT_PEER_ROLE_MOON: prole = "MOON"; break;
+ case ZT_PEER_ROLE_PLANET: prole = "PLANET"; break;
+ }
+
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
+ pj["address"] = tmp;
+ pj["versionMajor"] = peer->versionMajor;
+ pj["versionMinor"] = peer->versionMinor;
+ pj["versionRev"] = peer->versionRev;
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%d.%d.%d",peer->versionMajor,peer->versionMinor,peer->versionRev);
+ pj["version"] = tmp;
+ pj["latency"] = peer->latency;
+ pj["role"] = prole;
+
+ nlohmann::json pa = nlohmann::json::array();
+ for(unsigned int i=0;i<peer->pathCount;++i) {
+ int64_t lastSend = peer->paths[i].lastSend;
+ int64_t lastReceive = peer->paths[i].lastReceive;
+ nlohmann::json j;
+ j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString(tmp);
+ j["lastSend"] = (lastSend < 0) ? 0 : lastSend;
+ j["lastReceive"] = (lastReceive < 0) ? 0 : lastReceive;
+ j["trustedPathId"] = peer->paths[i].trustedPathId;
+ j["active"] = (bool)(peer->paths[i].expired == 0);
+ j["expired"] = (bool)(peer->paths[i].expired != 0);
+ j["preferred"] = (bool)(peer->paths[i].preferred != 0);
+ pa.push_back(j);
+ }
+ pj["paths"] = pa;
+}
+
+static void _moonToJson(nlohmann::json &mj,const World &world)
+{
+ char tmp[4096];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",world.id());
+ mj["id"] = tmp;
+ mj["timestamp"] = world.timestamp();
+ mj["signature"] = Utils::hex(world.signature().data,ZT_C25519_SIGNATURE_LEN,tmp);
+ mj["updatesMustBeSignedBy"] = Utils::hex(world.updatesMustBeSignedBy().data,ZT_C25519_PUBLIC_KEY_LEN,tmp);
+ nlohmann::json ra = nlohmann::json::array();
+ for(std::vector<World::Root>::const_iterator r(world.roots().begin());r!=world.roots().end();++r) {
+ nlohmann::json rj;
+ rj["identity"] = r->identity.toString(false,tmp);
+ nlohmann::json eps = nlohmann::json::array();
+ for(std::vector<InetAddress>::const_iterator a(r->stableEndpoints.begin());a!=r->stableEndpoints.end();++a)
+ eps.push_back(a->toString(tmp));
+ rj["stableEndpoints"] = eps;
+ ra.push_back(rj);
+ }
+ mj["roots"] = ra;
+ mj["waiting"] = false;
+}
+
+class OneServiceImpl;
+
+static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
+static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData);
+static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len);
+static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen);
+static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
+static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int64_t localSocket,const struct sockaddr_storage *remoteAddr);
+static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
+static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
static int ShttpOnMessageBegin(http_parser *parser);
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
@@ -453,49 +353,70 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
};
#endif
+/**
+ * A TCP connection and related state and buffers
+ */
struct TcpConnection
{
enum {
+ TCP_UNCATEGORIZED_INCOMING, // uncategorized incoming connection
TCP_HTTP_INCOMING,
- TCP_HTTP_OUTGOING, // not currently used
- TCP_TUNNEL_OUTGOING // fale-SSL outgoing tunnel -- HTTP-related fields are not used
+ TCP_HTTP_OUTGOING,
+ TCP_TUNNEL_OUTGOING // TUNNELED mode proxy outbound connection
} type;
- bool shouldKeepAlive;
OneServiceImpl *parent;
PhySocket *sock;
- InetAddress from;
+ InetAddress remoteAddr;
+ uint64_t lastReceive;
+
+ // Used for inbound HTTP connections
http_parser parser;
unsigned long messageSize;
- uint64_t lastActivity;
-
std::string currentHeaderField;
std::string currentHeaderValue;
-
std::string url;
std::string status;
std::map< std::string,std::string > headers;
- std::string body;
- std::string writeBuf;
- Mutex writeBuf_m;
+ std::string readq;
+ std::string writeq;
+ Mutex writeq_m;
};
-// Used to pseudo-randomize local source port picking
-static volatile unsigned int _udpPortPickerCounter = 0;
-
class OneServiceImpl : public OneService
{
public:
// begin member variables --------------------------------------------------
const std::string _homePath;
- BackgroundResolver _tcpFallbackResolver;
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- SqliteNetworkController *_controller;
-#endif
+ std::string _authToken;
+ std::string _controllerDbPath;
+ const std::string _networksPath;
+ const std::string _moonsPath;
+
+ EmbeddedNetworkController *_controller;
Phy<OneServiceImpl *> _phy;
Node *_node;
+ SoftwareUpdater *_updater;
+ PhySocket *_localControlSocket4;
+ PhySocket *_localControlSocket6;
+ bool _updateAutoApply;
+ bool _allowTcpFallbackRelay;
+ unsigned int _primaryPort;
+ volatile unsigned int _udpPortPickerCounter;
+
+ // Local configuration and memo-ized information from it
+ json _localConfig;
+ Hashtable< uint64_t,std::vector<InetAddress> > _v4Hints;
+ Hashtable< uint64_t,std::vector<InetAddress> > _v6Hints;
+ Hashtable< uint64_t,std::vector<InetAddress> > _v4Blacklists;
+ Hashtable< uint64_t,std::vector<InetAddress> > _v6Blacklists;
+ std::vector< InetAddress > _globalV4Blacklist;
+ std::vector< InetAddress > _globalV6Blacklist;
+ std::vector< InetAddress > _allowManagementFrom;
+ std::vector< std::string > _interfacePrefixBlacklist;
+ Mutex _localConfig_m;
/*
* To attempt to handle NAT/gateway craziness we use three local UDP ports:
@@ -508,17 +429,8 @@ public:
* destructively with uPnP port mapping behavior in very weird buggy ways.
* It's only used if uPnP/NAT-PMP is enabled in this build.
*/
-
- Binder _bindings[3];
unsigned int _ports[3];
- uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr
-
- // Sockets for JSON API -- bound only to V4 and V6 localhost
- PhySocket *_v4TcpControlSocket;
- PhySocket *_v6TcpControlSocket;
-
- // JSON API handler
- ControlPlane *_controlPlane;
+ Binder _binder;
// Time we last received a packet from a global address
uint64_t _lastDirectReceiveFromGlobal;
@@ -530,7 +442,7 @@ public:
uint64_t _lastRestart;
// Deadline for the next background task service function
- volatile uint64_t _nextBackgroundTaskDeadline;
+ volatile int64_t _nextBackgroundTaskDeadline;
// Configured networks
struct NetworkState
@@ -547,14 +459,15 @@ public:
EthernetTap *tap;
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
std::vector<InetAddress> managedIps;
- std::list<ManagedRoute> managedRoutes;
+ std::list< SharedPtr<ManagedRoute> > managedRoutes;
NetworkSettings settings;
};
std::map<uint64_t,NetworkState> _nets;
Mutex _nets_m;
// Active TCP/IP connections
- std::set< TcpConnection * > _tcpConnections; // no mutex for this since it's done in the main loop thread only
+ std::vector< TcpConnection * > _tcpConnections;
+ Mutex _tcpConnections_m;
TcpConnection *_tcpFallbackTunnel;
// Termination status information
@@ -563,17 +476,11 @@ public:
Mutex _termReason_m;
// uPnP/NAT-PMP port mapper if enabled
+ bool _portMappingEnabled; // local.conf settings
#ifdef ZT_USE_MINIUPNPC
PortMapper *_portMapper;
#endif
- // Cluster management instance if enabled
-#ifdef ZT_ENABLE_CLUSTER
- PhySocket *_clusterMessageSocket;
- ClusterDefinition *_clusterDefinition;
- unsigned int _clusterMemberId;
-#endif
-
// Set to false to force service to stop
volatile bool _run;
Mutex _run_m;
@@ -582,13 +489,18 @@ public:
OneServiceImpl(const char *hp,unsigned int port) :
_homePath((hp) ? hp : ".")
- ,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY)
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- ,_controller((SqliteNetworkController *)0)
-#endif
+ ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S "controller.d")
+ ,_networksPath(_homePath + ZT_PATH_SEPARATOR_S "networks.d")
+ ,_moonsPath(_homePath + ZT_PATH_SEPARATOR_S "moons.d")
+ ,_controller((EmbeddedNetworkController *)0)
,_phy(this,false,true)
,_node((Node *)0)
- ,_controlPlane((ControlPlane *)0)
+ ,_updater((SoftwareUpdater *)0)
+ ,_localControlSocket4((PhySocket *)0)
+ ,_localControlSocket6((PhySocket *)0)
+ ,_updateAutoApply(false)
+ ,_primaryPort(port)
+ ,_udpPortPickerCounter(0)
,_lastDirectReceiveFromGlobal(0)
#ifdef ZT_TCP_FALLBACK_RELAY
,_lastSendToGlobalV4(0)
@@ -597,107 +509,40 @@ public:
,_nextBackgroundTaskDeadline(0)
,_tcpFallbackTunnel((TcpConnection *)0)
,_termReason(ONE_STILL_RUNNING)
+ ,_portMappingEnabled(true)
#ifdef ZT_USE_MINIUPNPC
,_portMapper((PortMapper *)0)
#endif
-#ifdef ZT_ENABLE_CLUSTER
- ,_clusterMessageSocket((PhySocket *)0)
- ,_clusterDefinition((ClusterDefinition *)0)
- ,_clusterMemberId(0)
-#endif
,_run(true)
{
_ports[0] = 0;
_ports[1] = 0;
_ports[2] = 0;
-
- // The control socket is bound to the default/static port on localhost. If we
- // can do this, we have successfully allocated a port. The binders will take
- // care of binding non-local addresses for ZeroTier traffic.
- const int portTrials = (port == 0) ? 256 : 1; // if port is 0, pick random
- for(int k=0;k<portTrials;++k) {
- if (port == 0) {
- unsigned int randp = 0;
- Utils::getSecureRandom(&randp,sizeof(randp));
- port = 20000 + (randp % 45500);
- }
-
- if (_trialBind(port)) {
- struct sockaddr_in in4;
- memset(&in4,0,sizeof(in4));
- in4.sin_family = AF_INET;
- in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @127.0.0.1
- in4.sin_port = Utils::hton((uint16_t)port);
- _v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
-
- struct sockaddr_in6 in6;
- memset((void *)&in6,0,sizeof(in6));
- in6.sin6_family = AF_INET6;
- in6.sin6_port = in4.sin_port;
- in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
- _v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
-
- // We must bind one of IPv4 or IPv6 -- support either failing to support hosts that
- // have only IPv4 or only IPv6 stacks.
- if ((_v4TcpControlSocket)||(_v6TcpControlSocket)) {
- _ports[0] = port;
- break;
- } else {
- if (_v4TcpControlSocket)
- _phy.close(_v4TcpControlSocket,false);
- if (_v6TcpControlSocket)
- _phy.close(_v6TcpControlSocket,false);
- port = 0;
- }
- } else {
- port = 0;
- }
- }
-
- if (_ports[0] == 0)
- throw std::runtime_error("cannot bind to local control interface port");
-
- char portstr[64];
- Utils::snprintf(portstr,sizeof(portstr),"%u",_ports[0]);
- OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr));
}
virtual ~OneServiceImpl()
{
- for(int i=0;i<3;++i)
- _bindings[i].closeAll(_phy);
-
- _phy.close(_v4TcpControlSocket);
- _phy.close(_v6TcpControlSocket);
-
-#ifdef ZT_ENABLE_CLUSTER
- _phy.close(_clusterMessageSocket);
-#endif
-
+ _binder.closeAll(_phy);
+ _phy.close(_localControlSocket4);
+ _phy.close(_localControlSocket6);
#ifdef ZT_USE_MINIUPNPC
delete _portMapper;
#endif
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
delete _controller;
-#endif
-#ifdef ZT_ENABLE_CLUSTER
- delete _clusterDefinition;
-#endif
}
virtual ReasonForTermination run()
{
try {
- std::string authToken;
{
- std::string authTokenPath(_homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
- if (!OSUtils::readFile(authTokenPath.c_str(),authToken)) {
+ const std::string authTokenPath(_homePath + ZT_PATH_SEPARATOR_S "authtoken.secret");
+ if (!OSUtils::readFile(authTokenPath.c_str(),_authToken)) {
unsigned char foo[24];
Utils::getSecureRandom(foo,sizeof(foo));
- authToken = "";
+ _authToken = "";
for(unsigned int i=0;i<sizeof(foo);++i)
- authToken.push_back("abcdefghijklmnopqrstuvwxyz0123456789"[(unsigned long)foo[i] % 36]);
- if (!OSUtils::writeFile(authTokenPath.c_str(),authToken)) {
+ _authToken.push_back("abcdefghijklmnopqrstuvwxyz0123456789"[(unsigned long)foo[i] % 36]);
+ if (!OSUtils::writeFile(authTokenPath.c_str(),_authToken)) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
_fatalErrorMessage = "authtoken.secret could not be written";
@@ -706,71 +551,34 @@ public:
OSUtils::lockDownFile(authTokenPath.c_str(),false);
}
}
+ _authToken = _trimString(_authToken);
}
- authToken = _trimString(authToken);
-
- _node = new Node(
- OSUtils::now(),
- this,
- SnodeDataStoreGetFunction,
- SnodeDataStorePutFunction,
- SnodeWirePacketSendFunction,
- SnodeVirtualNetworkFrameFunction,
- SnodeVirtualNetworkConfigFunction,
- SnodePathCheckFunction,
- SnodeEventCallback);
- // Attempt to bind to a secondary port chosen from our ZeroTier address.
- // This exists because there are buggy NATs out there that fail if more
- // than one device behind the same NAT tries to use the same internal
- // private address port number.
- _ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
- for(int i=0;;++i) {
- if (i > 1000) {
- _ports[1] = 0;
- break;
- } else if (++_ports[1] >= 65536) {
- _ports[1] = 20000;
- }
- if (_trialBind(_ports[1]))
- break;
- }
-
-#ifdef ZT_USE_MINIUPNPC
- // If we're running uPnP/NAT-PMP, bind a *third* port for that. We can't
- // use the other two ports for that because some NATs do really funky
- // stuff with ports that are explicitly mapped that breaks things.
- if (_ports[1]) {
- _ports[2] = _ports[1];
- for(int i=0;;++i) {
- if (i > 1000) {
- _ports[2] = 0;
- break;
- } else if (++_ports[2] >= 65536) {
- _ports[2] = 20000;
- }
- if (_trialBind(_ports[2]))
- break;
- }
- if (_ports[2]) {
- char uniqueName[64];
- Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]);
- _portMapper = new PortMapper(_ports[2],uniqueName);
- }
+ {
+ struct ZT_Node_Callbacks cb;
+ cb.version = 0;
+ cb.stateGetFunction = SnodeStateGetFunction;
+ cb.statePutFunction = SnodeStatePutFunction;
+ cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
+ cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
+ cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
+ cb.eventCallback = SnodeEventCallback;
+ cb.pathCheckFunction = SnodePathCheckFunction;
+ cb.pathLookupFunction = SnodePathLookupFunction;
+ _node = new Node(this,(void *)0,&cb,OSUtils::now());
}
-#endif
-
- for(int i=0;i<3;++i)
- _portsBE[i] = Utils::hton((uint16_t)_ports[i]);
+ // Read local configuration
+ std::vector<InetAddress> explicitBind;
{
- FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustedpaths").c_str(),"r");
- uint64_t ids[ZT_MAX_TRUSTED_PATHS];
- InetAddress addresses[ZT_MAX_TRUSTED_PATHS];
+ std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc;
+
+ // LEGACY: support old "trustedpaths" flat file
+ FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
if (trustpaths) {
+ fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
char buf[1024];
- unsigned int count = 0;
- while ((fgets(buf,sizeof(buf),trustpaths))&&(count < ZT_MAX_TRUSTED_PATHS)) {
+ while (fgets(buf,sizeof(buf),trustpaths)) {
int fno = 0;
char *saveptr = (char *)0;
uint64_t trustedPathId = 0;
@@ -783,102 +591,197 @@ public:
} else break;
++fno;
}
- if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (trustedPathNetwork.netmaskBits() > 0) ) {
- ids[count] = trustedPathId;
- addresses[count] = trustedPathNetwork;
- ++count;
+ if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) {
+ ppc[trustedPathNetwork].trustedPathId = trustedPathId;
+ ppc[trustedPathNetwork].mtu = 0; // use default
}
}
fclose(trustpaths);
- if (count)
- _node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(addresses),ids,count);
}
- }
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- _controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
- _node->setNetconfMaster((void *)_controller);
-#endif
+ // Read local config file
+ Mutex::Lock _l2(_localConfig_m);
+ std::string lcbuf;
+ if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(),lcbuf)) {
+ try {
+ _localConfig = OSUtils::jsonParse(lcbuf);
+ if (!_localConfig.is_object()) {
+ fprintf(stderr,"WARNING: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S);
+ }
+ } catch ( ... ) {
+ fprintf(stderr,"WARNING: unable to parse local.conf (invalid JSON)" ZT_EOL_S);
+ }
+ }
-#ifdef ZT_ENABLE_CLUSTER
- if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S + "cluster").c_str())) {
- _clusterDefinition = new ClusterDefinition(_node->address(),(_homePath + ZT_PATH_SEPARATOR_S + "cluster").c_str());
- if (_clusterDefinition->size() > 0) {
- std::vector<ClusterDefinition::MemberDefinition> members(_clusterDefinition->members());
- for(std::vector<ClusterDefinition::MemberDefinition>::iterator m(members.begin());m!=members.end();++m) {
- PhySocket *cs = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(m->clusterEndpoint)));
- if (cs) {
- if (_clusterMessageSocket) {
- _phy.close(_clusterMessageSocket,false);
- _phy.close(cs,false);
-
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "Cluster: can't determine my cluster member ID: able to bind more than one cluster message socket IP/port!";
- return _termReason;
+ // Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere)
+ json &physical = _localConfig["physical"];
+ if (physical.is_object()) {
+ for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
+ InetAddress net(OSUtils::jsonString(phy.key(),"").c_str());
+ if (net) {
+ if (phy.value().is_object()) {
+ uint64_t tpid;
+ if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
+ if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6))
+ ppc[net].trustedPathId = tpid;
+ }
+ ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default
}
- _clusterMessageSocket = cs;
- _clusterMemberId = m->id;
}
}
+ }
- if (!_clusterMessageSocket) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "Cluster: can't determine my cluster member ID: unable to bind to any cluster message socket IP/port.";
- return _termReason;
+ json &settings = _localConfig["settings"];
+ if (settings.is_object()) {
+ // Allow controller DB path to be put somewhere else
+ const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],""));
+ if (cdbp.length() > 0)
+ _controllerDbPath = cdbp;
+
+ // Bind to wildcard instead of to specific interfaces (disables full tunnel capability)
+ json &bind = settings["bind"];
+ if (bind.is_array()) {
+ for(unsigned long i=0;i<bind.size();++i) {
+ const std::string ips(OSUtils::jsonString(bind[i],""));
+ if (ips.length() > 0) {
+ InetAddress ip(ips.c_str());
+ if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6))
+ explicitBind.push_back(ip);
+ }
+ }
}
+ }
+
+ // Set trusted paths if there are any
+ if (ppc.size() > 0) {
+ for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
+ _node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
+ }
+ }
+
+ // Apply other runtime configuration from local.conf
+ applyLocalConfig();
+
+ // Make sure we can use the primary port, and hunt for one if configured to do so
+ const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
+ for(int k=0;k<portTrials;++k) {
+ if (_primaryPort == 0) {
+ unsigned int randp = 0;
+ Utils::getSecureRandom(&randp,sizeof(randp));
+ _primaryPort = 20000 + (randp % 45500);
+ }
+ if (_trialBind(_primaryPort)) {
+ _ports[0] = _primaryPort;
+ } else {
+ _primaryPort = 0;
+ }
+ }
+ if (_ports[0] == 0) {
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = "cannot bind to local control interface port";
+ return _termReason;
+ }
- const ClusterDefinition::MemberDefinition &me = (*_clusterDefinition)[_clusterMemberId];
- InetAddress endpoints[255];
- unsigned int numEndpoints = 0;
- for(std::vector<InetAddress>::const_iterator i(me.zeroTierEndpoints.begin());i!=me.zeroTierEndpoints.end();++i)
- endpoints[numEndpoints++] = *i;
-
- if (_node->clusterInit(_clusterMemberId,reinterpret_cast<const struct sockaddr_storage *>(endpoints),numEndpoints,me.x,me.y,me.z,&SclusterSendFunction,this,_clusterDefinition->geo().available() ? &SclusterGeoIpFunction : 0,this) == ZT_RESULT_OK) {
- std::vector<ClusterDefinition::MemberDefinition> members(_clusterDefinition->members());
- for(std::vector<ClusterDefinition::MemberDefinition>::iterator m(members.begin());m!=members.end();++m) {
- if (m->id != _clusterMemberId)
- _node->clusterAddMember(m->id);
+ // Bind TCP control socket to 127.0.0.1 and ::1 as well for loopback TCP control socket queries
+ {
+ struct sockaddr_in lo4;
+ memset(&lo4,0,sizeof(lo4));
+ lo4.sin_family = AF_INET;
+ lo4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001);
+ lo4.sin_port = Utils::hton((uint16_t)_ports[0]);
+ _localControlSocket4 = _phy.tcpListen((const struct sockaddr *)&lo4);
+ struct sockaddr_in6 lo6;
+ memset(&lo6,0,sizeof(lo6));
+ lo6.sin6_family = AF_INET6;
+ lo6.sin6_addr.s6_addr[15] = 1;
+ lo6.sin6_port = lo4.sin_port;
+ _localControlSocket6 = _phy.tcpListen((const struct sockaddr *)&lo6);
+ }
+
+ // Save primary port to a file so CLIs and GUIs can learn it easily
+ char portstr[64];
+ OSUtils::ztsnprintf(portstr,sizeof(portstr),"%u",_ports[0]);
+ OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S "zerotier-one.port").c_str(),std::string(portstr));
+
+ // Attempt to bind to a secondary port chosen from our ZeroTier address.
+ // This exists because there are buggy NATs out there that fail if more
+ // than one device behind the same NAT tries to use the same internal
+ // private address port number. Buggy NATs are a running theme.
+ _ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
+ for(int i=0;;++i) {
+ if (i > 1000) {
+ _ports[1] = 0;
+ break;
+ } else if (++_ports[1] >= 65536) {
+ _ports[1] = 20000;
+ }
+ if (_trialBind(_ports[1]))
+ break;
+ }
+
+#ifdef ZT_USE_MINIUPNPC
+ if (_portMappingEnabled) {
+ // If we're running uPnP/NAT-PMP, bind a *third* port for that. We can't
+ // use the other two ports for that because some NATs do really funky
+ // stuff with ports that are explicitly mapped that breaks things.
+ if (_ports[1]) {
+ _ports[2] = _ports[1];
+ for(int i=0;;++i) {
+ if (i > 1000) {
+ _ports[2] = 0;
+ break;
+ } else if (++_ports[2] >= 65536) {
+ _ports[2] = 20000;
}
+ if (_trialBind(_ports[2]))
+ break;
+ }
+ if (_ports[2]) {
+ char uniqueName[64];
+ OSUtils::ztsnprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]);
+ _portMapper = new PortMapper(_ports[2],uniqueName);
}
- } else {
- delete _clusterDefinition;
- _clusterDefinition = (ClusterDefinition *)0;
}
}
#endif
- _controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str());
- _controlPlane->addAuthToken(authToken.c_str());
+ // Delete legacy iddb.d if present (cleanup)
+ OSUtils::rmDashRf((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str());
-#ifdef ZT_ENABLE_NETWORK_CONTROLLER
- _controlPlane->setController(_controller);
-#endif
+ // Network controller is now enabled by default for desktop and server
+ _controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str());
+ _node->setNetconfMaster((void *)_controller);
- { // Remember networks from previous session
- std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str()));
+ // Join existing networks in networks.d
+ {
+ std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str()));
for(std::vector<std::string>::iterator f(networksDotD.begin());f!=networksDotD.end();++f) {
std::size_t dot = f->find_last_of('.');
if ((dot == 16)&&(f->substr(16) == ".conf"))
- _node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0);
+ _node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0,(void *)0);
}
}
- // Start two background threads to handle expensive ops out of line
- Thread::start(_node);
- Thread::start(_node);
+ // Orbit existing moons in moons.d
+ {
+ std::vector<std::string> moonsDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "moons.d").c_str()));
+ for(std::vector<std::string>::iterator f(moonsDotD.begin());f!=moonsDotD.end();++f) {
+ std::size_t dot = f->find_last_of('.');
+ if ((dot == 16)&&(f->substr(16) == ".moon"))
+ _node->orbit((void *)0,Utils::hexStrToU64(f->substr(0,dot).c_str()),0);
+ }
+ }
+ // Main I/O loop
_nextBackgroundTaskDeadline = 0;
- uint64_t clockShouldBe = OSUtils::now();
+ int64_t clockShouldBe = OSUtils::now();
_lastRestart = clockShouldBe;
- uint64_t lastTapMulticastGroupCheck = 0;
- uint64_t lastTcpFallbackResolve = 0;
- uint64_t lastBindRefresh = 0;
- uint64_t lastLocalInterfaceAddressCheck = (OSUtils::now() - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
-#ifdef ZT_AUTO_UPDATE
- uint64_t lastSoftwareUpdateCheck = 0;
-#endif // ZT_AUTO_UPDATE
+ int64_t lastTapMulticastGroupCheck = 0;
+ int64_t lastBindRefresh = 0;
+ int64_t lastUpdateCheck = clockShouldBe;
+ int64_t lastCleanedPeersDb = 0;
+ int64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
for(;;) {
_run_m.lock();
if (!_run) {
@@ -891,7 +794,7 @@ public:
_run_m.unlock();
}
- const uint64_t now = OSUtils::now();
+ const int64_t now = OSUtils::now();
// Attempt to detect sleep/wake events by detecting delay overruns
bool restarted = false;
@@ -900,14 +803,23 @@ public:
restarted = true;
}
+ // Check for updates (if enabled)
+ if ((_updater)&&((now - lastUpdateCheck) > 10000)) {
+ lastUpdateCheck = now;
+ if (_updater->check(now) && _updateAutoApply)
+ _updater->apply();
+ }
+
// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default)
if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
lastBindRefresh = now;
+ unsigned int p[3];
+ unsigned int pc = 0;
for(int i=0;i<3;++i) {
- if (_ports[i]) {
- _bindings[i].refresh(_phy,_ports[i],*this);
- }
+ if (_ports[i])
+ p[pc++] = _ports[i];
}
+ _binder.refresh(_phy,p,pc,explicitBind,*this);
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
@@ -917,42 +829,40 @@ public:
}
}
- uint64_t dl = _nextBackgroundTaskDeadline;
+ // Run background task processor in core if it's time to do so
+ int64_t dl = _nextBackgroundTaskDeadline;
if (dl <= now) {
- _node->processBackgroundTasks(now,&_nextBackgroundTaskDeadline);
+ _node->processBackgroundTasks((void *)0,now,&_nextBackgroundTaskDeadline);
dl = _nextBackgroundTaskDeadline;
}
-#ifdef ZT_AUTO_UPDATE
- if ((now - lastSoftwareUpdateCheck) >= ZT_AUTO_UPDATE_CHECK_PERIOD) {
- lastSoftwareUpdateCheck = now;
- Thread::start(&backgroundSoftwareUpdateChecker);
- }
-#endif // ZT_AUTO_UPDATE
-
- if ((now - lastTcpFallbackResolve) >= ZT_TCP_FALLBACK_RERESOLVE_DELAY) {
- lastTcpFallbackResolve = now;
- _tcpFallbackResolver.resolveNow();
- }
-
+ // Close TCP fallback tunnel if we have direct UDP
if ((_tcpFallbackTunnel)&&((now - _lastDirectReceiveFromGlobal) < (ZT_TCP_FALLBACK_AFTER / 2)))
_phy.close(_tcpFallbackTunnel->sock);
+ // Sync multicast group memberships
if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
lastTapMulticastGroupCheck = now;
- Mutex::Lock _l(_nets_m);
- for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
- if (n->second.tap) {
- std::vector<MulticastGroup> added,removed;
- n->second.tap->scanMulticastGroups(added,removed);
- for(std::vector<MulticastGroup>::iterator m(added.begin());m!=added.end();++m)
- _node->multicastSubscribe(n->first,m->mac().toInt(),m->adi());
- for(std::vector<MulticastGroup>::iterator m(removed.begin());m!=removed.end();++m)
- _node->multicastUnsubscribe(n->first,m->mac().toInt(),m->adi());
+ std::vector< std::pair< uint64_t,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> > > > mgChanges;
+ {
+ Mutex::Lock _l(_nets_m);
+ mgChanges.reserve(_nets.size() + 1);
+ for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
+ if (n->second.tap) {
+ mgChanges.push_back(std::pair< uint64_t,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> > >(n->first,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> >()));
+ n->second.tap->scanMulticastGroups(mgChanges.back().second.first,mgChanges.back().second.second);
+ }
}
}
+ for(std::vector< std::pair< uint64_t,std::pair< std::vector<MulticastGroup>,std::vector<MulticastGroup> > > >::iterator c(mgChanges.begin());c!=mgChanges.end();++c) {
+ for(std::vector<MulticastGroup>::iterator m(c->second.first.begin());m!=c->second.first.end();++m)
+ _node->multicastSubscribe((void *)0,c->first,m->mac().toInt(),m->adi());
+ for(std::vector<MulticastGroup>::iterator m(c->second.second.begin());m!=c->second.second.end();++m)
+ _node->multicastUnsubscribe(c->first,m->mac().toInt(),m->adi());
+ }
}
+ // Sync information about physical network interfaces
if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
lastLocalInterfaceAddressCheck = now;
@@ -966,26 +876,33 @@ public:
}
#endif
- std::vector<InetAddress> boundAddrs(_bindings[0].allBoundLocalInterfaceAddresses());
+ std::vector<InetAddress> boundAddrs(_binder.allBoundLocalInterfaceAddresses());
for(std::vector<InetAddress>::const_iterator i(boundAddrs.begin());i!=boundAddrs.end();++i)
_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*i)));
}
+ // Clean peers.d periodically
+ if ((now - lastCleanedPeersDb) >= 3600000) {
+ lastCleanedPeersDb = now;
+ OSUtils::cleanDirectory((_homePath + ZT_PATH_SEPARATOR_S "peers.d").c_str(),now - 2592000000LL); // delete older than 30 days
+ }
+
const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
clockShouldBe = now + (uint64_t)delay;
_phy.poll(delay);
}
- } catch (std::exception &exc) {
+ } catch (std::exception &e) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = exc.what();
+ _fatalErrorMessage = std::string("unexpected exception in main thread: ")+e.what();
} catch ( ... ) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "unexpected exception in main thread";
+ _fatalErrorMessage = "unexpected exception in main thread: unknown exception";
}
try {
+ Mutex::Lock _l(_tcpConnections_m);
while (!_tcpConnections.empty())
_phy.close((*_tcpConnections.begin())->sock);
} catch ( ... ) {}
@@ -997,8 +914,8 @@ public:
_nets.clear();
}
- delete _controlPlane;
- _controlPlane = (ControlPlane *)0;
+ delete _updater;
+ _updater = (SoftwareUpdater *)0;
delete _node;
_node = (Node *)0;
@@ -1026,10 +943,46 @@ public:
else return std::string();
}
- virtual bool tcpFallbackActive() const
+#ifdef ZT_SDK
+ virtual void leave(const uint64_t hp)
+ {
+ _node->leave(hp, NULL, NULL);
+ }
+
+ virtual void join(const uint64_t hp)
+ {
+ _node->join(hp, NULL, NULL);
+ }
+
+ virtual std::string givenHomePath()
+ {
+ return _homePath;
+ }
+
+ std::vector<ZT_VirtualNetworkRoute> *getRoutes(uint64_t nwid)
{
- return (_tcpFallbackTunnel != (TcpConnection *)0);
+ Mutex::Lock _l(_nets_m);
+ NetworkState &n = _nets[nwid];
+ std::vector<ZT_VirtualNetworkRoute> *routes = new std::vector<ZT_VirtualNetworkRoute>();
+ for(int i=0; i<ZT_MAX_NETWORK_ROUTES; i++) {
+ routes->push_back(n.config.routes[i]);
+ }
+ return routes;
+ }
+
+ virtual Node *getNode()
+ {
+ return _node;
+ }
+
+ virtual void removeNets()
+ {
+ Mutex::Lock _l(_nets_m);
+ std::map<uint64_t,NetworkState>::iterator i;
+ for(i = _nets.begin(); i != _nets.end(); i++)
+ delete i->second.tap;
}
+#endif // ZT_SDK
virtual void terminate()
{
@@ -1045,7 +998,7 @@ public:
std::map<uint64_t,NetworkState>::const_iterator n(_nets.find(nwid));
if (n == _nets.end())
return false;
- memcpy(&settings,&(n->second.settings),sizeof(NetworkSettings));
+ settings = n->second.settings;
return true;
}
@@ -1056,10 +1009,10 @@ public:
std::map<uint64_t,NetworkState>::iterator n(_nets.find(nwid));
if (n == _nets.end())
return false;
- memcpy(&(n->second.settings),&settings,sizeof(NetworkSettings));
+ n->second.settings = settings;
- char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ char nlcpath[4096];
+ OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid);
FILE *out = fopen(nlcpath,"w");
if (out) {
fprintf(out,"allowManaged=%d\n",(int)n->second.settings.allowManaged);
@@ -1074,13 +1027,512 @@ public:
return true;
}
- // Begin private implementation methods
+ // =========================================================================
+ // Internal implementation methods for control plane, route setup, etc.
+ // =========================================================================
+
+ inline unsigned int handleControlPlaneHttpRequest(
+ const InetAddress &fromAddress,
+ unsigned int httpMethod,
+ const std::string &path,
+ const std::map<std::string,std::string> &headers,
+ const std::string &body,
+ std::string &responseBody,
+ std::string &responseContentType)
+ {
+ char tmp[256];
+ unsigned int scode = 404;
+ json res;
+ std::vector<std::string> ps(OSUtils::split(path.c_str(),"/","",""));
+ std::map<std::string,std::string> urlArgs;
+
+ /* Note: this is kind of restricted in what it'll take. It does not support
+ * URL encoding, and /'s in URL args will screw it up. But the only URL args
+ * it really uses in ?jsonp=funcionName, and otherwise it just takes simple
+ * paths to simply-named resources. */
+ if (ps.size() > 0) {
+ std::size_t qpos = ps[ps.size() - 1].find('?');
+ if (qpos != std::string::npos) {
+ std::string args(ps[ps.size() - 1].substr(qpos + 1));
+ ps[ps.size() - 1] = ps[ps.size() - 1].substr(0,qpos);
+ std::vector<std::string> asplit(OSUtils::split(args.c_str(),"&","",""));
+ for(std::vector<std::string>::iterator a(asplit.begin());a!=asplit.end();++a) {
+ std::size_t eqpos = a->find('=');
+ if (eqpos == std::string::npos)
+ urlArgs[*a] = "";
+ else urlArgs[a->substr(0,eqpos)] = a->substr(eqpos + 1);
+ }
+ }
+ }
+
+ bool isAuth = false;
+ {
+ std::map<std::string,std::string>::const_iterator ah(headers.find("x-zt1-auth"));
+ if ((ah != headers.end())&&(_authToken == ah->second)) {
+ isAuth = true;
+ } else {
+ ah = urlArgs.find("auth");
+ if ((ah != urlArgs.end())&&(_authToken == ah->second))
+ isAuth = true;
+ }
+ }
+
+#ifdef __SYNOLOGY__
+ // Authenticate via Synology's built-in cgi script
+ if (!isAuth) {
+ /*
+ fprintf(stderr, "path = %s\n", path.c_str());
+ fprintf(stderr, "headers.size=%d\n", headers.size());
+ std::map<std::string, std::string>::const_iterator it(headers.begin());
+ while(it != headers.end()) {
+ fprintf(stderr,"header[%s] = %s\n", (it->first).c_str(), (it->second).c_str());
+ it++;
+ }
+ */
+ // parse out url args
+ int synotoken_pos = path.find("SynoToken");
+ int argpos = path.find("?");
+ if(synotoken_pos != std::string::npos && argpos != std::string::npos) {
+ std::string cookie = path.substr(argpos+1, synotoken_pos-(argpos+1));
+ std::string synotoken = path.substr(synotoken_pos);
+ std::string cookie_val = cookie.substr(cookie.find("=")+1);
+ std::string synotoken_val = synotoken.substr(synotoken.find("=")+1);
+ // Set necessary env for auth script
+ std::map<std::string,std::string>::const_iterator ah2(headers.find("x-forwarded-for"));
+ setenv("HTTP_COOKIE", cookie_val.c_str(), true);
+ setenv("HTTP_X_SYNO_TOKEN", synotoken_val.c_str(), true);
+ setenv("REMOTE_ADDR", ah2->second.c_str(),true);
+ //fprintf(stderr, "HTTP_COOKIE: %s\n",std::getenv ("HTTP_COOKIE"));
+ //fprintf(stderr, "HTTP_X_SYNO_TOKEN: %s\n",std::getenv ("HTTP_X_SYNO_TOKEN"));
+ //fprintf(stderr, "REMOTE_ADDR: %s\n",std::getenv ("REMOTE_ADDR"));
+ // check synology web auth
+ char user[256], buf[1024];
+ FILE *fp = NULL;
+ bzero(user, 256);
+ fp = popen("/usr/syno/synoman/webman/modules/authenticate.cgi", "r");
+ if(!fp)
+ isAuth = false;
+ else {
+ bzero(buf, sizeof(buf));
+ fread(buf, 1024, 1, fp);
+ if(strlen(buf) > 0) {
+ snprintf(user, 256, "%s", buf);
+ isAuth = true;
+ }
+ }
+ pclose(fp);
+ }
+ }
+#endif
+
+ if (httpMethod == HTTP_GET) {
+ if (isAuth) {
+ if (ps[0] == "status") {
+ ZT_NodeStatus status;
+ _node->status(&status);
+
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",status.address);
+ res["address"] = tmp;
+ res["publicIdentity"] = status.publicIdentity;
+ res["online"] = (bool)(status.online != 0);
+ res["tcpFallbackActive"] = (_tcpFallbackTunnel != (TcpConnection *)0);
+ res["versionMajor"] = ZEROTIER_ONE_VERSION_MAJOR;
+ res["versionMinor"] = ZEROTIER_ONE_VERSION_MINOR;
+ res["versionRev"] = ZEROTIER_ONE_VERSION_REVISION;
+ res["versionBuild"] = ZEROTIER_ONE_VERSION_BUILD;
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%d.%d.%d",ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+ res["version"] = tmp;
+ res["clock"] = OSUtils::now();
+
+ {
+ Mutex::Lock _l(_localConfig_m);
+ res["config"] = _localConfig;
+ }
+ json &settings = res["config"]["settings"];
+ settings["primaryPort"] = OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
+ settings["allowTcpFallbackRelay"] = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],_allowTcpFallbackRelay);
+#ifdef ZT_USE_MINIUPNPC
+ settings["portMappingEnabled"] = OSUtils::jsonBool(settings["portMappingEnabled"],true);
+#else
+ settings["portMappingEnabled"] = false; // not supported in build
+#endif
+#ifndef ZT_SDK
+ settings["softwareUpdate"] = OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT);
+ settings["softwareUpdateChannel"] = OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL);
+#endif
+ const World planet(_node->planet());
+ res["planetWorldId"] = planet.id();
+ res["planetWorldTimestamp"] = planet.timestamp();
+
+ scode = 200;
+ } else if (ps[0] == "moon") {
+ std::vector<World> moons(_node->moons());
+ if (ps.size() == 1) {
+ // Return [array] of all moons
+
+ res = json::array();
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ json mj;
+ _moonToJson(mj,*m);
+ res.push_back(mj);
+ }
+
+ scode = 200;
+ } else {
+ // Return a single moon by ID
+
+ const uint64_t id = Utils::hexStrToU64(ps[1].c_str());
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ if (m->id() == id) {
+ _moonToJson(res,*m);
+ scode = 200;
+ break;
+ }
+ }
+
+ }
+ } else if (ps[0] == "network") {
+ ZT_VirtualNetworkList *nws = _node->networks();
+ if (nws) {
+ if (ps.size() == 1) {
+ // Return [array] of all networks
+
+ res = nlohmann::json::array();
+ for(unsigned long i=0;i<nws->networkCount;++i) {
+ OneService::NetworkSettings localSettings;
+ getNetworkSettings(nws->networks[i].nwid,localSettings);
+ nlohmann::json nj;
+ _networkToJson(nj,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings);
+ res.push_back(nj);
+ }
+
+ scode = 200;
+ } else if (ps.size() == 2) {
+ // Return a single network by ID or 404 if not found
+
+ const uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
+ for(unsigned long i=0;i<nws->networkCount;++i) {
+ if (nws->networks[i].nwid == wantnw) {
+ OneService::NetworkSettings localSettings;
+ getNetworkSettings(nws->networks[i].nwid,localSettings);
+ _networkToJson(res,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings);
+ scode = 200;
+ break;
+ }
+ }
+
+ } else scode = 404;
+ _node->freeQueryResult((void *)nws);
+ } else scode = 500;
+ } else if (ps[0] == "peer") {
+ ZT_PeerList *pl = _node->peers();
+ if (pl) {
+ if (ps.size() == 1) {
+ // Return [array] of all peers
+
+ res = nlohmann::json::array();
+ for(unsigned long i=0;i<pl->peerCount;++i) {
+ nlohmann::json pj;
+ _peerToJson(pj,&(pl->peers[i]));
+ res.push_back(pj);
+ }
+
+ scode = 200;
+ } else if (ps.size() == 2) {
+ // Return a single peer by ID or 404 if not found
+
+ uint64_t wantp = Utils::hexStrToU64(ps[1].c_str());
+ for(unsigned long i=0;i<pl->peerCount;++i) {
+ if (pl->peers[i].address == wantp) {
+ _peerToJson(res,&(pl->peers[i]));
+ scode = 200;
+ break;
+ }
+ }
+
+ } else scode = 404;
+ _node->freeQueryResult((void *)pl);
+ } else scode = 500;
+ } else {
+ if (_controller) {
+ scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
+ } else scode = 404;
+ }
+
+ } else scode = 401; // isAuth == false
+ } else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
+ if (isAuth) {
+
+ if (ps[0] == "moon") {
+ if (ps.size() == 2) {
+
+ uint64_t seed = 0;
+ try {
+ json j(OSUtils::jsonParse(body));
+ if (j.is_object()) {
+ seed = Utils::hexStrToU64(OSUtils::jsonString(j["seed"],"0").c_str());
+ }
+ } catch ( ... ) {
+ // discard invalid JSON
+ }
+
+ std::vector<World> moons(_node->moons());
+ const uint64_t id = Utils::hexStrToU64(ps[1].c_str());
+ for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
+ if (m->id() == id) {
+ _moonToJson(res,*m);
+ scode = 200;
+ break;
+ }
+ }
+
+ if ((scode != 200)&&(seed != 0)) {
+ char tmp[64];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",id);
+ res["id"] = tmp;
+ res["roots"] = json::array();
+ res["timestamp"] = 0;
+ res["signature"] = json();
+ res["updatesMustBeSignedBy"] = json();
+ res["waiting"] = true;
+ _node->orbit((void *)0,id,seed);
+ scode = 200;
+ }
+
+ } else scode = 404;
+ } else if (ps[0] == "network") {
+ if (ps.size() == 2) {
+
+ uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
+ _node->join(wantnw,(void *)0,(void *)0); // does nothing if we are a member
+ ZT_VirtualNetworkList *nws = _node->networks();
+ if (nws) {
+ for(unsigned long i=0;i<nws->networkCount;++i) {
+ if (nws->networks[i].nwid == wantnw) {
+ OneService::NetworkSettings localSettings;
+ getNetworkSettings(nws->networks[i].nwid,localSettings);
+
+ try {
+ json j(OSUtils::jsonParse(body));
+ if (j.is_object()) {
+ json &allowManaged = j["allowManaged"];
+ if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged;
+ json &allowGlobal = j["allowGlobal"];
+ if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal;
+ json &allowDefault = j["allowDefault"];
+ if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault;
+ }
+ } catch ( ... ) {
+ // discard invalid JSON
+ }
+
+ setNetworkSettings(nws->networks[i].nwid,localSettings);
+ _networkToJson(res,&(nws->networks[i]),portDeviceName(nws->networks[i].nwid),localSettings);
+
+ scode = 200;
+ break;
+ }
+ }
+ _node->freeQueryResult((void *)nws);
+ } else scode = 500;
+
+ } else scode = 404;
+ } else {
+ if (_controller)
+ scode = _controller->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
+ else scode = 404;
+ }
+
+ } else scode = 401; // isAuth == false
+ } else if (httpMethod == HTTP_DELETE) {
+ if (isAuth) {
+
+ if (ps[0] == "moon") {
+ if (ps.size() == 2) {
+ _node->deorbit((void *)0,Utils::hexStrToU64(ps[1].c_str()));
+ res["result"] = true;
+ scode = 200;
+ } // else 404
+ } else if (ps[0] == "network") {
+ ZT_VirtualNetworkList *nws = _node->networks();
+ if (nws) {
+ if (ps.size() == 2) {
+ uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
+ for(unsigned long i=0;i<nws->networkCount;++i) {
+ if (nws->networks[i].nwid == wantnw) {
+ _node->leave(wantnw,(void **)0,(void *)0);
+ res["result"] = true;
+ scode = 200;
+ break;
+ }
+ }
+ } // else 404
+ _node->freeQueryResult((void *)nws);
+ } else scode = 500;
+ } else {
+ if (_controller)
+ scode = _controller->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
+ else scode = 404;
+ }
+
+ } else scode = 401; // isAuth = false
+ } else {
+ scode = 400;
+ }
+
+ if (responseBody.length() == 0) {
+ if ((res.is_object())||(res.is_array()))
+ responseBody = OSUtils::jsonDump(res);
+ else responseBody = "{}";
+ responseContentType = "application/json";
+ }
+
+ // Wrap result in jsonp function call if the user included a jsonp= url argument.
+ // Also double-check isAuth since forbidding this without auth feels safer.
+ std::map<std::string,std::string>::const_iterator jsonp(urlArgs.find("jsonp"));
+ if ((isAuth)&&(jsonp != urlArgs.end())&&(responseContentType == "application/json")) {
+ if (responseBody.length() > 0)
+ responseBody = jsonp->second + "(" + responseBody + ");";
+ else responseBody = jsonp->second + "(null);";
+ responseContentType = "application/javascript";
+ }
+
+ return scode;
+ }
+
+ // Must be called after _localConfig is read or modified
+ void applyLocalConfig()
+ {
+ Mutex::Lock _l(_localConfig_m);
+ json lc(_localConfig);
+
+ _v4Hints.clear();
+ _v6Hints.clear();
+ _v4Blacklists.clear();
+ _v6Blacklists.clear();
+ json &virt = lc["virtual"];
+ if (virt.is_object()) {
+ for(json::iterator v(virt.begin());v!=virt.end();++v) {
+ const std::string nstr = v.key();
+ if ((nstr.length() == ZT_ADDRESS_LENGTH_HEX)&&(v.value().is_object())) {
+ const Address ztaddr(Utils::hexStrToU64(nstr.c_str()));
+ if (ztaddr) {
+ const uint64_t ztaddr2 = ztaddr.toInt();
+ std::vector<InetAddress> &v4h = _v4Hints[ztaddr2];
+ std::vector<InetAddress> &v6h = _v6Hints[ztaddr2];
+ std::vector<InetAddress> &v4b = _v4Blacklists[ztaddr2];
+ std::vector<InetAddress> &v6b = _v6Blacklists[ztaddr2];
+
+ json &tryAddrs = v.value()["try"];
+ if (tryAddrs.is_array()) {
+ for(unsigned long i=0;i<tryAddrs.size();++i) {
+ const InetAddress ip(OSUtils::jsonString(tryAddrs[i],"").c_str());
+ if (ip.ss_family == AF_INET)
+ v4h.push_back(ip);
+ else if (ip.ss_family == AF_INET6)
+ v6h.push_back(ip);
+ }
+ }
+ json &blAddrs = v.value()["blacklist"];
+ if (blAddrs.is_array()) {
+ for(unsigned long i=0;i<blAddrs.size();++i) {
+ const InetAddress ip(OSUtils::jsonString(blAddrs[i],"").c_str());
+ if (ip.ss_family == AF_INET)
+ v4b.push_back(ip);
+ else if (ip.ss_family == AF_INET6)
+ v6b.push_back(ip);
+ }
+ }
+
+ if (v4h.empty()) _v4Hints.erase(ztaddr2);
+ if (v6h.empty()) _v6Hints.erase(ztaddr2);
+ if (v4b.empty()) _v4Blacklists.erase(ztaddr2);
+ if (v6b.empty()) _v6Blacklists.erase(ztaddr2);
+ }
+ }
+ }
+ }
+
+ _globalV4Blacklist.clear();
+ _globalV6Blacklist.clear();
+ json &physical = lc["physical"];
+ if (physical.is_object()) {
+ for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
+ const InetAddress net(OSUtils::jsonString(phy.key(),"").c_str());
+ if ((net)&&(net.netmaskBits() > 0)) {
+ if (phy.value().is_object()) {
+ if (OSUtils::jsonBool(phy.value()["blacklist"],false)) {
+ if (net.ss_family == AF_INET)
+ _globalV4Blacklist.push_back(net);
+ else if (net.ss_family == AF_INET6)
+ _globalV6Blacklist.push_back(net);
+ }
+ }
+ }
+ }
+ }
+
+ _allowManagementFrom.clear();
+ _interfacePrefixBlacklist.clear();
+
+ json &settings = lc["settings"];
+
+ _primaryPort = (unsigned int)OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
+ _allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true);
+ _portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true);
+
+#ifndef ZT_SDK
+ const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT));
+ const bool udist = OSUtils::jsonBool(settings["softwareUpdateDist"],false);
+ if (((up == "apply")||(up == "download"))||(udist)) {
+ if (!_updater)
+ _updater = new SoftwareUpdater(*_node,_homePath);
+ _updateAutoApply = (up == "apply");
+ _updater->setUpdateDistribution(udist);
+ _updater->setChannel(OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL));
+ } else {
+ delete _updater;
+ _updater = (SoftwareUpdater *)0;
+ _updateAutoApply = false;
+ }
+#endif
+
+ json &ignoreIfs = settings["interfacePrefixBlacklist"];
+ if (ignoreIfs.is_array()) {
+ for(unsigned long i=0;i<ignoreIfs.size();++i) {
+ const std::string tmp(OSUtils::jsonString(ignoreIfs[i],""));
+ if (tmp.length() > 0)
+ _interfacePrefixBlacklist.push_back(tmp);
+ }
+ }
+
+ json &amf = settings["allowManagementFrom"];
+ if (amf.is_array()) {
+ for(unsigned long i=0;i<amf.size();++i) {
+ const InetAddress nw(OSUtils::jsonString(amf[i],"").c_str());
+ if (nw)
+ _allowManagementFrom.push_back(nw);
+ }
+ }
+ }
// Checks if a managed IP or route target is allowed
bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
{
if (!n.settings.allowManaged)
return false;
+
+ if (n.settings.allowManagedWhitelist.size() > 0) {
+ bool allowed = false;
+ for (InetAddress addr : n.settings.allowManagedWhitelist) {
+ if (addr.containsAddress(target) && addr.netmaskBits() <= target.netmaskBits()) {
+ allowed = true;
+ break;
+ }
+ }
+ if (!allowed) return false;
+ }
+
if (target.isDefaultRoute())
return n.settings.allowDefault;
switch(target.ipScope()) {
@@ -1109,6 +1561,8 @@ public:
// Apply or update managed IPs for a configured network (be sure n.tap exists)
void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes)
{
+ char ipbuf[64];
+
// assumes _nets_m is locked
if (syncIps) {
std::vector<InetAddress> newManagedIps;
@@ -1124,23 +1578,27 @@ public:
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
if (!n.tap->removeIp(*ip))
- fprintf(stderr,"ERROR: unable to remove ip address %s"ZT_EOL_S, ip->toString().c_str());
+ fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf));
}
}
+#ifdef __SYNOLOGY__
+ if (!n.tap->addIpSyn(newManagedIps))
+ fprintf(stderr,"ERROR: unable to add ip addresses to ifcfg" ZT_EOL_S);
+#else
for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
if (!n.tap->addIp(*ip))
- fprintf(stderr,"ERROR: unable to add ip address %s"ZT_EOL_S, ip->toString().c_str());
- }
+ fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
+ }
}
-
+#endif
n.managedIps.swap(newManagedIps);
}
if (syncRoutes) {
char tapdev[64];
-#ifdef __WINDOWS__
- Utils::snprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
+ OSUtils::ztsnprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
#else
Utils::scopy(tapdev,sizeof(tapdev),n.tap->deviceName().c_str());
#endif
@@ -1148,13 +1606,13 @@ public:
std::vector<InetAddress> myIps(n.tap->ips());
// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
- for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
+ for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
bool haveRoute = false;
- if ( (checkIfManagedIsAllowed(n,mr->target())) && ((mr->via().ss_family != mr->target().ss_family)||(!matchIpOnly(myIps,mr->via()))) ) {
+ if ( (checkIfManagedIsAllowed(n,(*mr)->target())) && (((*mr)->via().ss_family != (*mr)->target().ss_family)||(!matchIpOnly(myIps,(*mr)->via()))) ) {
for(unsigned int i=0;i<n.config.routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
- if ( (mr->target() == *target) && ( ((via->ss_family == target->ss_family)&&(mr->via() == *via)) || (tapdev == mr->device()) ) ) {
+ if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
haveRoute = true;
break;
}
@@ -1178,20 +1636,23 @@ public:
bool haveRoute = false;
// Ignore routes implied by local managed IPs since adding the IP adds the route
+ // Commented out to fix ticket #600 (disappearing routes on macOS). Remove this block when we're sure there's no side effects
+ /*
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) {
haveRoute = true;
break;
}
}
+ */
if (haveRoute)
continue;
// If we've already applied this route, just sync it and continue
- for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
- if ( (mr->target() == *target) && ( ((via->ss_family == target->ss_family)&&(mr->via() == *via)) || (tapdev == mr->device()) ) ) {
+ for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
+ if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) {
haveRoute = true;
- mr->sync();
+ (*mr)->sync();
break;
}
}
@@ -1199,41 +1660,32 @@ public:
continue;
// Add and apply new routes
- n.managedRoutes.push_back(ManagedRoute());
- if (!n.managedRoutes.back().set(*target,*via,tapdev))
+ n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev)));
+ if (!n.managedRoutes.back()->sync())
n.managedRoutes.pop_back();
}
}
}
+ // =========================================================================
+ // Handlers for Node and Phy<> callbacks
+ // =========================================================================
+
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{
-#ifdef ZT_ENABLE_CLUSTER
- if (sock == _clusterMessageSocket) {
- _lastDirectReceiveFromGlobal = OSUtils::now();
- _node->clusterHandleIncomingMessage(data,len);
- return;
- }
-#endif
-
-#ifdef ZT_BREAK_UDP
- if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
- return;
-#endif
-
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
_lastDirectReceiveFromGlobal = OSUtils::now();
-
const ZT_ResultCode rc = _node->processWirePacket(
+ (void *)0,
OSUtils::now(),
- reinterpret_cast<const struct sockaddr_storage *>(localAddr),
- (const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
+ reinterpret_cast<int64_t>(sock),
+ reinterpret_cast<const struct sockaddr_storage *>(from), // Phy<> uses sockaddr_storage, so it'll always be that big
data,
len,
&_nextBackgroundTaskDeadline);
if (ZT_ResultCode_isFatal(rc)) {
char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
_fatalErrorMessage = tmp;
@@ -1243,191 +1695,227 @@ public:
inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
{
- if (!success)
+ if (!success) {
+ phyOnTcpClose(sock,uptr);
return;
+ }
- // Outgoing TCP connections are always TCP fallback tunnel connections.
-
- TcpConnection *tc = new TcpConnection();
- _tcpConnections.insert(tc);
-
- tc->type = TcpConnection::TCP_TUNNEL_OUTGOING;
- tc->shouldKeepAlive = true;
- tc->parent = this;
+ TcpConnection *const tc = reinterpret_cast<TcpConnection *>(*uptr);
+ if (!tc) { // sanity check
+ _phy.close(sock,true);
+ return;
+ }
tc->sock = sock;
- // from and parser are not used
- tc->messageSize = 0; // unused
- tc->lastActivity = OSUtils::now();
- // HTTP stuff is not used
- tc->writeBuf = "";
- *uptr = (void *)tc;
-
- // Send "hello" message
- tc->writeBuf.push_back((char)0x17);
- tc->writeBuf.push_back((char)0x03);
- tc->writeBuf.push_back((char)0x03); // fake TLS 1.2 header
- tc->writeBuf.push_back((char)0x00);
- tc->writeBuf.push_back((char)0x04); // mlen == 4
- tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MAJOR);
- tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MINOR);
- tc->writeBuf.push_back((char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff));
- tc->writeBuf.push_back((char)(ZEROTIER_ONE_VERSION_REVISION & 0xff));
- _phy.setNotifyWritable(sock,true);
-
- _tcpFallbackTunnel = tc;
+
+ if (tc->type == TcpConnection::TCP_TUNNEL_OUTGOING) {
+ if (_tcpFallbackTunnel)
+ _phy.close(_tcpFallbackTunnel->sock);
+ _tcpFallbackTunnel = tc;
+ _phy.streamSend(sock,ZT_TCP_TUNNEL_HELLO,sizeof(ZT_TCP_TUNNEL_HELLO));
+ } else {
+ _phy.close(sock,true);
+ }
}
inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
{
- if ((!from)||(reinterpret_cast<const InetAddress *>(from)->ipScope() != InetAddress::IP_SCOPE_LOOPBACK)) {
- // Non-Loopback: deny (for now)
+ if (!from) {
_phy.close(sockN,false);
return;
} else {
- // Loopback == HTTP JSON API request
TcpConnection *tc = new TcpConnection();
- _tcpConnections.insert(tc);
- tc->type = TcpConnection::TCP_HTTP_INCOMING;
- tc->shouldKeepAlive = true;
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+
+ tc->type = TcpConnection::TCP_UNCATEGORIZED_INCOMING;
tc->parent = this;
tc->sock = sockN;
- tc->from = from;
+ tc->remoteAddr = from;
+ tc->lastReceive = OSUtils::now();
http_parser_init(&(tc->parser),HTTP_REQUEST);
tc->parser.data = (void *)tc;
tc->messageSize = 0;
- tc->lastActivity = OSUtils::now();
- tc->currentHeaderField = "";
- tc->currentHeaderValue = "";
- tc->url = "";
- tc->status = "";
- tc->headers.clear();
- tc->body = "";
- tc->writeBuf = "";
+
*uptrN = (void *)tc;
}
}
- inline void phyOnTcpClose(PhySocket *sock,void **uptr)
+ void phyOnTcpClose(PhySocket *sock,void **uptr)
{
TcpConnection *tc = (TcpConnection *)*uptr;
if (tc) {
- if (tc == _tcpFallbackTunnel)
+ if (tc == _tcpFallbackTunnel) {
_tcpFallbackTunnel = (TcpConnection *)0;
- _tcpConnections.erase(tc);
+ }
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.erase(std::remove(_tcpConnections.begin(),_tcpConnections.end(),tc),_tcpConnections.end());
+ }
delete tc;
}
}
- inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+ void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
{
- TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
- switch(tc->type) {
+ try {
+ if (!len) return; // sanity check, should never happen
+ TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
+ tc->lastReceive = OSUtils::now();
+ switch(tc->type) {
+
+ case TcpConnection::TCP_UNCATEGORIZED_INCOMING:
+ switch(reinterpret_cast<uint8_t *>(data)[0]) {
+ // HTTP: GET, PUT, POST, HEAD, DELETE
+ case 'G':
+ case 'P':
+ case 'D':
+ case 'H': {
+ // This is only allowed from IPs permitted to access the management
+ // backplane, which is just 127.0.0.1/::1 unless otherwise configured.
+ bool allow;
+ {
+ Mutex::Lock _l(_localConfig_m);
+ if (_allowManagementFrom.size() == 0) {
+ allow = (tc->remoteAddr.ipScope() == InetAddress::IP_SCOPE_LOOPBACK);
+ } else {
+ allow = false;
+ for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) {
+ if (i->containsAddress(tc->remoteAddr)) {
+ allow = true;
+ break;
+ }
+ }
+ }
+ }
+ if (allow) {
+ tc->type = TcpConnection::TCP_HTTP_INCOMING;
+ phyOnTcpData(sock,uptr,data,len);
+ } else {
+ _phy.close(sock);
+ }
+ } break;
- case TcpConnection::TCP_HTTP_INCOMING:
- case TcpConnection::TCP_HTTP_OUTGOING:
- http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len);
- if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK)) {
- _phy.close(sock);
+ // Drop unknown protocols
+ default:
+ _phy.close(sock);
+ break;
+ }
+ return;
+
+ case TcpConnection::TCP_HTTP_INCOMING:
+ case TcpConnection::TCP_HTTP_OUTGOING:
+ http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len);
+ if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK))
+ _phy.close(sock);
return;
- }
- break;
- case TcpConnection::TCP_TUNNEL_OUTGOING:
- tc->body.append((const char *)data,len);
- while (tc->body.length() >= 5) {
- const char *data = tc->body.data();
- const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) );
- if (tc->body.length() >= (mlen + 5)) {
- InetAddress from;
+ case TcpConnection::TCP_TUNNEL_OUTGOING:
+ tc->readq.append((const char *)data,len);
+ while (tc->readq.length() >= 5) {
+ const char *data = tc->readq.data();
+ const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) );
+ if (tc->readq.length() >= (mlen + 5)) {
+ InetAddress from;
unsigned long plen = mlen; // payload length, modified if there's an IP header
- data += 5; // skip forward past pseudo-TLS junk and mlen
- if (plen == 4) {
- // Hello message, which isn't sent by proxy and would be ignored by client
- } else if (plen) {
- // Messages should contain IPv4 or IPv6 source IP address data
- switch(data[0]) {
- case 4: // IPv4
- if (plen >= 7) {
- from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff));
- data += 7; // type + 4 byte IP + 2 byte port
- plen -= 7;
- } else {
+ data += 5; // skip forward past pseudo-TLS junk and mlen
+ if (plen == 4) {
+ // Hello message, which isn't sent by proxy and would be ignored by client
+ } else if (plen) {
+ // Messages should contain IPv4 or IPv6 source IP address data
+ switch(data[0]) {
+ case 4: // IPv4
+ if (plen >= 7) {
+ from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff));
+ data += 7; // type + 4 byte IP + 2 byte port
+ plen -= 7;
+ } else {
+ _phy.close(sock);
+ return;
+ }
+ break;
+ case 6: // IPv6
+ if (plen >= 19) {
+ from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff));
+ data += 19; // type + 16 byte IP + 2 byte port
+ plen -= 19;
+ } else {
+ _phy.close(sock);
+ return;
+ }
+ break;
+ case 0: // none/omitted
+ ++data;
+ --plen;
+ break;
+ default: // invalid address type
_phy.close(sock);
return;
- }
- break;
- case 6: // IPv6
- if (plen >= 19) {
- from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff));
- data += 19; // type + 16 byte IP + 2 byte port
- plen -= 19;
- } else {
+ }
+
+ if (from) {
+ InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
+ const ZT_ResultCode rc = _node->processWirePacket(
+ (void *)0,
+ OSUtils::now(),
+ -1,
+ reinterpret_cast<struct sockaddr_storage *>(&from),
+ data,
+ plen,
+ &_nextBackgroundTaskDeadline);
+ if (ZT_ResultCode_isFatal(rc)) {
+ char tmp[256];
+ OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = tmp;
+ this->terminate();
_phy.close(sock);
return;
}
- break;
- case 0: // none/omitted
- ++data;
- --plen;
- break;
- default: // invalid address type
- _phy.close(sock);
- return;
- }
-
- if (from) {
- InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
- const ZT_ResultCode rc = _node->processWirePacket(
- OSUtils::now(),
- reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
- reinterpret_cast<struct sockaddr_storage *>(&from),
- data,
- plen,
- &_nextBackgroundTaskDeadline);
- if (ZT_ResultCode_isFatal(rc)) {
- char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = tmp;
- this->terminate();
- _phy.close(sock);
- return;
}
}
- }
- if (tc->body.length() > (mlen + 5))
- tc->body = tc->body.substr(mlen + 5);
- else tc->body = "";
- } else break;
- }
- break;
+ if (tc->readq.length() > (mlen + 5))
+ tc->readq.erase(tc->readq.begin(),tc->readq.begin() + (mlen + 5));
+ else tc->readq.clear();
+ } else break;
+ }
+ return;
+ }
+ } catch ( ... ) {
+ _phy.close(sock);
}
}
inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
- Mutex::Lock _l(tc->writeBuf_m);
- if (tc->writeBuf.length() > 0) {
- long sent = (long)_phy.streamSend(sock,tc->writeBuf.data(),(unsigned long)tc->writeBuf.length(),true);
- if (sent > 0) {
- tc->lastActivity = OSUtils::now();
- if ((unsigned long)sent >= (unsigned long)tc->writeBuf.length()) {
- tc->writeBuf = "";
- _phy.setNotifyWritable(sock,false);
- if (!tc->shouldKeepAlive)
- _phy.close(sock); // will call close handler to delete from _tcpConnections
- } else {
- tc->writeBuf = tc->writeBuf.substr(sent);
+ bool closeit = false;
+ {
+ Mutex::Lock _l(tc->writeq_m);
+ if (tc->writeq.length() > 0) {
+ long sent = (long)_phy.streamSend(sock,tc->writeq.data(),(unsigned long)tc->writeq.length(),true);
+ if (sent > 0) {
+ if ((unsigned long)sent >= (unsigned long)tc->writeq.length()) {
+ tc->writeq.clear();
+ _phy.setNotifyWritable(sock,false);
+
+ if (tc->type == TcpConnection::TCP_HTTP_INCOMING)
+ closeit = true; // HTTP keep alive not supported
+ } else {
+ tc->writeq.erase(tc->writeq.begin(),tc->writeq.begin() + sent);
+ }
}
+ } else {
+ _phy.setNotifyWritable(sock,false);
}
- } else {
- _phy.setNotifyWritable(sock,false);
}
+ if (closeit)
+ _phy.close(sock);
}
inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {}
@@ -1447,7 +1935,8 @@ public:
if (!n.tap) {
try {
char friendlyName[128];
- Utils::snprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
+ OSUtils::ztsnprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
+
n.tap = new EthernetTap(
_homePath.c_str(),
MAC(nwc->mac),
@@ -1460,24 +1949,47 @@ public:
*nuptr = (void *)&n;
char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
std::string nlcbuf;
if (OSUtils::readFile(nlcpath,nlcbuf)) {
Dictionary<4096> nc;
nc.load(nlcbuf.c_str());
- n.settings.allowManaged = nc.getB("allowManaged",true);
- n.settings.allowGlobal = nc.getB("allowGlobal",false);
- n.settings.allowDefault = nc.getB("allowDefault",false);
+ Buffer<1024> allowManaged;
+ if (nc.get("allowManaged", allowManaged) && allowManaged.size() != 0) {
+ std::string addresses (allowManaged.begin(), allowManaged.size());
+ if (allowManaged.size() <= 5) { // untidy parsing for backward compatibility
+ if (allowManaged[0] == '1' || allowManaged[0] == 't' || allowManaged[0] == 'T') {
+ n.settings.allowManaged = true;
+ } else {
+ n.settings.allowManaged = false;
+ }
+ } else {
+ // this should be a list of IP addresses
+ n.settings.allowManaged = true;
+ size_t pos = 0;
+ while (true) {
+ size_t nextPos = addresses.find(',', pos);
+ std::string address = addresses.substr(pos, (nextPos == std::string::npos ? addresses.size() : nextPos) - pos);
+ n.settings.allowManagedWhitelist.push_back(InetAddress(address.c_str()));
+ if (nextPos == std::string::npos) break;
+ pos = nextPos + 1;
+ }
+ }
+ } else {
+ n.settings.allowManaged = true;
+ }
+ n.settings.allowGlobal = nc.getB("allowGlobal", false);
+ n.settings.allowDefault = nc.getB("allowDefault", false);
}
} catch (std::exception &exc) {
#ifdef __WINDOWS__
FILE *tapFailLog = fopen((_homePath + ZT_PATH_SEPARATOR_S"port_error_log.txt").c_str(),"a");
if (tapFailLog) {
- fprintf(tapFailLog,"%.16llx: %s"ZT_EOL_S,(unsigned long long)nwid,exc.what());
+ fprintf(tapFailLog,"%.16llx: %s" ZT_EOL_S,(unsigned long long)nwid,exc.what());
fclose(tapFailLog);
}
#else
- fprintf(stderr,"ERROR: unable to configure virtual network port: %s"ZT_EOL_S,exc.what());
+ fprintf(stderr,"ERROR: unable to configure virtual network port: %s" ZT_EOL_S,exc.what());
#endif
_nets.erase(nwid);
return -999;
@@ -1488,9 +2000,20 @@ public:
// After setting up tap, fall through to CONFIG_UPDATE since we also want to do this...
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
- memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
+ ZT_FAST_MEMCPY(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
if (n.tap) { // sanity check
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
+ // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized
+ //
+ // without WindowsEthernetTap::isInitialized() returning true, the won't actually
+ // be online yet and setting managed routes on it will fail.
+ const int MAX_SLEEP_COUNT = 500;
+ for (int i = 0; !n.tap->isInitialized() && i < MAX_SLEEP_COUNT; i++) {
+ Sleep(10);
+ }
+#endif
syncManagedStuff(n,true,true);
+ n.tap->setMtu(nwc->mtu);
} else {
_nets.erase(nwid);
return -999; // tap init failed
@@ -1500,19 +2023,19 @@ public:
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
if (n.tap) { // sanity check
-#ifdef __WINDOWS__
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
std::string winInstanceId(n.tap->instanceId());
#endif
*nuptr = (void *)0;
delete n.tap;
_nets.erase(nwid);
-#ifdef __WINDOWS__
+#if defined(__WINDOWS__) && !defined(ZT_SDK)
if ((op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0))
WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str());
#endif
if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
OSUtils::rm(nlcpath);
}
} else {
@@ -1536,145 +2059,204 @@ public:
case ZT_EVENT_TRACE: {
if (metaData) {
- ::fprintf(stderr,"%s"ZT_EOL_S,(const char *)metaData);
+ ::fprintf(stderr,"%s" ZT_EOL_S,(const char *)metaData);
::fflush(stderr);
}
} break;
+ case ZT_EVENT_USER_MESSAGE: {
+ const ZT_UserMessage *um = reinterpret_cast<const ZT_UserMessage *>(metaData);
+ if ((um->typeId == ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE)&&(_updater)) {
+ _updater->handleSoftwareUpdateUserMessage(um->origin,um->data,um->length);
+ }
+ } break;
+
+ case ZT_EVENT_REMOTE_TRACE: {
+ const ZT_RemoteTrace *rt = reinterpret_cast<const ZT_RemoteTrace *>(metaData);
+ if ((rt)&&(rt->len > 0)&&(rt->len <= ZT_MAX_REMOTE_TRACE_SIZE)&&(rt->data))
+ _controller->handleRemoteTrace(*rt);
+ }
+
default:
break;
}
}
- inline long nodeDataStoreGetFunction(const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
+ inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
{
- std::string p(_dataStorePrepPath(name));
- if (!p.length())
- return -2;
-
- FILE *f = fopen(p.c_str(),"rb");
- if (!f)
- return -1;
- if (fseek(f,0,SEEK_END) != 0) {
- fclose(f);
- return -2;
- }
- long ts = ftell(f);
- if (ts < 0) {
- fclose(f);
- return -2;
+ char p[1024];
+ FILE *f;
+ bool secure = false;
+ char dirname[1024];
+ dirname[0] = 0;
+
+ switch(type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "moons.d",_homePath.c_str());
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.moon",dirname,(unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str());
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]);
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "peers.d",_homePath.c_str());
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",dirname,(unsigned long long)id[0]);
+ break;
+ default:
+ return;
}
- *totalSize = (unsigned long)ts;
- if (fseek(f,(long)readIndex,SEEK_SET) != 0) {
- fclose(f);
- return -2;
+
+ if (len >= 0) {
+ // Check to see if we've already written this first. This reduces
+ // redundant writes and I/O overhead on most platforms and has
+ // little effect on others.
+ f = fopen(p,"rb");
+ if (f) {
+ char buf[65535];
+ long l = (long)fread(buf,1,sizeof(buf),f);
+ fclose(f);
+ if ((l == (long)len)&&(memcmp(data,buf,l) == 0))
+ return;
+ }
+
+ f = fopen(p,"wb");
+ if ((!f)&&(dirname[0])) { // create subdirectory if it does not exist
+ OSUtils::mkdir(dirname);
+ f = fopen(p,"wb");
+ }
+ if (f) {
+ if (fwrite(data,len,1,f) != 1)
+ fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p);
+ fclose(f);
+ if (secure)
+ OSUtils::lockDownFile(p,false);
+ } else {
+ fprintf(stderr,"WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,p);
+ }
+ } else {
+ OSUtils::rm(p);
}
- long n = (long)fread(buf,1,bufSize,f);
- fclose(f);
- return n;
}
- inline int nodeDataStorePutFunction(const char *name,const void *data,unsigned long len,int secure)
+ inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
{
- std::string p(_dataStorePrepPath(name));
- if (!p.length())
- return -2;
-
- if (!data) {
- OSUtils::rm(p.c_str());
- return 0;
+ char p[4096];
+ switch(type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d" ZT_PATH_SEPARATOR_S "%.16llx.moon",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",_homePath.c_str(),(unsigned long long)id[0]);
+ break;
+ default:
+ return -1;
}
-
- FILE *f = fopen(p.c_str(),"wb");
- if (!f)
- return -1;
- if (fwrite(data,len,1,f) == 1) {
- fclose(f);
- if (secure)
- OSUtils::lockDownFile(p.c_str(),false);
- return 0;
- } else {
+ FILE *f = fopen(p,"rb");
+ if (f) {
+ int n = (int)fread(data,1,maxlen,f);
fclose(f);
- OSUtils::rm(p.c_str());
- return -1;
+ if (n >= 0)
+ return n;
}
+ return -1;
}
- inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+ inline int nodeWirePacketSendFunction(const int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{
- unsigned int fromBindingNo = 0;
-
- if (addr->ss_family == AF_INET) {
- if (reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port == 0) {
- // If sender is sending from wildcard (null address), choose the secondary backup
- // port 1/4 of the time. (but only for IPv4)
- fromBindingNo = (++_udpPortPickerCounter & 0x4) >> 2;
- if (!_ports[fromBindingNo])
- fromBindingNo = 0;
- } else {
- const uint16_t lp = reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port;
- if (lp == _portsBE[1])
- fromBindingNo = 1;
- else if (lp == _portsBE[2])
- fromBindingNo = 2;
- }
-
#ifdef ZT_TCP_FALLBACK_RELAY
- // TCP fallback tunnel support, currently IPv4 only
- if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
- // Engage TCP tunnel fallback if we haven't received anything valid from a global
- // IP address in ZT_TCP_FALLBACK_AFTER milliseconds. If we do start getting
- // valid direct traffic we'll stop using it and close the socket after a while.
- const uint64_t now = OSUtils::now();
- if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) {
- if (_tcpFallbackTunnel) {
- Mutex::Lock _l(_tcpFallbackTunnel->writeBuf_m);
- if (!_tcpFallbackTunnel->writeBuf.length())
- _phy.setNotifyWritable(_tcpFallbackTunnel->sock,true);
- unsigned long mlen = len + 7;
- _tcpFallbackTunnel->writeBuf.push_back((char)0x17);
- _tcpFallbackTunnel->writeBuf.push_back((char)0x03);
- _tcpFallbackTunnel->writeBuf.push_back((char)0x03); // fake TLS 1.2 header
- _tcpFallbackTunnel->writeBuf.push_back((char)((mlen >> 8) & 0xff));
- _tcpFallbackTunnel->writeBuf.push_back((char)(mlen & 0xff));
- _tcpFallbackTunnel->writeBuf.push_back((char)4); // IPv4
- _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
- _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
- _tcpFallbackTunnel->writeBuf.append((const char *)data,len);
- } else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
- std::vector<InetAddress> tunnelIps(_tcpFallbackResolver.get());
- if (tunnelIps.empty()) {
- if (!_tcpFallbackResolver.running())
- _tcpFallbackResolver.resolveNow();
- } else {
+ if(_allowTcpFallbackRelay) {
+ if (addr->ss_family == AF_INET) {
+ // TCP fallback tunnel support, currently IPv4 only
+ if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
+ // Engage TCP tunnel fallback if we haven't received anything valid from a global
+ // IP address in ZT_TCP_FALLBACK_AFTER milliseconds. If we do start getting
+ // valid direct traffic we'll stop using it and close the socket after a while.
+ const int64_t now = OSUtils::now();
+ if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) {
+ if (_tcpFallbackTunnel) {
+ bool flushNow = false;
+ {
+ Mutex::Lock _l(_tcpFallbackTunnel->writeq_m);
+ if (_tcpFallbackTunnel->writeq.size() < (1024 * 64)) {
+ if (_tcpFallbackTunnel->writeq.length() == 0) {
+ _phy.setNotifyWritable(_tcpFallbackTunnel->sock,true);
+ flushNow = true;
+ }
+ const unsigned long mlen = len + 7;
+ _tcpFallbackTunnel->writeq.push_back((char)0x17);
+ _tcpFallbackTunnel->writeq.push_back((char)0x03);
+ _tcpFallbackTunnel->writeq.push_back((char)0x03); // fake TLS 1.2 header
+ _tcpFallbackTunnel->writeq.push_back((char)((mlen >> 8) & 0xff));
+ _tcpFallbackTunnel->writeq.push_back((char)(mlen & 0xff));
+ _tcpFallbackTunnel->writeq.push_back((char)4); // IPv4
+ _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
+ _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
+ _tcpFallbackTunnel->writeq.append((const char *)data,len);
+ }
+ }
+ if (flushNow) {
+ void *tmpptr = (void *)_tcpFallbackTunnel;
+ phyOnTcpWritable(_tcpFallbackTunnel->sock,&tmpptr);
+ }
+ } else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
+ const InetAddress addr(ZT_TCP_FALLBACK_RELAY);
+ TcpConnection *tc = new TcpConnection();
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+ tc->type = TcpConnection::TCP_TUNNEL_OUTGOING;
+ tc->remoteAddr = addr;
+ tc->lastReceive = OSUtils::now();
+ tc->parent = this;
+ tc->sock = (PhySocket *)0; // set in connect handler
+ tc->messageSize = 0;
bool connected = false;
- InetAddress addr(tunnelIps[(unsigned long)now % tunnelIps.size()]);
- addr.setPort(ZT_TCP_FALLBACK_RELAY_PORT);
- _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected);
+ _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected,(void *)tc,true);
}
}
+ _lastSendToGlobalV4 = now;
}
- _lastSendToGlobalV4 = now;
}
-#endif // ZT_TCP_FALLBACK_RELAY
- } else if (addr->ss_family == AF_INET6) {
- if (reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port != 0) {
- const uint16_t lp = reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port;
- if (lp == _portsBE[1])
- fromBindingNo = 1;
- else if (lp == _portsBE[2])
- fromBindingNo = 2;
- }
- } else {
- return -1;
}
+#endif // ZT_TCP_FALLBACK_RELAY
-#ifdef ZT_BREAK_UDP
- if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
- return 0; // silently break UDP
-#endif
+ // Even when relaying we still send via UDP. This way if UDP starts
+ // working we can instantly "fail forward" to it and stop using TCP
+ // proxy fallback, which is slow.
- return (_bindings[fromBindingNo].udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
+ if ((localSocket != -1)&&(localSocket != 0)&&(_binder.isUdpSocketValid((PhySocket *)((uintptr_t)localSocket)))) {
+ if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),ttl);
+ const bool r = _phy.udpSend((PhySocket *)((uintptr_t)localSocket),(const struct sockaddr *)addr,data,len);
+ if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),255);
+ return ((r) ? 0 : -1);
+ } else {
+ return ((_binder.udpSendAll(_phy,addr,data,len,ttl)) ? 0 : -1);
+ }
}
inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
@@ -1685,16 +2267,18 @@ public:
n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
}
- inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
+ inline int nodePathCheckFunction(uint64_t ztaddr,const int64_t localSocket,const struct sockaddr_storage *remoteAddr)
{
- Mutex::Lock _l(_nets_m);
-
- for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
- if (n->second.tap) {
- std::vector<InetAddress> ips(n->second.tap->ips());
- for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
- if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
- return 0;
+ // Make sure we're not trying to do ZeroTier-over-ZeroTier
+ {
+ Mutex::Lock _l(_nets_m);
+ for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
+ if (n->second.tap) {
+ std::vector<InetAddress> ips(n->second.tap->ips());
+ for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
+ if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
+ return 0;
+ }
}
}
}
@@ -1704,28 +2288,76 @@ public:
* because of the "route forking" and interface binding that we do. This
* ensures (we hope) that ZeroTier traffic will still take the physical
* path even if its managed routes override this for other traffic. Will
- * revisit if we see problems with this. */
-
+ * revisit if we see recursion problems. */
+
+ // Check blacklists
+ const Hashtable< uint64_t,std::vector<InetAddress> > *blh = (const Hashtable< uint64_t,std::vector<InetAddress> > *)0;
+ const std::vector<InetAddress> *gbl = (const std::vector<InetAddress> *)0;
+ if (remoteAddr->ss_family == AF_INET) {
+ blh = &_v4Blacklists;
+ gbl = &_globalV4Blacklist;
+ } else if (remoteAddr->ss_family == AF_INET6) {
+ blh = &_v6Blacklists;
+ gbl = &_globalV6Blacklist;
+ }
+ if (blh) {
+ Mutex::Lock _l(_localConfig_m);
+ const std::vector<InetAddress> *l = blh->get(ztaddr);
+ if (l) {
+ for(std::vector<InetAddress>::const_iterator a(l->begin());a!=l->end();++a) {
+ if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr)))
+ return 0;
+ }
+ }
+ }
+ if (gbl) {
+ for(std::vector<InetAddress>::const_iterator a(gbl->begin());a!=gbl->end();++a) {
+ if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr)))
+ return 0;
+ }
+ }
return 1;
}
+ inline int nodePathLookupFunction(uint64_t ztaddr,int family,struct sockaddr_storage *result)
+ {
+ const Hashtable< uint64_t,std::vector<InetAddress> > *lh = (const Hashtable< uint64_t,std::vector<InetAddress> > *)0;
+ if (family < 0)
+ lh = (_node->prng() & 1) ? &_v4Hints : &_v6Hints;
+ else if (family == AF_INET)
+ lh = &_v4Hints;
+ else if (family == AF_INET6)
+ lh = &_v6Hints;
+ else return 0;
+ const std::vector<InetAddress> *l = lh->get(ztaddr);
+ if ((l)&&(l->size() > 0)) {
+ ZT_FAST_MEMCPY(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage));
+ return 1;
+ } else return 0;
+ }
+
inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
- _node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline);
+ _node->processVirtualNetworkFrame((void *)0,OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline);
}
inline void onHttpRequestToServer(TcpConnection *tc)
{
- char tmpn[256];
+ char tmpn[4096];
std::string data;
std::string contentType("text/plain"); // default if not changed in handleRequest()
unsigned int scode = 404;
+ // Note that we check allowed IP ranges when HTTP connections are first detected in
+ // phyOnTcpData(). If we made it here the source IP is okay.
+
try {
- if (_controlPlane)
- scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType);
- else scode = 500;
+ scode = handleControlPlaneHttpRequest(tc->remoteAddr,tc->parser.method,tc->url,tc->headers,tc->readq,data,contentType);
+ } catch (std::exception &exc) {
+ fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
+ scode = 500;
} catch ( ... ) {
+ fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exception" ZT_EOL_S);
scode = 500;
}
@@ -1742,19 +2374,16 @@ public:
default: scodestr = "Error"; break;
}
- Utils::snprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n",scode,scodestr);
+ OSUtils::ztsnprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: %s\r\nContent-Length: %lu\r\nConnection: close\r\n\r\n",
+ scode,
+ scodestr,
+ contentType.c_str(),
+ (unsigned long)data.length());
{
- Mutex::Lock _l(tc->writeBuf_m);
- tc->writeBuf.assign(tmpn);
- tc->writeBuf.append("Content-Type: ");
- tc->writeBuf.append(contentType);
- Utils::snprintf(tmpn,sizeof(tmpn),"\r\nContent-Length: %lu\r\n",(unsigned long)data.length());
- tc->writeBuf.append(tmpn);
- if (!tc->shouldKeepAlive)
- tc->writeBuf.append("Connection: close\r\n");
- tc->writeBuf.append("\r\n");
+ Mutex::Lock _l(tc->writeq_m);
+ tc->writeq = tmpn;
if (tc->parser.method != HTTP_HEAD)
- tc->writeBuf.append(data);
+ tc->writeq.append(data);
}
_phy.setNotifyWritable(tc->sock,true);
@@ -1762,46 +2391,65 @@ public:
inline void onHttpResponseFromClient(TcpConnection *tc)
{
- if (!tc->shouldKeepAlive)
- _phy.close(tc->sock); // will call close handler, which deletes from _tcpConnections
+ _phy.close(tc->sock);
}
bool shouldBindInterface(const char *ifname,const InetAddress &ifaddr)
{
- if (isBlacklistedLocalInterfaceForZeroTierTraffic(ifname))
- return false;
+#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+ if ((ifname[0] == 'l')&&(ifname[1] == 'o')) return false; // loopback
+ if ((ifname[0] == 'z')&&(ifname[1] == 't')) return false; // sanity check: zt#
+ if ((ifname[0] == 't')&&(ifname[1] == 'u')&&(ifname[2] == 'n')) return false; // tun# is probably an OpenVPN tunnel or similar
+ if ((ifname[0] == 't')&&(ifname[1] == 'a')&&(ifname[2] == 'p')) return false; // tap# is probably an OpenVPN tunnel or similar
+#endif
- Mutex::Lock _l(_nets_m);
- for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
- if (n->second.tap) {
- std::vector<InetAddress> ips(n->second.tap->ips());
- for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
- if (i->ipsEqual(ifaddr))
+#ifdef __APPLE__
+ if ((ifname[0] == 'l')&&(ifname[1] == 'o')) return false; // loopback
+ if ((ifname[0] == 'z')&&(ifname[1] == 't')) return false; // sanity check: zt#
+ if ((ifname[0] == 't')&&(ifname[1] == 'u')&&(ifname[2] == 'n')) return false; // tun# is probably an OpenVPN tunnel or similar
+ if ((ifname[0] == 't')&&(ifname[1] == 'a')&&(ifname[2] == 'p')) return false; // tap# is probably an OpenVPN tunnel or similar
+ if ((ifname[0] == 'u')&&(ifname[1] == 't')&&(ifname[2] == 'u')&&(ifname[3] == 'n')) return false; // ... as is utun#
+#endif
+
+ {
+ Mutex::Lock _l(_localConfig_m);
+ for(std::vector<std::string>::const_iterator p(_interfacePrefixBlacklist.begin());p!=_interfacePrefixBlacklist.end();++p) {
+ if (!strncmp(p->c_str(),ifname,p->length()))
+ return false;
+ }
+ }
+ {
+ // Check global blacklists
+ const std::vector<InetAddress> *gbl = (const std::vector<InetAddress> *)0;
+ if (ifaddr.ss_family == AF_INET) {
+ gbl = &_globalV4Blacklist;
+ } else if (ifaddr.ss_family == AF_INET6) {
+ gbl = &_globalV6Blacklist;
+ }
+ if (gbl) {
+ Mutex::Lock _l(_localConfig_m);
+ for(std::vector<InetAddress>::const_iterator a(gbl->begin());a!=gbl->end();++a) {
+ if (a->containsAddress(ifaddr))
return false;
}
}
}
+ {
+ Mutex::Lock _l(_nets_m);
+ for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) {
+ if (n->second.tap) {
+ std::vector<InetAddress> ips(n->second.tap->ips());
+ for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
+ if (i->ipsEqual(ifaddr))
+ return false;
+ }
+ }
+ }
+ }
return true;
}
- std::string _dataStorePrepPath(const char *name) const
- {
- std::string p(_homePath);
- p.push_back(ZT_PATH_SEPARATOR);
- char lastc = (char)0;
- for(const char *n=name;(*n);++n) {
- if ((*n == '.')&&(lastc == '.'))
- return std::string(); // don't allow ../../ stuff as a precaution
- if (*n == '/') {
- OSUtils::mkdir(p.c_str());
- p.push_back(ZT_PATH_SEPARATOR);
- } else p.push_back(*n);
- lastc = *n;
- }
- return p;
- }
-
bool _trialBind(unsigned int port)
{
struct sockaddr_in in4;
@@ -1838,37 +2486,23 @@ public:
}
};
-static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf)
+static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); }
-static void SnodeEventCallback(ZT_Node *node,void *uptr,enum ZT_Event event,const void *metaData)
+static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeEventCallback(event,metaData); }
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStoreGetFunction(name,buf,bufSize,readIndex,totalSize); }
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); }
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); }
-static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
+{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeStatePutFunction(type,id,data,len); }
+static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeStateGetFunction(type,id,data,maxlen); }
+static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localSocket,addr,data,len,ttl); }
+static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); }
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(localAddr,remoteAddr); }
-
-#ifdef ZT_ENABLE_CLUSTER
-static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)
-{
- OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr);
- const ClusterDefinition::MemberDefinition &md = (*(impl->_clusterDefinition))[toMemberId];
- if (md.clusterEndpoint)
- impl->_phy.udpSend(impl->_clusterMessageSocket,reinterpret_cast<const struct sockaddr *>(&(md.clusterEndpoint)),data,len);
-}
-static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z)
-{
- OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr);
- return (int)(impl->_clusterDefinition->geo().locate(*(reinterpret_cast<const InetAddress *>(addr)),*x,*y,*z));
-}
-#endif
-
-static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int64_t localSocket,const struct sockaddr_storage *remoteAddr)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localSocket,remoteAddr); }
+static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); }
+static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); }
static int ShttpOnMessageBegin(http_parser *parser)
@@ -1877,10 +2511,10 @@ static int ShttpOnMessageBegin(http_parser *parser)
tc->currentHeaderField = "";
tc->currentHeaderValue = "";
tc->messageSize = 0;
- tc->url = "";
- tc->status = "";
+ tc->url.clear();
+ tc->status.clear();
tc->headers.clear();
- tc->body = "";
+ tc->readq.clear();
return 0;
}
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
@@ -1897,16 +2531,7 @@ static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
#else
static int ShttpOnStatus(http_parser *parser)
#endif
-{
- /*
- TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
- tc->messageSize += (unsigned long)length;
- if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
- return -1;
- tc->status.append(ptr,length);
- */
- return 0;
-}
+{ return 0; }
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
@@ -1944,14 +2569,12 @@ static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
tc->messageSize += (unsigned long)length;
if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1;
- tc->body.append(ptr,length);
+ tc->readq.append(ptr,length);
return 0;
}
static int ShttpOnMessageComplete(http_parser *parser)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
- tc->shouldKeepAlive = (http_should_keep_alive(parser) != 0);
- tc->lastActivity = OSUtils::now();
if (tc->type == TcpConnection::TCP_HTTP_INCOMING) {
tc->parent->onHttpRequestToServer(tc);
} else {
@@ -1967,30 +2590,6 @@ std::string OneService::platformDefaultHomePath()
return OSUtils::platformDefaultHomePath();
}
-std::string OneService::autoUpdateUrl()
-{
-#ifdef ZT_AUTO_UPDATE
-
-/*
-#if defined(__LINUX__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) )
- if (sizeof(void *) == 8)
- return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x64-LATEST.nfo";
- else return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x86-LATEST.nfo";
-#endif
-*/
-
-#if defined(__APPLE__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) )
- return "http://download.zerotier.com/update/mac_intel/";
-#endif
-
-#ifdef __WINDOWS__
- return "http://download.zerotier.com/update/win_intel/";
-#endif
-
-#endif // ZT_AUTO_UPDATE
- return std::string();
-}
-
OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); }
OneService::~OneService() {}
diff --git a/service/OneService.hpp b/service/OneService.hpp
index cead381d..3b670e4a 100644
--- a/service/OneService.hpp
+++ b/service/OneService.hpp
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,29 +14,35 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef ZT_ONESERVICE_HPP
#define ZT_ONESERVICE_HPP
#include <string>
+#include <vector>
+
+#include "../node/InetAddress.hpp"
+
+#ifdef ZT_SDK
+#include "../node/Node.hpp"
+// Use the virtual netcon endpoint instead of a tun/tap port driver
+#include "../include/VirtualTap.h"
+namespace ZeroTier { typedef VirtualTap EthernetTap; }
+#endif
namespace ZeroTier {
/**
* Local service for ZeroTier One as system VPN/NFV provider
- *
- * If built with ZT_ENABLE_NETWORK_CONTROLLER defined, this includes and
- * runs controller/SqliteNetworkController with a database called
- * controller.db in the specified home directory.
- *
- * If built with ZT_AUTO_UPDATE, an official ZeroTier update URL is
- * periodically checked and updates are automatically downloaded, verified
- * against a built-in list of update signing keys, and installed. This is
- * only supported for certain platforms.
- *
- * If built with ZT_ENABLE_CLUSTER, a 'cluster' file is checked and if
- * present is read to determine the identity of other cluster members.
*/
class OneService
{
@@ -78,6 +84,12 @@ public:
bool allowManaged;
/**
+ * Whitelist of addresses that can be configured by this network.
+ * If empty and allowManaged is true, allow all private/pseudoprivate addresses.
+ */
+ std::vector<InetAddress> allowManagedWhitelist;
+
+ /**
* Allow configuration of IPs and routes within global (Internet) IP space?
*/
bool allowGlobal;
@@ -94,11 +106,6 @@ public:
static std::string platformDefaultHomePath();
/**
- * @return Auto-update URL or empty string if auto-updates unsupported or not enabled
- */
- static std::string autoUpdateUrl();
-
- /**
* Create a new instance of the service
*
* Once created, you must call the run() method to actually start
@@ -111,9 +118,7 @@ public:
* @param hp Home path
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
*/
- static OneService *newInstance(
- const char *hp,
- unsigned int port);
+ static OneService *newInstance(const char *hp,unsigned int port);
virtual ~OneService();
@@ -141,10 +146,14 @@ public:
*/
virtual std::string portDeviceName(uint64_t nwid) const = 0;
- /**
- * @return True if TCP fallback is currently active
- */
- virtual bool tcpFallbackActive() const = 0;
+#ifdef ZT_SDK
+ virtual void leave(const uint64_t hp) = 0;
+ virtual void join(const uint64_t hp) = 0;
+ virtual std::string givenHomePath() = 0;
+ virtual Node * getNode() = 0;
+ virtual void removeNets() = 0;
+ virtual std::vector<ZT_VirtualNetworkRoute> *getRoutes(uint64_t nwid) = 0;
+#endif
/**
* Terminate background service (can be called from other threads)
diff --git a/service/README.md b/service/README.md
index 75c437dd..da29d3d0 100644
--- a/service/README.md
+++ b/service/README.md
@@ -1,9 +1,69 @@
ZeroTier One Network Virtualization Service
======
-This is the common background service implementation for ZeroTier One, the VPN-like OS-level network virtualization service.
-
-It provides a ready-made core I/O loop and a local HTTP-based JSON control bus for controlling the service. This control bus HTTP server can also serve the files in ui/ if this folder's contents are installed in the ZeroTier home folder. The ui/ implements a React-based HTML5 user interface which is then wrappered for various platforms via MacGap, Windows .NET WebControl, etc. It can also be used locally from scripts or via *curl*.
+This is the actual implementation of ZeroTier One, a service providing connectivity to ZeroTier virtual networks for desktops, laptops, servers, VMs, etc. (Mobile versions for iOS and Android have their own implementations in native Java and Objective C that leverage only the ZeroTier core engine.)
+
+### Local Configuration File
+
+A file called `local.conf` in the ZeroTier home folder contains configuration options that apply to the local node. It can be used to set up trusted paths, blacklist physical paths, set up physical path hints for certain nodes, and define trusted upstream devices (federated roots). In a large deployment it can be deployed using a tool like Puppet, Chef, SaltStack, etc. to set a uniform configuration across systems. It's a JSON format file that can also be edited and rewritten by ZeroTier One itself, so ensure that proper JSON formatting is used.
+
+Settings available in `local.conf` (this is not valid JSON, and JSON does not allow comments):
+
+```javascript
+{
+ "physical": { /* Settings that apply to physical L2/L3 network paths. */
+ "NETWORK/bits": { /* Network e.g. 10.0.0.0/24 or fd00::/32 */
+ "blacklist": true|false, /* If true, blacklist this path for all ZeroTier traffic */
+ "trustedPathId": 0|!0, /* If present and nonzero, define this as a trusted path (see below) */
+ "mtu": 0|!0 /* if present and non-zero, set UDP maximum payload MTU for this path */
+ } /* ,... additional networks */
+ },
+ "virtual": { /* Settings applied to ZeroTier virtual network devices (VL1) */
+ "##########": { /* 10-digit ZeroTier address */
+ "try": [ "IP/port"/*,...*/ ], /* Hints on where to reach this peer if no upstreams/roots are online */
+ "blacklist": [ "NETWORK/bits"/*,...*/ ] /* Blacklist a physical path for only this peer. */
+ }
+ },
+ "settings": { /* Other global settings */
+ "primaryPort": 0-65535, /* If set, override default port of 9993 and any command line port */
+ "portMappingEnabled": true|false, /* If true (the default), try to use uPnP or NAT-PMP to map ports */
+ "softwareUpdate": "apply"|"download"|"disable", /* Automatically apply updates, just download, or disable built-in software updates */
+ "softwareUpdateChannel": "release"|"beta", /* Software update channel */
+ "softwareUpdateDist": true|false, /* If true, distribute software updates (only really useful to ZeroTier, Inc. itself, default is false) */
+ "interfacePrefixBlacklist": [ "XXX",... ], /* Array of interface name prefixes (e.g. eth for eth#) to blacklist for ZT traffic */
+ "allowManagementFrom": "NETWORK/bits"|null, /* If non-NULL, allow JSON/HTTP management from this IP network. Default is 127.0.0.1 only. */
+ "bind": [ "ip",... ] /* If present and non-null, bind to these IPs instead of to each interface (wildcard IP allowed) */
+ }
+}
+```
+
+ * **trustedPathId**: A trusted path is a physical network over which encryption and authentication are not required. This provides a performance boost but sacrifices all ZeroTier's security features when communicating over this path. Only use this if you know what you are doing and really need the performance! To set up a trusted path, all devices using it *MUST* have the *same trusted path ID* for the same network. Trusted path IDs are arbitrary positive non-zero integers. For example a group of devices on a LAN with IPs in 10.0.0.0/24 could use it as a fast trusted path if they all had the same trusted path ID of "25" defined for that network.
+
+An example `local.conf`:
+
+```javascript
+{
+ "physical": {
+ "10.0.0.0/24": {
+ "blacklist": true
+ },
+ "10.10.10.0/24": {
+ "trustedPathId": 101010024
+ },
+ },
+ "virtual": {
+ "feedbeef12": {
+ "role": "UPSTREAM",
+ "try": [ "10.10.20.1/9993" ],
+ "blacklist": [ "192.168.0.0/24" ]
+ }
+ },
+ "settings": {
+ "softwareUpdate": "apply",
+ "softwareUpdateChannel": "release"
+ }
+}
+```
### Network Virtualization Service API
@@ -21,30 +81,20 @@ A *jsonp* URL argument may be supplied to request JSONP encapsulation. A JSONP r
* Methods: GET
* Returns: { object }
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>address</td><td>string</td><td>10-digit hexadecimal ZeroTier address of this node</td><td>no</td></tr>
-<tr><td>publicIdentity</td><td>string</td><td>Full public ZeroTier identity of this node</td><td>no</td></tr>
-<tr><td>online</td><td>boolean</td><td>Does this node appear to have upstream network access?</td><td>no</td></tr>
-<tr><td>tcpFallbackActive</td><td>boolean</td><td>Is TCP fallback mode active?</td><td>no</td></tr>
-<tr><td>versionMajor</td><td>integer</td><td>ZeroTier major version</td><td>no</td></tr>
-<tr><td>versionMinor</td><td>integer</td><td>ZeroTier minor version</td><td>no</td></tr>
-<tr><td>versionRev</td><td>integer</td><td>ZeroTier revision</td><td>no</td></tr>
-<tr><td>version</td><td>string</td><td>Version in major.minor.rev format</td><td>no</td></tr>
-<tr><td>clock</td><td>integer</td><td>Node system clock in ms since epoch</td><td>no</td></tr>
-</table>
-
-#### /config
-
- * Purpose: Get or set local configuration
- * Methods: GET, POST
- * Returns: { object }
-
-No local configuration options are exposed yet.
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-</table>
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| address | string | 10-digit hex ZeroTier address of this node | no |
+| publicIdentity | string | This node's ZeroTier identity.public | no |
+| worldId | integer | ZeroTier world ID (never changes except for test) | no |
+| worldTimestamp | integer | Timestamp of most recent world definition | no |
+| online | boolean | If true at least one upstream peer is reachable | no |
+| tcpFallbackActive | boolean | If true we are using slow TCP fallback | no |
+| relayPolicy | string | Relay policy: ALWAYS, TRUSTED, or NEVER | no |
+| versionMajor | integer | Software major version | no |
+| versionMinor | integer | Software minor version | no |
+| versionRev | integer | Software revision | no |
+| version | string | major.minor.revision | no |
+| clock | integer | Current system clock at node (ms since epoch) | no |
#### /network
@@ -64,23 +114,35 @@ To join a network, POST to it. Since networks have no mandatory writable paramet
Most network settings are not writable, as they are defined by the network controller.
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>nwid</td><td>string</td><td>16-digit hex network ID</td><td>no</td></tr>
-<tr><td>mac</td><td>string</td><td>Ethernet MAC address of virtual network port</td><td>no</td></tr>
-<tr><td>name</td><td>string</td><td>Network short name as configured on network controller</td><td>no</td></tr>
-<tr><td>status</td><td>string</td><td>Network status: OK, ACCESS_DENIED, PORT_ERROR, etc.</td><td>no</td></tr>
-<tr><td>type</td><td>string</td><td>Network type, currently PUBLIC or PRIVATE</td><td>no</td></tr>
-<tr><td>mtu</td><td>integer</td><td>Ethernet MTU</td><td>no</td></tr>
-<tr><td>dhcp</td><td>boolean</td><td>If true, DHCP may be used to obtain an IP address</td><td>no</td></tr>
-<tr><td>bridge</td><td>boolean</td><td>If true, this node may bridge in other Ethernet devices</td><td>no</td></tr>
-<tr><td>broadcastEnabled</td><td>boolean</td><td>Is Ethernet broadcast (ff:ff:ff:ff:ff:ff) allowed?</td><td>no</td></tr>
-<tr><td>portError</td><td>integer</td><td>Error code (if any) returned by underlying OS "tap" driver</td><td>no</td></tr>
-<tr><td>netconfRevision</td><td>integer</td><td>Network configuration revision ID</td><td>no</td></tr>
-<tr><td>multicastSubscriptions</td><td>[string]</td><td>Multicast memberships as array of MAC/ADI tuples</td><td>no</td></tr>
-<tr><td>assignedAddresses</td><td>[string]</td><td>ZeroTier-managed IP address assignments as array of IP/netmask bits tuples</td><td>no</td></tr>
-<tr><td>portDeviceName</td><td>string</td><td>OS-specific network device name (if available)</td><td>no</td></tr>
-</table>
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| id | string | 16-digit hex network ID | no |
+| nwid | string | 16-digit hex network ID (legacy field) | no |
+| mac | string | MAC address of network device for this network | no |
+| name | string | Short name of this network (from controller) | no |
+| status | string | Network status (OK, ACCESS_DENIED, etc.) | no |
+| type | string | Network type (PUBLIC or PRIVATE) | no |
+| mtu | integer | Ethernet MTU | no |
+| dhcp | boolean | If true, DHCP should be used to get IP info | no |
+| bridge | boolean | If true, this device can bridge others | no |
+| broadcastEnabled | boolean | If true ff:ff:ff:ff:ff:ff broadcasts work | no |
+| portError | integer | Error code returned by underlying tap driver | no |
+| netconfRevision | integer | Network configuration revision ID | no |
+| assignedAddresses | [string] | Array of ZeroTier-assigned IP addresses (/bits) | no |
+| routes | [object] | Array of ZeroTier-assigned routes (see below) | no |
+| portDeviceName | string | Name of virtual network device (if any) | no |
+| allowManaged | boolean | Allow IP and route management | yes |
+| allowGlobal | boolean | Allow IPs and routes that overlap with global IPs | yes |
+| allowDefault | boolean | Allow overriding of system default route | yes |
+
+Route objects:
+
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| target | string | Target network / netmask bits | no |
+| via | string | Gateway IP address (next hop) or null for LAN | no |
+| flags | integer | Flags, currently always 0 | no |
+| metric | integer | Route metric (not currently used) | no |
#### /peer
@@ -92,31 +154,29 @@ Getting /peer returns an array of peer objects for all current peers. See below
#### /peer/\<address\>
- * Purpose: Get information about a peer
- * Methods: GET
+ * Purpose: Get or set information about a peer
+ * Methods: GET, POST
* Returns: { object }
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>address</td><td>string</td><td>10-digit hex ZeroTier address</td><td>no</td></tr>
-<tr><td>lastUnicastFrame</td><td>integer</td><td>Time of last unicast frame in ms since epoch</td><td>no</td></tr>
-<tr><td>lastMulticastFrame</td><td>integer</td><td>Time of last multicast frame in ms since epoch</td><td>no</td></tr>
-<tr><td>versionMajor</td><td>integer</td><td>Major version of remote if known</td><td>no</td></tr>
-<tr><td>versionMinor</td><td>integer</td><td>Minor version of remote if known</td><td>no</td></tr>
-<tr><td>versionRev</td><td>integer</td><td>Revision of remote if known</td><td>no</td></tr>
-<tr><td>version</td><td>string</td><td>Version in major.minor.rev format</td><td>no</td></tr>
-<tr><td>latency</td><td>integer</td><td>Latency in milliseconds if known</td><td>no</td></tr>
-<tr><td>role</td><td>string</td><td>LEAF, HUB, or ROOTSERVER</td><td>no</td></tr>
-<tr><td>paths</td><td>[object]</td><td>Array of path objects (see below)</td><td>no</td></tr>
-</table>
-
-Path objects describe direct physical paths to peer. If no path objects are listed, peer is only reachable via indirect relay fallback. Path object format is:
-
-<table>
-<tr><td><b>Field</b></td><td><b>Type</b></td><td><b>Description</b></td><td><b>Writable</b></td></tr>
-<tr><td>address</td><td>string</td><td>Physical socket address e.g. IP/port for UDP</td><td>no</td></tr>
-<tr><td>lastSend</td><td>integer</td><td>Last send via this path in ms since epoch</td><td>no</td></tr>
-<tr><td>lastReceive</td><td>integer</td><td>Last receive via this path in ms since epoch</td><td>no</td></tr>
-<tr><td>fixed</td><td>boolean</td><td>If true, this is a statically-defined "fixed" path</td><td>no</td></tr>
-<tr><td>preferred</td><td>boolean</td><td>If true, this is the current preferred path</td><td>no</td></tr>
-</table>
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| address | string | 10-digit hex ZeroTier address of peer | no |
+| versionMajor | integer | Major version of remote (if known) | no |
+| versionMinor | integer | Minor version of remote (if known) | no |
+| versionRev | integer | Software revision of remote (if known) | no |
+| version | string | major.minor.revision | no |
+| latency | integer | Latency in milliseconds if known | no |
+| role | string | LEAF, UPSTREAM, ROOT or PLANET | no |
+| paths | [object] | Currently active physical paths (see below) | no |
+
+Path objects:
+
+| Field | Type | Description | Writable |
+| --------------------- | ------------- | ------------------------------------------------- | -------- |
+| address | string | Physical socket address e.g. IP/port | no |
+| lastSend | integer | Time of last send through this path | no |
+| lastReceive | integer | Time of last receive through this path | no |
+| active | boolean | Is this path in use? | no |
+| expired | boolean | Is this path expired? | no |
+| preferred | boolean | Is this a current preferred path? | no |
+| trustedPathId | integer | If nonzero this is a trusted path (unencrypted) | no |
diff --git a/service/SoftwareUpdater.cpp b/service/SoftwareUpdater.cpp
new file mode 100644
index 00000000..d12861d4
--- /dev/null
+++ b/service/SoftwareUpdater.cpp
@@ -0,0 +1,439 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../node/Constants.hpp"
+#include "../version.h"
+
+#ifdef __WINDOWS__
+#include <WinSock2.h>
+#include <Windows.h>
+#include <ShlObj.h>
+#include <netioapi.h>
+#include <iphlpapi.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+#endif
+
+#include "SoftwareUpdater.hpp"
+
+#include "../node/Utils.hpp"
+#include "../node/SHA512.hpp"
+#include "../node/Buffer.hpp"
+#include "../node/Node.hpp"
+
+#include "../osdep/OSUtils.hpp"
+
+namespace ZeroTier {
+
+static int _compareVersion(unsigned int maj1,unsigned int min1,unsigned int rev1,unsigned int b1,unsigned int maj2,unsigned int min2,unsigned int rev2,unsigned int b2)
+{
+ if (maj1 > maj2) {
+ return 1;
+ } else if (maj1 < maj2) {
+ return -1;
+ } else {
+ if (min1 > min2) {
+ return 1;
+ } else if (min1 < min2) {
+ return -1;
+ } else {
+ if (rev1 > rev2) {
+ return 1;
+ } else if (rev1 < rev2) {
+ return -1;
+ } else {
+ if (b1 > b2) {
+ return 1;
+ } else if (b1 < b2) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+}
+
+SoftwareUpdater::SoftwareUpdater(Node &node,const std::string &homePath) :
+ _node(node),
+ _lastCheckTime(0),
+ _homePath(homePath),
+ _channel(ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL),
+ _distLog((FILE *)0),
+ _latestValid(false),
+ _downloadLength(0)
+{
+ OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str());
+}
+
+SoftwareUpdater::~SoftwareUpdater()
+{
+ if (_distLog)
+ fclose(_distLog);
+}
+
+void SoftwareUpdater::setUpdateDistribution(bool distribute)
+{
+ _dist.clear();
+ if (distribute) {
+ _distLog = fopen((_homePath + ZT_PATH_SEPARATOR_S "update-dist.log").c_str(),"a");
+
+ const std::string udd(_homePath + ZT_PATH_SEPARATOR_S "update-dist.d");
+ const std::vector<std::string> ud(OSUtils::listDirectory(udd.c_str()));
+ for(std::vector<std::string>::const_iterator u(ud.begin());u!=ud.end();++u) {
+ // Each update has a companion .json file describing it. Other files are ignored.
+ if ((u->length() > 5)&&(u->substr(u->length() - 5,5) == ".json")) {
+
+ std::string buf;
+ if (OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + *u).c_str(),buf)) {
+ try {
+ _D d;
+ d.meta = OSUtils::jsonParse(buf); // throws on invalid JSON
+
+ // If update meta is called e.g. foo.exe.json, then foo.exe is the update itself
+ const std::string binPath(udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5));
+ const std::string metaHash(OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]));
+ if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile(binPath.c_str(),d.bin))) {
+ std::array<uint8_t,ZT_SHA512_DIGEST_LEN> sha512;
+ SHA512::hash(sha512.data(),d.bin.data(),(unsigned int)d.bin.length());
+ if (!memcmp(sha512.data(),metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
+ d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional
+ std::array<uint8_t,16> shakey;
+ memcpy(shakey.data(),sha512.data(),16);
+ _dist[shakey] = d;
+ if (_distLog) {
+ fprintf(_distLog,".......... INIT: DISTRIBUTING %s (%u bytes)" ZT_EOL_S,binPath.c_str(),(unsigned int)d.bin.length());
+ fflush(_distLog);
+ }
+ }
+ }
+ } catch ( ... ) {} // ignore bad meta JSON, etc.
+ }
+
+ }
+ }
+ } else {
+ if (_distLog) {
+ fclose(_distLog);
+ _distLog = (FILE *)0;
+ }
+ }
+}
+
+void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void *data,unsigned int len)
+{
+ if (!len) return;
+ const MessageVerb v = (MessageVerb)reinterpret_cast<const uint8_t *>(data)[0];
+ try {
+ switch(v) {
+
+ case VERB_GET_LATEST:
+ case VERB_LATEST: {
+ nlohmann::json req = OSUtils::jsonParse(std::string(reinterpret_cast<const char *>(data) + 1,len - 1)); // throws on invalid JSON
+ if (req.is_object()) {
+ const unsigned int rvMaj = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR],0);
+ const unsigned int rvMin = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR],0);
+ const unsigned int rvRev = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION],0);
+ const unsigned int rvBld = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD],0);
+ const unsigned int rvPlatform = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_PLATFORM],0);
+ const unsigned int rvArch = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE],0);
+ const unsigned int rvVendor = (unsigned int)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_VENDOR],0);
+ const std::string rvChannel(OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_CHANNEL],""));
+
+ if (v == VERB_GET_LATEST) {
+
+ if (_dist.size() > 0) {
+ const nlohmann::json *latest = (const nlohmann::json *)0;
+ const std::string expectedSigner = OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY],"");
+ unsigned int bestVMaj = rvMaj;
+ unsigned int bestVMin = rvMin;
+ unsigned int bestVRev = rvRev;
+ unsigned int bestVBld = rvBld;
+ for(std::map< std::array<uint8_t,16>,_D >::const_iterator d(_dist.begin());d!=_dist.end();++d) {
+ // The arch field in update description .json files can be an array for e.g. multi-arch update files
+ const nlohmann::json &dvArch2 = d->second.meta[ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE];
+ std::vector<unsigned int> dvArch;
+ if (dvArch2.is_array()) {
+ for(unsigned long i=0;i<dvArch2.size();++i)
+ dvArch.push_back((unsigned int)OSUtils::jsonInt(dvArch2[i],0));
+ } else {
+ dvArch.push_back((unsigned int)OSUtils::jsonInt(dvArch2,0));
+ }
+
+ if ((OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_PLATFORM],0) == rvPlatform)&&
+ (std::find(dvArch.begin(),dvArch.end(),rvArch) != dvArch.end())&&
+ (OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VENDOR],0) == rvVendor)&&
+ (OSUtils::jsonString(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_CHANNEL],"") == rvChannel)&&
+ (OSUtils::jsonString(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY],"") == expectedSigner)) {
+ const unsigned int dvMaj = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR],0);
+ const unsigned int dvMin = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR],0);
+ const unsigned int dvRev = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION],0);
+ const unsigned int dvBld = (unsigned int)OSUtils::jsonInt(d->second.meta[ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD],0);
+ if (_compareVersion(dvMaj,dvMin,dvRev,dvBld,bestVMaj,bestVMin,bestVRev,bestVBld) > 0) {
+ latest = &(d->second.meta);
+ bestVMaj = dvMaj;
+ bestVMin = dvMin;
+ bestVRev = dvRev;
+ bestVBld = dvBld;
+ }
+ }
+ }
+ if (latest) {
+ std::string lj;
+ lj.push_back((char)VERB_LATEST);
+ lj.append(OSUtils::jsonDump(*latest));
+ _node.sendUserMessage((void *)0,origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,lj.data(),(unsigned int)lj.length());
+ if (_distLog) {
+ fprintf(_distLog,"%.10llx GET_LATEST %u.%u.%u_%u platform %u arch %u vendor %u channel %s -> LATEST %u.%u.%u_%u" ZT_EOL_S,(unsigned long long)origin,rvMaj,rvMin,rvRev,rvBld,rvPlatform,rvArch,rvVendor,rvChannel.c_str(),bestVMaj,bestVMin,bestVRev,bestVBld);
+ fflush(_distLog);
+ }
+ }
+ } // else no reply, since we have nothing to distribute
+
+ } else { // VERB_LATEST
+
+ if ((origin == ZT_SOFTWARE_UPDATE_SERVICE)&&
+ (_compareVersion(rvMaj,rvMin,rvRev,rvBld,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION,ZEROTIER_ONE_VERSION_BUILD) > 0)&&
+ (OSUtils::jsonString(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY],"") == ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY)) {
+ const unsigned long len = (unsigned long)OSUtils::jsonInt(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE],0);
+ const std::string hash = OSUtils::jsonBinFromHex(req[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]);
+ if ((len <= ZT_SOFTWARE_UPDATE_MAX_SIZE)&&(hash.length() >= 16)) {
+ if (_latestMeta != req) {
+ _latestMeta = req;
+ _latestValid = false;
+ OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME).c_str());
+ _download = std::string();
+ memcpy(_downloadHashPrefix.data(),hash.data(),16);
+ _downloadLength = len;
+ }
+
+ if ((_downloadLength > 0)&&(_download.length() < _downloadLength)) {
+ Buffer<128> gd;
+ gd.append((uint8_t)VERB_GET_DATA);
+ gd.append(_downloadHashPrefix.data(),16);
+ gd.append((uint32_t)_download.length());
+ _node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+ }
+ }
+ }
+ }
+
+ }
+ } break;
+
+ case VERB_GET_DATA:
+ if ((len >= 21)&&(_dist.size() > 0)) {
+ unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 17) << 24;
+ idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16;
+ idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8;
+ idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20);
+ std::array<uint8_t,16> shakey;
+ memcpy(shakey.data(),reinterpret_cast<const uint8_t *>(data) + 1,16);
+ std::map< std::array<uint8_t,16>,_D >::iterator d(_dist.find(shakey));
+ if ((d != _dist.end())&&(idx < (unsigned long)d->second.bin.length())) {
+ Buffer<ZT_SOFTWARE_UPDATE_CHUNK_SIZE + 128> buf;
+ buf.append((uint8_t)VERB_DATA);
+ buf.append(reinterpret_cast<const uint8_t *>(data) + 1,16);
+ buf.append((uint32_t)idx);
+ buf.append(d->second.bin.data() + idx,std::min((unsigned long)ZT_SOFTWARE_UPDATE_CHUNK_SIZE,(unsigned long)(d->second.bin.length() - idx)));
+ _node.sendUserMessage((void *)0,origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,buf.data(),buf.size());
+ }
+ }
+ break;
+
+ case VERB_DATA:
+ if ((len >= 21)&&(_downloadLength > 0)&&(!memcmp(_downloadHashPrefix.data(),reinterpret_cast<const uint8_t *>(data) + 1,16))) {
+ unsigned long idx = (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 17) << 24;
+ idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16;
+ idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8;
+ idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20);
+ if (idx == (unsigned long)_download.length()) {
+ _download.append(reinterpret_cast<const char *>(data) + 21,len - 21);
+ if (_download.length() < _downloadLength) {
+ Buffer<128> gd;
+ gd.append((uint8_t)VERB_GET_DATA);
+ gd.append(_downloadHashPrefix.data(),16);
+ gd.append((uint32_t)_download.length());
+ _node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+ }
+ }
+ }
+ break;
+
+ default:
+ if (_distLog) {
+ fprintf(_distLog,"%.10llx WARNING: bad update message verb==%u length==%u (unrecognized verb)" ZT_EOL_S,(unsigned long long)origin,(unsigned int)v,len);
+ fflush(_distLog);
+ }
+ break;
+ }
+ } catch ( ... ) {
+ if (_distLog) {
+ fprintf(_distLog,"%.10llx WARNING: bad update message verb==%u length==%u (unexpected exception, likely invalid JSON)" ZT_EOL_S,(unsigned long long)origin,(unsigned int)v,len);
+ fflush(_distLog);
+ }
+ }
+}
+
+bool SoftwareUpdater::check(const int64_t now)
+{
+ if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) {
+ _lastCheckTime = now;
+ char tmp[512];
+ const unsigned int len = OSUtils::ztsnprintf(tmp,sizeof(tmp),
+ "%c{\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "\":\"%s\","
+ "\"" ZT_SOFTWARE_UPDATE_JSON_PLATFORM "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_VENDOR "\":%d,"
+ "\"" ZT_SOFTWARE_UPDATE_JSON_CHANNEL "\":\"%s\"}",
+ (char)VERB_GET_LATEST,
+ ZEROTIER_ONE_VERSION_MAJOR,
+ ZEROTIER_ONE_VERSION_MINOR,
+ ZEROTIER_ONE_VERSION_REVISION,
+ ZEROTIER_ONE_VERSION_BUILD,
+ ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY,
+ ZT_BUILD_PLATFORM,
+ ZT_BUILD_ARCHITECTURE,
+ (int)ZT_VENDOR_ZEROTIER,
+ _channel.c_str());
+ _node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len);
+ }
+
+ if (_latestValid)
+ return true;
+
+ if (_downloadLength > 0) {
+ if (_download.length() >= _downloadLength) {
+ // This is the very important security validation part that makes sure
+ // this software update doesn't have cooties.
+
+ const std::string binPath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME);
+ try {
+ // (1) Check the hash itself to make sure the image is basically okay
+ uint8_t sha512[ZT_SHA512_DIGEST_LEN];
+ SHA512::hash(sha512,_download.data(),(unsigned int)_download.length());
+ char hexbuf[(ZT_SHA512_DIGEST_LEN * 2) + 2];
+ if (OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"") == Utils::hex(sha512,ZT_SHA512_DIGEST_LEN,hexbuf)) {
+ // (2) Check signature by signing authority
+ const std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE]));
+ if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_download.data(),(unsigned int)_download.length(),sig.data(),(unsigned int)sig.length())) {
+ // (3) Try to save file, and if so we are good.
+ OSUtils::rm(binPath.c_str());
+ if (OSUtils::writeFile(binPath.c_str(),_download)) {
+ OSUtils::lockDownFile(binPath.c_str(),false);
+ _latestValid = true;
+ _download = std::string();
+ _downloadLength = 0;
+ return true;
+ }
+ }
+ }
+ } catch ( ... ) {} // any exception equals verification failure
+
+ // If we get here, checks failed.
+ OSUtils::rm(binPath.c_str());
+ _latestMeta = nlohmann::json();
+ _latestValid = false;
+ _download = std::string();
+ _downloadLength = 0;
+ } else {
+ Buffer<128> gd;
+ gd.append((uint8_t)VERB_GET_DATA);
+ gd.append(_downloadHashPrefix.data(),16);
+ gd.append((uint32_t)_download.length());
+ _node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+ }
+ }
+
+ return false;
+}
+
+void SoftwareUpdater::apply()
+{
+ std::string updatePath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME);
+ if ((_latestMeta.is_object())&&(_latestValid)&&(OSUtils::fileExists(updatePath.c_str(),false))) {
+#ifdef __WINDOWS__
+ std::string cmdArgs(OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS],""));
+ if (cmdArgs.length() > 0) {
+ updatePath.push_back(' ');
+ updatePath.append(cmdArgs);
+ }
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi;
+ memset(&si,0,sizeof(si));
+ memset(&pi,0,sizeof(pi));
+ CreateProcessA(NULL,const_cast<LPSTR>(updatePath.c_str()),NULL,NULL,FALSE,CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi);
+ // Windows doesn't exit here -- updater will stop the service during update, etc. -- but we do want to stop multiple runs from happening
+ _latestMeta = nlohmann::json();
+ _latestValid = false;
+#else
+ char *argv[256];
+ unsigned long ac = 0;
+ argv[ac++] = const_cast<char *>(updatePath.c_str());
+ const std::vector<std::string> argsSplit(OSUtils::split(OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS],"").c_str()," ","\\","\""));
+ for(std::vector<std::string>::const_iterator a(argsSplit.begin());a!=argsSplit.end();++a) {
+ argv[ac] = const_cast<char *>(a->c_str());
+ if (++ac == 255) break;
+ }
+ argv[ac] = (char *)0;
+ chmod(updatePath.c_str(),0700);
+
+ // Close all open file descriptors except stdout/stderr/etc.
+ int minMyFd = STDIN_FILENO;
+ if (STDOUT_FILENO > minMyFd) minMyFd = STDOUT_FILENO;
+ if (STDERR_FILENO > minMyFd) minMyFd = STDERR_FILENO;
+ ++minMyFd;
+#ifdef _SC_OPEN_MAX
+ int maxMyFd = (int)sysconf(_SC_OPEN_MAX);
+ if (maxMyFd <= minMyFd)
+ maxMyFd = 65536;
+#else
+ int maxMyFd = 65536;
+#endif
+ while (minMyFd < maxMyFd)
+ close(minMyFd++);
+
+ execv(updatePath.c_str(),argv);
+ fprintf(stderr,"FATAL: unable to execute software update binary at %s\n",updatePath.c_str());
+ exit(1);
+#endif
+ }
+}
+
+} // namespace ZeroTier
diff --git a/service/SoftwareUpdater.hpp b/service/SoftwareUpdater.hpp
new file mode 100644
index 00000000..7f73eba2
--- /dev/null
+++ b/service/SoftwareUpdater.hpp
@@ -0,0 +1,217 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_SOFTWAREUPDATER_HPP
+#define ZT_SOFTWAREUPDATER_HPP
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <vector>
+#include <map>
+#include <string>
+#include <array>
+
+#include "../include/ZeroTierOne.h"
+
+#include "../node/Identity.hpp"
+#include "../node/Packet.hpp"
+
+#include "../ext/json/json.hpp"
+
+/**
+ * VERB_USER_MESSAGE type ID for software update messages
+ */
+#define ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE 100
+
+/**
+ * ZeroTier address of node that provides software updates
+ */
+#define ZT_SOFTWARE_UPDATE_SERVICE 0xb1d366e81fULL
+
+/**
+ * ZeroTier identity that must be used to sign software updates
+ *
+ * df24360f3e - update-signing-key-0010 generated Fri Jan 13th, 2017 at 4:05pm PST
+ */
+#define ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY "df24360f3e:0:06072642959c8dfb68312904d74d90197c8a7692697caa1b3fd769eca714f4370fab462fcee6ebcb5fffb63bc5af81f28a2514b2cd68daabb42f7352c06f21db"
+
+/**
+ * Chunk size for in-band downloads (can be changed, designed to always fit in one UDP packet easily)
+ */
+#define ZT_SOFTWARE_UPDATE_CHUNK_SIZE (ZT_PROTO_MAX_PACKET_LENGTH - 128)
+
+/**
+ * Sanity limit for the size of an update binary image
+ */
+#define ZT_SOFTWARE_UPDATE_MAX_SIZE (1024 * 1024 * 256)
+
+/**
+ * How often (ms) do we check?
+ */
+#define ZT_SOFTWARE_UPDATE_CHECK_PERIOD (60 * 10 * 1000)
+
+/**
+ * Default update channel
+ */
+#define ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL "release"
+
+/**
+ * Filename for latest update's binary image
+ */
+#define ZT_SOFTWARE_UPDATE_BIN_FILENAME "latest-update.exe"
+
+#define ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "vMajor"
+#define ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "vMinor"
+#define ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "vRev"
+#define ZT_SOFTWARE_UPDATE_JSON_VERSION_BUILD "vBuild"
+#define ZT_SOFTWARE_UPDATE_JSON_PLATFORM "platform"
+#define ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "arch"
+#define ZT_SOFTWARE_UPDATE_JSON_VENDOR "vendor"
+#define ZT_SOFTWARE_UPDATE_JSON_CHANNEL "channel"
+#define ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "expectedSigner"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY "signer"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE "signature"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH "hash"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE "size"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS "execArgs"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_URL "url"
+
+namespace ZeroTier {
+
+class Node;
+
+/**
+ * This class handles retrieving and executing updates, or serving them
+ */
+class SoftwareUpdater
+{
+public:
+ /**
+ * Each message begins with an 8-bit message verb
+ */
+ enum MessageVerb
+ {
+ /**
+ * Payload: JSON containing current system platform, version, etc.
+ */
+ VERB_GET_LATEST = 1,
+
+ /**
+ * Payload: JSON describing latest update for this target. (No response is sent if there is none.)
+ */
+ VERB_LATEST = 2,
+
+ /**
+ * Payload:
+ * <[16] first 128 bits of hash of data object>
+ * <[4] 32-bit index of chunk to get>
+ */
+ VERB_GET_DATA = 3,
+
+ /**
+ * Payload:
+ * <[16] first 128 bits of hash of data object>
+ * <[4] 32-bit index of chunk>
+ * <[...] chunk data>
+ */
+ VERB_DATA = 4
+ };
+
+ SoftwareUpdater(Node &node,const std::string &homePath);
+ ~SoftwareUpdater();
+
+ /**
+ * Set whether or not we will distribute updates
+ *
+ * @param distribute If true, scan update-dist.d now and distribute updates found there -- if false, clear and stop distributing
+ */
+ void setUpdateDistribution(bool distribute);
+
+ /**
+ * Handle a software update user message
+ *
+ * @param origin ZeroTier address of message origin
+ * @param data Message payload
+ * @param len Length of message
+ */
+ void handleSoftwareUpdateUserMessage(uint64_t origin,const void *data,unsigned int len);
+
+ /**
+ * Check for updates and do other update-related housekeeping
+ *
+ * It should be called about every 10 seconds.
+ *
+ * @return True if we've downloaded and verified an update
+ */
+ bool check(const int64_t now);
+
+ /**
+ * @return Meta-data for downloaded update or NULL if none
+ */
+ inline const nlohmann::json &pending() const { return _latestMeta; }
+
+ /**
+ * Apply any ready update now
+ *
+ * Depending on the platform this function may never return and may forcibly
+ * exit the process. It does nothing if no update is ready.
+ */
+ void apply();
+
+ /**
+ * Set software update channel
+ *
+ * @param channel 'release', 'beta', etc.
+ */
+ inline void setChannel(const std::string &channel) { _channel = channel; }
+
+private:
+ Node &_node;
+ uint64_t _lastCheckTime;
+ std::string _homePath;
+ std::string _channel;
+ FILE *_distLog;
+
+ // Offered software updates if we are an update host (we have update-dist.d and update hosting is enabled)
+ struct _D
+ {
+ nlohmann::json meta;
+ std::string bin;
+ };
+ std::map< std::array<uint8_t,16>,_D > _dist; // key is first 16 bytes of hash
+
+ nlohmann::json _latestMeta;
+ bool _latestValid;
+
+ std::string _download;
+ std::array<uint8_t,16> _downloadHashPrefix;
+ unsigned long _downloadLength;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/tcp-proxy/Makefile b/tcp-proxy/Makefile
deleted file mode 100644
index af4e71e3..00000000
--- a/tcp-proxy/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-CXX=$(shell which clang++ g++ c++ 2>/dev/null | head -n 1)
-
-all:
- $(CXX) -O3 -fno-rtti -o tcp-proxy tcp-proxy.cpp
-
-clean:
- rm -f *.o tcp-proxy *.dSYM
diff --git a/tcp-proxy/README.md b/tcp-proxy/README.md
deleted file mode 100644
index 6f347d64..00000000
--- a/tcp-proxy/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-TCP Proxy Server
-======
-
-This is the TCP proxy server we run for TCP tunneling from peers behind fascist NATs. Regular users won't have much use for this.
diff --git a/tcp-proxy/tcp-proxy.cpp b/tcp-proxy/tcp-proxy.cpp
deleted file mode 100644
index 2fe500d1..00000000
--- a/tcp-proxy/tcp-proxy.cpp
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-// HACK! Will eventually use epoll() or something in Phy<> instead of select().
-// Also be sure to change ulimit -n and fs.file-max in /etc/sysctl.conf on relays.
-#if defined(__linux__) || defined(__LINUX__) || defined(__LINUX) || defined(LINUX)
-#include <linux/posix_types.h>
-#include <bits/types.h>
-#undef __FD_SETSIZE
-#define __FD_SETSIZE 1048576
-#undef FD_SETSIZE
-#define FD_SETSIZE 1048576
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <signal.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <algorithm>
-#include <vector>
-
-#include "../osdep/Phy.hpp"
-
-#define ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS 300
-#define ZT_TCP_PROXY_TCP_PORT 443
-
-using namespace ZeroTier;
-
-/*
- * ZeroTier TCP Proxy Server
- *
- * This implements a simple packet encapsulation that is designed to look like
- * a TLS connection. It's not a TLS connection, but it sends TLS format record
- * headers. It could be extended in the future to implement a fake TLS
- * handshake.
- *
- * At the moment, each packet is just made to look like TLS application data:
- * <[1] TLS content type> - currently 0x17 for "application data"
- * <[1] TLS major version> - currently 0x03 for TLS 1.2
- * <[1] TLS minor version> - currently 0x03 for TLS 1.2
- * <[2] payload length> - 16-bit length of payload in bytes
- * <[...] payload> - Message payload
- *
- * TCP is inherently inefficient for encapsulating Ethernet, since TCP and TCP
- * like protocols over TCP lead to double-ACKs. So this transport is only used
- * to enable access when UDP or other datagram protocols are not available.
- *
- * Clients send a greeting, which is a four-byte message that contains:
- * <[1] ZeroTier major version>
- * <[1] minor version>
- * <[2] revision>
- *
- * If a client has sent a greeting, it uses the new version of this protocol
- * in which every encapsulated ZT packet is prepended by an IP address where
- * it should be forwarded (or where it came from for replies). This causes
- * this proxy to act as a remote UDP socket similar to a socks proxy, which
- * will allow us to move this function off the rootservers and onto dedicated
- * proxy nodes.
- *
- * Older ZT clients that do not send this message get their packets relayed
- * to/from 127.0.0.1:9993, which will allow them to talk to and relay via
- * the ZT node on the same machine as the proxy. We'll only support this for
- * as long as such nodes appear to be in the wild.
- */
-
-struct TcpProxyService;
-struct TcpProxyService
-{
- Phy<TcpProxyService *> *phy;
- int udpPortCounter;
- struct Client
- {
- char tcpReadBuf[131072];
- char tcpWriteBuf[131072];
- unsigned long tcpWritePtr;
- unsigned long tcpReadPtr;
- PhySocket *tcp;
- PhySocket *udp;
- time_t lastActivity;
- bool newVersion;
- };
- std::map< PhySocket *,Client > clients;
-
- PhySocket *getUnusedUdp(void *uptr)
- {
- for(int i=0;i<65535;++i) {
- ++udpPortCounter;
- if (udpPortCounter > 0xfffe)
- udpPortCounter = 1024;
- struct sockaddr_in laddr;
- memset(&laddr,0,sizeof(struct sockaddr_in));
- laddr.sin_family = AF_INET;
- laddr.sin_port = htons((uint16_t)udpPortCounter);
- PhySocket *udp = phy->udpBind(reinterpret_cast<struct sockaddr *>(&laddr),uptr);
- if (udp)
- return udp;
- }
- return (PhySocket *)0;
- }
-
- void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len)
- {
- if (!*uptr)
- return;
- if ((from->sa_family == AF_INET)&&(len >= 16)&&(len < 2048)) {
- Client &c = *((Client *)*uptr);
- c.lastActivity = time((time_t *)0);
-
- unsigned long mlen = len;
- if (c.newVersion)
- mlen += 7; // new clients get IP info
-
- if ((c.tcpWritePtr + 5 + mlen) <= sizeof(c.tcpWriteBuf)) {
- if (!c.tcpWritePtr)
- phy->tcpSetNotifyWritable(c.tcp,true);
-
- c.tcpWriteBuf[c.tcpWritePtr++] = 0x17; // look like TLS data
- c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2
- c.tcpWriteBuf[c.tcpWritePtr++] = 0x03; // look like TLS 1.2
-
- c.tcpWriteBuf[c.tcpWritePtr++] = (char)((mlen >> 8) & 0xff);
- c.tcpWriteBuf[c.tcpWritePtr++] = (char)(mlen & 0xff);
-
- if (c.newVersion) {
- c.tcpWriteBuf[c.tcpWritePtr++] = (char)4; // IPv4
- *((uint32_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_addr.s_addr;
- c.tcpWritePtr += 4;
- *((uint16_t *)(c.tcpWriteBuf + c.tcpWritePtr)) = ((const struct sockaddr_in *)from)->sin_port;
- c.tcpWritePtr += 2;
- }
-
- for(unsigned long i=0;i<len;++i)
- c.tcpWriteBuf[c.tcpWritePtr++] = ((const char *)data)[i];
- }
-
- //printf("<< UDP %s:%d -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(int)ntohs(reinterpret_cast<const struct sockaddr_in *>(from)->sin_port),(unsigned long long)&c);
- }
- }
-
- void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
- {
- // unused, we don't initiate outbound connections
- }
-
- void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
- {
- Client &c = clients[sockN];
- PhySocket *udp = getUnusedUdp((void *)&c);
- if (!udp) {
- phy->close(sockN);
- clients.erase(sockN);
- //printf("** TCP rejected, no more UDP ports to assign\n");
- return;
- }
- c.tcpWritePtr = 0;
- c.tcpReadPtr = 0;
- c.tcp = sockN;
- c.udp = udp;
- c.lastActivity = time((time_t *)0);
- c.newVersion = false;
- *uptrN = (void *)&c;
- //printf("<< TCP from %s -> %.16llx\n",inet_ntoa(reinterpret_cast<const struct sockaddr_in *>(from)->sin_addr),(unsigned long long)&c);
- }
-
- void phyOnTcpClose(PhySocket *sock,void **uptr)
- {
- if (!*uptr)
- return;
- Client &c = *((Client *)*uptr);
- phy->close(c.udp);
- clients.erase(sock);
- //printf("** TCP %.16llx closed\n",(unsigned long long)*uptr);
- }
-
- void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
- {
- Client &c = *((Client *)*uptr);
- c.lastActivity = time((time_t *)0);
-
- for(unsigned long i=0;i<len;++i) {
- if (c.tcpReadPtr >= sizeof(c.tcpReadBuf)) {
- phy->close(sock);
- return;
- }
- c.tcpReadBuf[c.tcpReadPtr++] = ((const char *)data)[i];
-
- if (c.tcpReadPtr >= 5) {
- unsigned long mlen = ( ((((unsigned long)c.tcpReadBuf[3]) & 0xff) << 8) | (((unsigned long)c.tcpReadBuf[4]) & 0xff) );
- if (c.tcpReadPtr >= (mlen + 5)) {
- if (mlen == 4) {
- // Right now just sending this means the client is 'new enough' for the IP header
- c.newVersion = true;
- //printf("<< TCP %.16llx HELLO\n",(unsigned long long)*uptr);
- } else if (mlen >= 7) {
- char *payload = c.tcpReadBuf + 5;
- unsigned long payloadLen = mlen;
-
- struct sockaddr_in dest;
- memset(&dest,0,sizeof(dest));
- if (c.newVersion) {
- if (*payload == (char)4) {
- // New clients tell us where their packets go.
- ++payload;
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = *((uint32_t *)payload);
- payload += 4;
- dest.sin_port = *((uint16_t *)payload); // will be in network byte order already
- payload += 2;
- payloadLen -= 7;
- }
- } else {
- // For old clients we will just proxy everything to a local ZT instance. The
- // fact that this will come from 127.0.0.1 will in turn prevent that instance
- // from doing unite() with us. It'll just forward. There will not be many of
- // these.
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
- dest.sin_port = htons(9993);
- }
-
- // Note: we do not relay to privileged ports... just an abuse prevention rule.
- if ((ntohs(dest.sin_port) > 1024)&&(payloadLen >= 16)) {
- phy->udpSend(c.udp,(const struct sockaddr *)&dest,payload,payloadLen);
- //printf(">> TCP %.16llx to %s:%d\n",(unsigned long long)*uptr,inet_ntoa(dest.sin_addr),(int)ntohs(dest.sin_port));
- }
- }
-
- memmove(c.tcpReadBuf,c.tcpReadBuf + (mlen + 5),c.tcpReadPtr -= (mlen + 5));
- }
- }
- }
- }
-
- void phyOnTcpWritable(PhySocket *sock,void **uptr)
- {
- Client &c = *((Client *)*uptr);
- if (c.tcpWritePtr) {
- long n = phy->tcpSend(sock,c.tcpWriteBuf,c.tcpWritePtr);
- if (n > 0) {
- memmove(c.tcpWriteBuf,c.tcpWriteBuf + n,c.tcpWritePtr -= (unsigned long)n);
- if (!c.tcpWritePtr)
- phy->tcpSetNotifyWritable(sock,false);
- }
- } else phy->tcpSetNotifyWritable(sock,false);
- }
-
- void doHousekeeping()
- {
- std::vector<PhySocket *> toClose;
- time_t now = time((time_t *)0);
- for(std::map< PhySocket *,Client >::iterator c(clients.begin());c!=clients.end();++c) {
- if ((now - c->second.lastActivity) >= ZT_TCP_PROXY_CONNECTION_TIMEOUT_SECONDS) {
- toClose.push_back(c->first);
- toClose.push_back(c->second.udp);
- }
- }
- for(std::vector<PhySocket *>::iterator s(toClose.begin());s!=toClose.end();++s)
- phy->close(*s);
- }
-};
-
-int main(int argc,char **argv)
-{
- signal(SIGPIPE,SIG_IGN);
- signal(SIGHUP,SIG_IGN);
- srand(time((time_t *)0));
-
- TcpProxyService svc;
- Phy<TcpProxyService *> phy(&svc,false,true);
- svc.phy = &phy;
- svc.udpPortCounter = 1023;
-
- {
- struct sockaddr_in laddr;
- memset(&laddr,0,sizeof(laddr));
- laddr.sin_family = AF_INET;
- laddr.sin_port = htons(ZT_TCP_PROXY_TCP_PORT);
- if (!phy.tcpListen((const struct sockaddr *)&laddr)) {
- fprintf(stderr,"%s: fatal error: unable to bind TCP port %d\n",argv[0],ZT_TCP_PROXY_TCP_PORT);
- return 1;
- }
- }
-
- time_t lastDidHousekeeping = time((time_t *)0);
- for(;;) {
- phy.poll(120000);
- time_t now = time((time_t *)0);
- if ((now - lastDidHousekeeping) > 120) {
- lastDidHousekeeping = now;
- svc.doHousekeeping();
- }
- }
-
- return 0;
-}
diff --git a/version.h b/version.h
index 18300110..064f2585 100644
--- a/version.h
+++ b/version.h
@@ -1,6 +1,6 @@
/*
* ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/
*
* 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
@@ -14,6 +14,14 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
*/
#ifndef _ZT_VERSION_H
@@ -27,11 +35,27 @@
/**
* Minor version
*/
-#define ZEROTIER_ONE_VERSION_MINOR 1
+#define ZEROTIER_ONE_VERSION_MINOR 2
/**
* Revision
*/
-#define ZEROTIER_ONE_VERSION_REVISION 14
+#define ZEROTIER_ONE_VERSION_REVISION 6
+
+/**
+ * Build version
+ *
+ * This starts at 0 for each major.minor.rev tuple and can be incremented
+ * to force a minor update without an actual version number change. It's
+ * not part of the actual release version number.
+ */
+#define ZEROTIER_ONE_VERSION_BUILD 0
+
+#ifndef ZT_BUILD_ARCHITECTURE
+#define ZT_BUILD_ARCHITECTURE 0
+#endif
+#ifndef ZT_BUILD_PLATFORM
+#define ZT_BUILD_PLATFORM 0
+#endif
#endif
diff --git a/windows/TapDriver6/TapDriver6.vcxproj b/windows/TapDriver6/TapDriver6.vcxproj
index b1f9ae18..cf6b1500 100644
--- a/windows/TapDriver6/TapDriver6.vcxproj
+++ b/windows/TapDriver6/TapDriver6.vcxproj
@@ -63,7 +63,6 @@
<VCTargetsPath Condition="'$(VCTargetsPath11)' != '' and '$(VisualStudioVersion)' == '11.0'">$(VCTargetsPath11)</VCTargetsPath>
</PropertyGroup>
<PropertyGroup Label="PropertySheets">
- <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>KMDF</DriverType>
</PropertyGroup>
@@ -71,54 +70,66 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|Win32'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'" Label="Configuration">
<TargetVersion>Windows7</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'" Label="Configuration">
<TargetVersion>Windows7</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Debug|Win32'" Label="Configuration">
<TargetVersion>Vista</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'" Label="Configuration">
<TargetVersion>Vista</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<KMDF_VERSION_MINOR>7</KMDF_VERSION_MINOR>
+ <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Debug|x64'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'" Label="Configuration">
<TargetVersion>Windows8</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'" Label="Configuration">
<TargetVersion>Windows7</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'" Label="Configuration">
<TargetVersion>Windows7</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Debug|x64'" Label="Configuration">
<TargetVersion>Vista</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'" Label="Configuration">
<TargetVersion>Vista</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
<KMDF_VERSION_MINOR>7</KMDF_VERSION_MINOR>
+ <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -218,10 +229,10 @@
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Vista Release|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Link>
- <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">$(DDK_LIB_PATH)ndis.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Link>
- <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">$(DDK_LIB_PATH)ndis.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Link>
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">C:\WinDDK\7600.16385.1\lib\win7\amd64\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\amd64\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -236,10 +247,10 @@
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Vista Release|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Link>
- <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">$(DDK_LIB_PATH)ndis.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Link>
- <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">$(DDK_LIB_PATH)ndis.lib;$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Link>
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">C:\WinDDK\7600.16385.1\lib\win7\i386\ndis.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\ntstrsafe.lib;C:\WinDDK\7600.16385.1\lib\win7\i386\wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
@@ -261,13 +272,18 @@
</Inf>
<Inf>
<TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">3.00.00.0</TimeStamp>
- <SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">false</SpecifyDriverVerDirectiveVersion>
- <SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">false</SpecifyDriverVerDirectiveDate>
+ <SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">true</SpecifyDriverVerDirectiveVersion>
+ <SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">true</SpecifyDriverVerDirectiveDate>
+ <VersionHeaderPath Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">
+ </VersionHeaderPath>
+ <CatalogFileName Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|Win32'">zttap300.cat</CatalogFileName>
</Inf>
<Inf>
- <TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">3.00.00.0</TimeStamp>
+ <TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">
+ </TimeStamp>
<SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">false</SpecifyDriverVerDirectiveVersion>
<SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'">false</SpecifyDriverVerDirectiveDate>
+ <DateStamp Condition="'$(Configuration)|$(Platform)'=='Win7 Release|Win32'" />
</Inf>
<Inf>
<TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win8 Release|Win32'">3.00.00.0</TimeStamp>
@@ -291,13 +307,18 @@
</Inf>
<Inf>
<TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">3.00.00.0</TimeStamp>
- <SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">false</SpecifyDriverVerDirectiveVersion>
- <SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">false</SpecifyDriverVerDirectiveDate>
+ <SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">true</SpecifyDriverVerDirectiveVersion>
+ <SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">true</SpecifyDriverVerDirectiveDate>
+ <VersionHeaderPath Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">
+ </VersionHeaderPath>
+ <CatalogFileName Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">zttap300.cat</CatalogFileName>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Win7 Debug|x64'">-v "3.00.00.0" %(AdditionalOptions)</AdditionalOptions>
</Inf>
<Inf>
<TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">3.00.00.0</TimeStamp>
- <SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">false</SpecifyDriverVerDirectiveVersion>
- <SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">false</SpecifyDriverVerDirectiveDate>
+ <SpecifyDriverVerDirectiveVersion Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">true</SpecifyDriverVerDirectiveVersion>
+ <SpecifyDriverVerDirectiveDate Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">true</SpecifyDriverVerDirectiveDate>
+ <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Win7 Release|x64'">-v "3.00.00.0" %(AdditionalOptions)</AdditionalOptions>
</Inf>
<Inf>
<TimeStamp Condition="'$(Configuration)|$(Platform)'=='Win8 Release|x64'">3.00.00.0</TimeStamp>
diff --git a/windows/TapDriver6/tap-windows.h b/windows/TapDriver6/tap-windows.h
index 7e01846d..fd41a798 100644
--- a/windows/TapDriver6/tap-windows.h
+++ b/windows/TapDriver6/tap-windows.h
@@ -42,7 +42,9 @@
//#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED)
//#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED)
-//#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
+#if DBG
+#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
+#endif
//#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED)
/* Added in 8.2 */
diff --git a/windows/WinUI/APIHandler.cs b/windows/WinUI/APIHandler.cs
index 92b83021..015415c3 100644
--- a/windows/WinUI/APIHandler.cs
+++ b/windows/WinUI/APIHandler.cs
@@ -7,6 +7,8 @@ using System.Net;
using System.IO;
using System.Windows;
using Newtonsoft.Json;
+using System.Diagnostics;
+using System.Windows.Threading;
namespace WinUI
{
@@ -18,18 +20,137 @@ namespace WinUI
private string url = null;
- public APIHandler()
+ private static volatile APIHandler instance;
+ private static object syncRoot = new Object();
+
+ public delegate void NetworkListCallback(List<ZeroTierNetwork> networks);
+ public delegate void StatusCallback(ZeroTierStatus status);
+
+ private string ZeroTierAddress = "";
+
+ public static APIHandler Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncRoot)
+ {
+ if (instance == null)
+ {
+ if (!initHandler())
+ {
+ return null;
+ }
+ }
+ }
+ }
+
+ return instance;
+ }
+ }
+
+ private static bool initHandler(bool resetToken = false)
+ {
+ String localZtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String globalZtDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\ZeroTier\\One";
+
+ String authToken = "";
+ Int32 port = 9993;
+
+ if (resetToken)
+ {
+ instance = null;
+ if (File.Exists(localZtDir + "\\authtoken.secret"))
+ {
+ File.Delete(localZtDir + "\\authtoken.secret");
+ }
+
+ if (File.Exists(localZtDir + "\\zerotier-one.port"))
+ {
+ File.Delete(localZtDir + "\\zerotier-one.port");
+ }
+ }
+
+ if (!File.Exists(localZtDir + "\\authtoken.secret") || !File.Exists(localZtDir + "\\zerotier-one.port"))
+ {
+ // launch external process to copy file into place
+ String curPath = System.Reflection.Assembly.GetEntryAssembly().Location;
+ int index = curPath.LastIndexOf("\\");
+ curPath = curPath.Substring(0, index);
+ ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", "\""+globalZtDir+"\"" + " " + "\""+localZtDir+"\"");
+ startInfo.Verb = "runas";
+
+
+ var process = Process.Start(startInfo);
+ process.WaitForExit();
+ }
+
+ authToken = readAuthToken(localZtDir + "\\authtoken.secret");
+
+ if ((authToken == null) || (authToken.Length <= 0))
+ {
+ MessageBox.Show("Unable to read ZeroTier One authtoken", "ZeroTier One");
+ return false;
+ }
+
+ port = readPort(localZtDir + "\\zerotier-one.port");
+ instance = new APIHandler(port, authToken);
+ return true;
+ }
+
+ private static String readAuthToken(String path)
+ {
+ String authToken = "";
+
+ if (File.Exists(path))
+ {
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ authToken = System.Text.Encoding.UTF8.GetString(tmp).Trim();
+ }
+ catch
+ {
+ MessageBox.Show("Unable to read ZeroTier One Auth Token from:\r\n" + path, "ZeroTier One");
+ }
+ }
+
+ return authToken;
+ }
+
+ private static Int32 readPort(String path)
+ {
+ Int32 port = 9993;
+
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ port = Int32.Parse(System.Text.Encoding.ASCII.GetString(tmp).Trim());
+ if ((port <= 0) || (port > 65535))
+ port = 9993;
+ }
+ catch
+ {
+ }
+
+ return port;
+ }
+
+ private APIHandler()
{
url = "http://127.0.0.1:9993";
}
public APIHandler(int port, string authtoken)
{
- url = "http://localhost:" + port;
+ url = "http://127.0.0.1:" + port;
this.authtoken = authtoken;
}
- public ZeroTierStatus GetStatus()
+
+
+ public void GetStatus(StatusCallback cb)
{
var request = WebRequest.Create(url + "/status" + "?auth=" + authtoken) as HttpWebRequest;
if (request != null)
@@ -41,136 +162,245 @@ namespace WinUI
try
{
var httpResponse = (HttpWebResponse)request.GetResponse();
- using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
- {
- var responseText = streamReader.ReadToEnd();
+ if (httpResponse.StatusCode == HttpStatusCode.OK)
+ {
+ using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
+ {
+ var responseText = streamReader.ReadToEnd();
- ZeroTierStatus status = null;
- try
- {
- status = JsonConvert.DeserializeObject<ZeroTierStatus>(responseText);
- }
- catch (JsonReaderException e)
- {
- Console.WriteLine(e.ToString());
- }
- return status;
- }
+ ZeroTierStatus status = null;
+ try
+ {
+ status = JsonConvert.DeserializeObject<ZeroTierStatus>(responseText);
+
+ if (ZeroTierAddress != status.Address)
+ {
+ ZeroTierAddress = status.Address;
+ }
+ }
+ catch (JsonReaderException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ cb(status);
+ }
+ }
+ else if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
}
catch (System.Net.Sockets.SocketException)
{
- return null;
+ cb(null);
}
- catch (System.Net.WebException)
+ catch (System.Net.WebException e)
{
- return null;
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else
+ {
+ cb(null);
+ }
}
}
- public List<ZeroTierNetwork> GetNetworks()
+
+
+ public void GetNetworks(NetworkListCallback cb)
{
var request = WebRequest.Create(url + "/network" + "?auth=" + authtoken) as HttpWebRequest;
if (request == null)
{
- return null;
+ cb(null);
}
request.Method = "GET";
request.ContentType = "application/json";
+ request.Timeout = 10000;
try
{
var httpResponse = (HttpWebResponse)request.GetResponse();
- using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
- {
- var responseText = streamReader.ReadToEnd();
- List<ZeroTierNetwork> networkList = null;
- try
- {
- networkList = JsonConvert.DeserializeObject<List<ZeroTierNetwork>>(responseText);
- }
- catch (JsonReaderException e)
- {
- Console.WriteLine(e.ToString());
- }
- return networkList;
- }
+ if (httpResponse.StatusCode == HttpStatusCode.OK)
+ {
+ using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
+ {
+ var responseText = streamReader.ReadToEnd();
+
+ List<ZeroTierNetwork> networkList = null;
+ try
+ {
+ networkList = JsonConvert.DeserializeObject<List<ZeroTierNetwork>>(responseText);
+ foreach (ZeroTierNetwork n in networkList)
+ {
+ // all networks received via JSON are connected by definition
+ n.IsConnected = true;
+ }
+ }
+ catch (JsonReaderException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ cb(networkList);
+ }
+ }
+ else if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
}
catch (System.Net.Sockets.SocketException)
{
- return null;
+ cb(null);
}
- catch (System.Net.WebException)
+ catch (System.Net.WebException e)
{
- return null;
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else
+ {
+ cb(null);
+ }
}
}
- public void JoinNetwork(string nwid)
+ public void JoinNetwork(Dispatcher d, string nwid, bool allowManaged = true, bool allowGlobal = false, bool allowDefault = false)
{
- var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
- if (request == null)
- {
- return;
- }
+ Task.Factory.StartNew(() =>
+ {
+ var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ return;
+ }
- request.Method = "POST";
+ request.Method = "POST";
+ request.ContentType = "applicaiton/json";
+ request.Timeout = 30000;
+ try
+ {
+ using (var streamWriter = new StreamWriter(((HttpWebRequest)request).GetRequestStream()))
+ {
+ string json = "{\"allowManaged\":" + (allowManaged ? "true" : "false") + "," +
+ "\"allowGlobal\":" + (allowGlobal ? "true" : "false") + "," +
+ "\"allowDefault\":" + (allowDefault ? "true" : "false") + "}";
+ streamWriter.Write(json);
+ streamWriter.Flush();
+ streamWriter.Close();
+ }
+ }
+ catch (System.Net.WebException)
+ {
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }));
+ return;
+ }
- try
- {
- var httpResponse = (HttpWebResponse)request.GetResponse();
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
- if (httpResponse.StatusCode != HttpStatusCode.OK)
- {
- Console.WriteLine("Error sending join network message");
- }
- }
- catch (System.Net.Sockets.SocketException)
- {
- MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
- }
- catch (System.Net.WebException)
- {
- MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
- }
+ if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else if (httpResponse.StatusCode != HttpStatusCode.OK)
+ {
+ Console.WriteLine("Error sending join network message");
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }));
+ }
+ catch (System.Net.WebException e)
+ {
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Joining Network: Cannot connect to ZeroTier service.");
+ }));
+ }
+ });
}
- public void LeaveNetwork(string nwid)
+ public void LeaveNetwork(Dispatcher d, string nwid)
{
- var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
- if (request == null)
- {
- return;
- }
+ Task.Factory.StartNew(() =>
+ {
+ var request = WebRequest.Create(url + "/network/" + nwid + "?auth=" + authtoken) as HttpWebRequest;
+ if (request == null)
+ {
+ return;
+ }
- request.Method = "DELETE";
+ request.Method = "DELETE";
+ request.Timeout = 30000;
- try
- {
- var httpResponse = (HttpWebResponse)request.GetResponse();
+ try
+ {
+ var httpResponse = (HttpWebResponse)request.GetResponse();
- if (httpResponse.StatusCode != HttpStatusCode.OK)
- {
- Console.WriteLine("Error sending leave network message");
- }
- }
- catch (System.Net.Sockets.SocketException)
- {
- MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
- }
- catch (System.Net.WebException)
- {
- MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
- }
+ if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else if (httpResponse.StatusCode != HttpStatusCode.OK)
+ {
+ Console.WriteLine("Error sending leave network message");
+ }
+ }
+ catch (System.Net.Sockets.SocketException)
+ {
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
+ }));
+ }
+ catch (System.Net.WebException e)
+ {
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ d.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ MessageBox.Show("Error Leaving Network: Cannot connect to ZeroTier service.");
+ }));
+ }
+ catch
+ {
+ Console.WriteLine("Error leaving network: Unknown error");
+ }
+ });
}
- public List<ZeroTierPeer> GetPeers()
+ public delegate void PeersCallback(List<ZeroTierPeer> peers);
+
+ public void GetPeers(PeersCallback cb)
{
var request = WebRequest.Create(url + "/peer" + "?auth=" + authtoken) as HttpWebRequest;
if (request == null)
{
- return null;
+ cb(null);
}
request.Method = "GET";
@@ -179,30 +409,50 @@ namespace WinUI
try
{
var httpResponse = (HttpWebResponse)request.GetResponse();
- using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
- {
- var responseText = streamReader.ReadToEnd();
- //Console.WriteLine(responseText);
- List<ZeroTierPeer> peerList = null;
- try
- {
- peerList = JsonConvert.DeserializeObject<List<ZeroTierPeer>>(responseText);
- }
- catch (JsonReaderException e)
- {
- Console.WriteLine(e.ToString());
- }
- return peerList;
- }
+ if (httpResponse.StatusCode == HttpStatusCode.OK)
+ {
+ using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
+ {
+ var responseText = streamReader.ReadToEnd();
+ //Console.WriteLine(responseText);
+ List<ZeroTierPeer> peerList = null;
+ try
+ {
+ peerList = JsonConvert.DeserializeObject<List<ZeroTierPeer>>(responseText);
+ }
+ catch (JsonReaderException e)
+ {
+ Console.WriteLine(e.ToString());
+ }
+ cb(peerList);
+ }
+ }
+ else if (httpResponse.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
}
catch (System.Net.Sockets.SocketException)
{
- return null;
+ cb(null);
}
- catch (System.Net.WebException)
+ catch (System.Net.WebException e)
{
- return null;
+ HttpWebResponse res = (HttpWebResponse)e.Response;
+ if (res != null && res.StatusCode == HttpStatusCode.Unauthorized)
+ {
+ APIHandler.initHandler(true);
+ }
+ else
+ {
+ cb(null);
+ }
}
}
+
+ public string NodeAddress()
+ {
+ return ZeroTierAddress;
+ }
}
}
diff --git a/windows/WinUI/AboutView.xaml b/windows/WinUI/AboutView.xaml
new file mode 100644
index 00000000..39213b73
--- /dev/null
+++ b/windows/WinUI/AboutView.xaml
@@ -0,0 +1,35 @@
+<Window x:Class="WinUI.AboutView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="clr-namespace:WinUI"
+ mc:Ignorable="d"
+ Title="AboutView" Height="368.267" Width="300" Icon="ZeroTierIcon.ico">
+ <Grid>
+ <Image x:Name="image" HorizontalAlignment="Center" Height="100" Margin="0,10,0,0" VerticalAlignment="Top" Width="100" Source="ZeroTierIcon.ico"/>
+ <RichTextBox x:Name="richTextBox" HorizontalAlignment="Left" Height="209" Margin="10,123,0,0" VerticalAlignment="Top" Width="275" IsReadOnly="True" IsDocumentEnabled="True" BorderThickness="0" FontSize="18" RenderTransformOrigin="0.506,0.63">
+ <RichTextBox.Resources>
+ <Style TargetType="Hyperlink">
+ <Setter Property="Cursor" Value="Hand" />
+ </Style>
+ </RichTextBox.Resources>
+ <FlowDocument>
+ <Paragraph TextAlignment="Center">
+ <Run Text="ZeroTier One"/>
+ </Paragraph>
+ <Paragraph TextAlignment="Center">
+ <Run FontSize="14" Text="Version 1.2.6"/>
+ <LineBreak/>
+ <Run FontSize="14" Text="(c) 2011-2017 ZeroTier, Inc."/>
+ <LineBreak/>
+ <Run FontSize="14" Text="www.zerotier.com"/>
+ </Paragraph>
+ <Paragraph TextAlignment="Center">
+ <Run FontSize="14" Text="ZeroTier One allows your computer to join virtual networks. Just select &quot;join&quot; and enter a network's 16-digit ID. Each network appears on your computer as a new network port."/>
+ </Paragraph>
+ </FlowDocument>
+ </RichTextBox>
+
+ </Grid>
+</Window>
diff --git a/windows/WinUI/AboutView.xaml.cs b/windows/WinUI/AboutView.xaml.cs
new file mode 100644
index 00000000..9c48493d
--- /dev/null
+++ b/windows/WinUI/AboutView.xaml.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for AboutView.xaml
+ /// </summary>
+ public partial class AboutView : Window
+ {
+ public AboutView()
+ {
+ InitializeComponent();
+ }
+
+ private void Hyperlink_MouseLeftButtonDown(object sender, RequestNavigateEventArgs e)
+ {
+ var hyperlink = (Hyperlink)sender;
+ Process.Start(hyperlink.NavigateUri.ToString());
+ }
+ }
+}
diff --git a/windows/WinUI/App.xaml b/windows/WinUI/App.xaml
index 08b9b792..12ed85f9 100644
--- a/windows/WinUI/App.xaml
+++ b/windows/WinUI/App.xaml
@@ -1,7 +1,7 @@
<Application x:Class="WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- StartupUri="MainWindow.xaml">
+ StartupUri="ToolbarItem.xaml">
<Application.Resources>
<ResourceDictionary>
diff --git a/windows/WinUI/App.xaml.cs b/windows/WinUI/App.xaml.cs
index a97edde7..53ef2f67 100644
--- a/windows/WinUI/App.xaml.cs
+++ b/windows/WinUI/App.xaml.cs
@@ -5,6 +5,7 @@ using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
+using Hardcodet.Wpf.TaskbarNotification;
namespace WinUI
{
@@ -13,5 +14,12 @@ namespace WinUI
/// </summary>
public partial class App : Application
{
+ private TaskbarIcon tb;
+
+ private void InitApplication()
+ {
+ tb = (TaskbarIcon)FindResource("NotifyIcon");
+ tb.Visibility = Visibility.Visible;
+ }
}
}
diff --git a/windows/WinUI/CentralAPI.cs b/windows/WinUI/CentralAPI.cs
new file mode 100644
index 00000000..fc37aedf
--- /dev/null
+++ b/windows/WinUI/CentralAPI.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralAPI
+ {
+ private static volatile CentralAPI instance;
+ private static object syncRoot = new Object();
+
+ private CookieContainer cookieContainer;
+ private HttpClientHandler clientHandler;
+ private HttpClient client;
+
+ private CentralServer server;
+ public CentralServer Central
+ {
+ get
+ {
+ return this.server;
+ }
+ set
+ {
+ this.server = value;
+ WriteCentralConfig();
+ UpdateRequestHeaders();
+ }
+ }
+
+ public static CentralAPI Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncRoot)
+ {
+ if (instance == null)
+ {
+ instance = new CentralAPI();
+ }
+ }
+ }
+
+ return instance;
+ }
+ }
+
+
+
+ private CentralAPI()
+ {
+#if DEBUG
+ ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
+#endif
+ cookieContainer = new CookieContainer();
+ clientHandler = new HttpClientHandler
+ {
+ AllowAutoRedirect = true,
+ UseCookies = true,
+ CookieContainer = cookieContainer
+ };
+
+ client = new HttpClient(clientHandler);
+
+ string centralConfigPath = CentralConfigFile();
+ if (File.Exists(centralConfigPath))
+ {
+ byte[] tmp = File.ReadAllBytes(centralConfigPath);
+ string json = Encoding.UTF8.GetString(tmp).Trim();
+ Central = JsonConvert.DeserializeObject<CentralServer>(json);
+ }
+ else
+ {
+ Central = new CentralServer();
+ }
+ }
+
+ public bool HasAccessToken()
+ {
+ if (Central == null)
+ return false;
+
+ return !string.IsNullOrEmpty(Central.APIKey);
+ }
+
+ private string ZeroTierDir()
+ {
+ return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ }
+
+ private string CentralConfigFile()
+ {
+ return ZeroTierDir() + "\\central.conf";
+ }
+
+ public void WriteCentralConfig()
+ {
+ string json = JsonConvert.SerializeObject(Central);
+ byte[] tmp = Encoding.UTF8.GetBytes(json);
+ File.WriteAllBytes(CentralConfigFile(), tmp);
+ }
+
+ private void UpdateRequestHeaders()
+ {
+ if (client.DefaultRequestHeaders.Contains("Authorization"))
+ {
+ client.DefaultRequestHeaders.Remove("Authorization");
+ }
+
+ if (!string.IsNullOrEmpty(Central.APIKey))
+ {
+ client.DefaultRequestHeaders.Add("Authorization", "bearer " + Central.APIKey);
+ }
+ }
+
+ public async Task<bool> Login(string email, string password, bool isNewUser)
+ {
+ string postURL = Central.ServerURL + "/api/_auth/local";
+ CentralLogin login = new CentralLogin(email, password, isNewUser);
+ var content = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await client.PostAsync(postURL, content);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ return false;
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ CentralUser user = JsonConvert.DeserializeObject<CentralUser>(resContent);
+
+ if (user.Tokens.Count == 0)
+ {
+ // create token
+ user = await CreateAuthToken(user);
+ }
+
+ Central.APIKey = user.Tokens[0];
+
+ UpdateRequestHeaders();
+ WriteCentralConfig();
+
+ return true;
+ }
+
+ public async Task<CentralUser> CreateAuthToken(CentralUser user)
+ {
+ string randomTokenURL = Central.ServerURL + "/api/randomToken";
+ HttpResponseMessage response = await client.GetAsync(randomTokenURL);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ // TODO: throw an error
+ return null;
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ CentralToken t = JsonConvert.DeserializeObject<CentralToken>(resContent);
+
+ user.Tokens.Add(t.Token);
+
+ string tokenObj = "{ \"tokens\": " + JsonConvert.SerializeObject(user.Tokens) + " } ";
+
+ string postURL = Central.ServerURL + "/api/user/" + user.Id;
+ var postContent = new StringContent(tokenObj, Encoding.UTF8, "application/json");
+ response = await client.PostAsync(postURL, postContent);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ // TODO: thrown an error
+ return null;
+ }
+
+ resContent = await response.Content.ReadAsStringAsync();
+ user = JsonConvert.DeserializeObject<CentralUser>(resContent);
+
+ return user;
+ }
+
+ public async Task<List<CentralNetwork>> GetNetworkList()
+ {
+ string networkURL = Central.ServerURL + "/api/network";
+
+ HttpResponseMessage response = await client.GetAsync(networkURL);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ // TODO: Throw Error
+ return new List<CentralNetwork>();
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ List<CentralNetwork> networkList = JsonConvert.DeserializeObject<List<CentralNetwork>>(resContent);
+
+ return networkList;
+ }
+
+ public async Task<CentralNetwork> CreateNewNetwork()
+ {
+ string networkURL = Central.ServerURL + "/api/network/";
+ CentralNetwork network = new CentralNetwork();
+ network.Config = new CentralNetwork.CentralNetworkConfig();
+ network.Config.Name = NetworkNameGenerator.GenerateName();
+ string jsonNetwork = JsonConvert.SerializeObject(network);
+ var postContent = new StringContent(jsonNetwork, Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await client.PostAsync(networkURL, postContent);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ return null;
+ }
+
+ string resContent = await response.Content.ReadAsStringAsync();
+
+ CentralNetwork newNetwork = JsonConvert.DeserializeObject<CentralNetwork>(resContent);
+
+ return newNetwork;
+ }
+
+ public async Task<bool> AuthorizeNode(string nodeAddress, string networkId)
+ {
+ string json = "{ \"config\": { \"authorized\": true } }";
+ string postURL = Central.ServerURL + "/api/network/" + networkId + "/member/" + nodeAddress;
+ var postContent = new StringContent(json, Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await client.PostAsync(postURL, postContent);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/windows/WinUI/CentralLogin.cs b/windows/WinUI/CentralLogin.cs
new file mode 100644
index 00000000..97265dcf
--- /dev/null
+++ b/windows/WinUI/CentralLogin.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralLogin
+ {
+
+
+ public CentralLogin(string email, string password, bool isNew)
+ {
+ Login = email;
+ Password = password;
+ IsNew = isNew;
+ }
+
+ [JsonProperty("login")]
+ public string Login { get; set; }
+
+ [JsonProperty("password")]
+ public string Password { get; set; }
+
+ [JsonProperty("register")]
+ public bool IsNew { get; set; }
+ }
+}
diff --git a/windows/WinUI/CentralNetwork.cs b/windows/WinUI/CentralNetwork.cs
new file mode 100644
index 00000000..26ad5234
--- /dev/null
+++ b/windows/WinUI/CentralNetwork.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralNetwork
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+
+ [JsonProperty("rulesSource")]
+ public string RulesSource { get; set; }
+
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ [JsonProperty("ownerId")]
+ public string OwnerID { get; set; }
+
+ [JsonProperty("onlineMemberCount")]
+ public int OnlineMemberCount { get; set; }
+
+ [JsonProperty("config")]
+ public CentralNetworkConfig Config { get; set; }
+
+ public class CentralNetworkConfig
+ {
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("nwid")]
+ public string NetworkID { get; set; }
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ }
+ }
+}
diff --git a/windows/WinUI/CentralServer.cs b/windows/WinUI/CentralServer.cs
new file mode 100644
index 00000000..8e268653
--- /dev/null
+++ b/windows/WinUI/CentralServer.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralServer
+ {
+ public CentralServer()
+ {
+ ServerURL = "https://my.zerotier.com";
+ }
+
+ [JsonProperty("server_url")]
+ public string ServerURL { get; set; }
+
+ [JsonProperty("api_key")]
+ public string APIKey { get; set; }
+ }
+}
diff --git a/windows/WinUI/CentralToken.cs b/windows/WinUI/CentralToken.cs
new file mode 100644
index 00000000..1db548aa
--- /dev/null
+++ b/windows/WinUI/CentralToken.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralToken
+ {
+ [JsonProperty("token")]
+ public string Token { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+
+ [JsonProperty("raw")]
+ public string Raw { get; set; }
+ }
+}
diff --git a/windows/WinUI/CentralUser.cs b/windows/WinUI/CentralUser.cs
new file mode 100644
index 00000000..8a8945a2
--- /dev/null
+++ b/windows/WinUI/CentralUser.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ class CentralUser
+ {
+ public class CentralGlobalPermissions
+ {
+ [JsonProperty("a")]
+ public bool Administrator { get; set; }
+
+ [JsonProperty("d")]
+ public bool Delete { get; set; }
+
+ [JsonProperty("m")]
+ public bool Modify { get; set; }
+
+ [JsonProperty("r")]
+ public bool Read { get; set; }
+ }
+
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ [JsonProperty("clock")]
+ public UInt64 Clock { get; set; }
+
+ [JsonProperty("globalPermissions")]
+ public CentralGlobalPermissions GlobalPermissions { get; set; }
+
+ [JsonProperty("displayName")]
+ public string DisplayName { get; set; }
+
+ [JsonProperty("email")]
+ public string Email { get; set; }
+
+ [JsonProperty("smsNumber")]
+ public string SmsNumber { get; set; }
+
+ [JsonProperty("tokens")]
+ public List<string> Tokens { get; set; }
+ }
+}
diff --git a/windows/WinUI/ISwitchable.cs b/windows/WinUI/ISwitchable.cs
new file mode 100644
index 00000000..e485a14c
--- /dev/null
+++ b/windows/WinUI/ISwitchable.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ interface ISwitchable
+ {
+ void UtilizeState(object state);
+ }
+}
diff --git a/windows/WinUI/JoinNetworkView.xaml b/windows/WinUI/JoinNetworkView.xaml
new file mode 100644
index 00000000..1cd1e98d
--- /dev/null
+++ b/windows/WinUI/JoinNetworkView.xaml
@@ -0,0 +1,16 @@
+<Window x:Class="WinUI.JoinNetworkView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="clr-namespace:WinUI"
+ mc:Ignorable="d"
+ Title="Join a Network" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto" Icon="ZeroTierIcon.ico">
+ <Grid HorizontalAlignment="Left" Margin="0,0,0,0" Width="315">
+ <TextBox x:Name="joinNetworkBox" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="291" PreviewTextInput="joinNetworkBox_OnTextEntered" PreviewKeyDown="joinNetworkBox_OnKeyDown"/>
+ <CheckBox x:Name="allowManagedCheckbox" Content="Allow Managed" HorizontalAlignment="Left" Margin="10,38,0,0" VerticalAlignment="Top" IsChecked="True"/>
+ <CheckBox x:Name="allowGlobalCheckbox" Content="Allow Global" HorizontalAlignment="Left" Margin="118,38,0,0" VerticalAlignment="Top"/>
+ <CheckBox x:Name="allowDefaultCheckbox" Content="Allow Default" HorizontalAlignment="Left" Margin="210,38,-6,0" VerticalAlignment="Top"/>
+ <Button x:Name="joinButton" Content="Join" HorizontalAlignment="Left" Margin="226,58,0,10" Background="#FFFFB354" VerticalAlignment="Top" Width="75" Click="joinButton_Click" IsEnabled="False"/>
+ </Grid>
+</Window>
diff --git a/windows/WinUI/JoinNetworkView.xaml.cs b/windows/WinUI/JoinNetworkView.xaml.cs
new file mode 100644
index 00000000..0b4ac324
--- /dev/null
+++ b/windows/WinUI/JoinNetworkView.xaml.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for JoinNetworkView.xaml
+ /// </summary>
+ public partial class JoinNetworkView : Window
+ {
+ Regex charRegex = new Regex("[0-9a-fxA-FX]");
+ Regex wholeStringRegex = new Regex("^[0-9a-fxA-FX]+$");
+
+ public JoinNetworkView()
+ {
+ InitializeComponent();
+
+ DataObject.AddPastingHandler(joinNetworkBox, onPaste);
+ DataObject.AddCopyingHandler(joinNetworkBox, onCopyCut);
+ }
+
+ private void joinNetworkBox_OnTextEntered(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = !charRegex.IsMatch(e.Text);
+
+ if ( (joinNetworkBox.Text.Length + e.Text.Length) == 16)
+ {
+ joinButton.IsEnabled = true;
+ }
+ else
+ {
+ joinButton.IsEnabled = false;
+ }
+ }
+
+ private void joinNetworkBox_OnKeyDown(object sender, KeyEventArgs e)
+ {
+ if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+ {
+ if (e.Key == Key.X && joinNetworkBox.IsSelectionActive)
+ {
+ // handle ctrl-x removing characters
+ joinButton.IsEnabled = false;
+ }
+ }
+ else if (e.Key == Key.Delete || e.Key == Key.Back)
+ {
+ if ((joinNetworkBox.Text.Length - 1) == 16)
+ {
+ joinButton.IsEnabled = true;
+ }
+ else
+ {
+ joinButton.IsEnabled = false;
+ }
+ }
+ else
+ {
+ if ((joinNetworkBox.Text.Length + 1) > 16)
+ {
+ e.Handled = true;
+ }
+ }
+ }
+
+ private void onPaste(object sender, DataObjectPastingEventArgs e)
+ {
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
+ if (!isText)
+ {
+ joinButton.IsEnabled = false;
+ return;
+ }
+
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
+
+ if (!wholeStringRegex.IsMatch(text))
+ {
+ e.Handled = true;
+ e.CancelCommand();
+ }
+
+ if (text.Length == 16 || (joinNetworkBox.Text.Length + text.Length) == 16)
+ {
+ joinButton.IsEnabled = true;
+ }
+ else if (text.Length > 16 || (joinNetworkBox.Text.Length + text.Length) > 16)
+ {
+ e.Handled = true;
+ e.CancelCommand();
+ }
+ else
+ {
+ joinButton.IsEnabled = false;
+ }
+ }
+
+ private void onCopyCut(object sender, DataObjectCopyingEventArgs e)
+ {
+
+ }
+
+ private void joinButton_Click(object sender, RoutedEventArgs e)
+ {
+ bool allowDefault = allowDefaultCheckbox.IsChecked.Value;
+ bool allowGlobal = allowGlobalCheckbox.IsChecked.Value;
+ bool allowManaged = allowManagedCheckbox.IsChecked.Value;
+
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, joinNetworkBox.Text, allowManaged, allowGlobal, allowDefault);
+
+ Close();
+ }
+ }
+}
diff --git a/windows/WinUI/MainWindow.xaml.cs b/windows/WinUI/MainWindow.xaml.cs
index 47f00bfe..e4a82f96 100644
--- a/windows/WinUI/MainWindow.xaml.cs
+++ b/windows/WinUI/MainWindow.xaml.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@@ -57,40 +58,77 @@ namespace WinUI
}
}
- private bool InitAPIHandler()
+
+ private String readAuthToken(String path)
{
- String ztDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\ZeroTier\\One";
String authToken = "";
+
+ if (File.Exists(path))
+ {
+ try
+ {
+ byte[] tmp = File.ReadAllBytes(path);
+ authToken = System.Text.Encoding.UTF8.GetString(tmp).Trim();
+ }
+ catch
+ {
+ MessageBox.Show("Unable to read ZeroTier One Auth Token from:\r\n" + path, "ZeroTier One");
+ }
+ }
+
+ return authToken;
+ }
+
+ private Int32 readPort(String path)
+ {
Int32 port = 9993;
+
try
{
- byte[] tmp = File.ReadAllBytes(ztDir + "\\authtoken.secret");
- authToken = System.Text.Encoding.ASCII.GetString(tmp).Trim();
+ byte[] tmp = File.ReadAllBytes(path);
+ port = Int32.Parse(System.Text.Encoding.ASCII.GetString(tmp).Trim());
+ if ((port <= 0) || (port > 65535))
+ port = 9993;
}
catch
{
- MessageBox.Show("Unable to read ZeroTier One authtoken.secret from:\r\n" + ztDir, "ZeroTier One");
- this.Close();
- return false;
}
+ return port;
+ }
+
+ private bool InitAPIHandler()
+ {
+ String localZtDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String globalZtDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\ZeroTier\\One";
+
+ String authToken = "";
+ Int32 port = 9993;
+
+ if (!File.Exists(localZtDir + "\\authtoken.secret") || !File.Exists(localZtDir + "\\zerotier-one.port"))
+ {
+ // launch external process to copy file into place
+ String curPath = System.Reflection.Assembly.GetEntryAssembly().Location;
+ int index = curPath.LastIndexOf("\\");
+ curPath = curPath.Substring(0, index);
+ ProcessStartInfo startInfo = new ProcessStartInfo(curPath + "\\copyutil.exe", globalZtDir + " " + localZtDir);
+ startInfo.Verb = "runas";
+
+
+ var process = Process.Start(startInfo);
+ process.WaitForExit();
+ }
+
+ authToken = readAuthToken(localZtDir + "\\authtoken.secret");
+
if ((authToken == null) || (authToken.Length <= 0))
{
- MessageBox.Show("Unable to read ZeroTier One authtoken.secret from:\r\n" + ztDir, "ZeroTier One");
+ MessageBox.Show("Unable to read ZeroTier One authtoken", "ZeroTier One");
this.Close();
return false;
}
- try
- {
- byte[] tmp = File.ReadAllBytes(ztDir + "\\zerotier-one.port");
- port = Int32.Parse(System.Text.Encoding.ASCII.GetString(tmp).Trim());
- if ((port <= 0) || (port > 65535))
- port = 9993;
- }
- catch
- {
- }
+ port = readPort(localZtDir + "\\zerotier-one.port");
handler = new APIHandler(port, authToken);
return true;
}
@@ -105,7 +143,7 @@ namespace WinUI
networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
- this.networkId.Content = status.Address;
+ this.networkId.Text = status.Address;
}));
versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
@@ -122,7 +160,7 @@ namespace WinUI
networkId.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
- this.networkId.Content = "";
+ this.networkId.Text = "";
}));
versionString.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
diff --git a/windows/WinUI/NetworkInfoView.xaml b/windows/WinUI/NetworkInfoView.xaml
index 54ff0375..6b32569e 100644
--- a/windows/WinUI/NetworkInfoView.xaml
+++ b/windows/WinUI/NetworkInfoView.xaml
@@ -26,6 +26,9 @@
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
+ <RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3">
@@ -48,8 +51,11 @@
<TextBlock TextWrapping="Wrap" Text="Bridging" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="7" Foreground="#FF000000"/>
<TextBlock TextWrapping="Wrap" Text="Device" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="8" Foreground="#FF000000"/>
<TextBlock TextWrapping="Wrap" Text="Managed IPs" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="9" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Allow Global IP" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="10" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Allow Managed IP" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="11" Foreground="#FF000000"/>
+ <TextBlock TextWrapping="Wrap" Text="Allow Default Route" HorizontalAlignment="Right" Grid.Column="0" Grid.Row="12" Foreground="#FF000000"/>
- <Rectangle Grid.Column="2" Grid.Row="2" Grid.RowSpan="8" Fill="#FFEEEEEE"/>
+ <Rectangle Grid.Column="2" Grid.Row="2" Grid.RowSpan="11" Fill="#FFEEEEEE"/>
<TextBlock x:Name="networkStatus" FontFamily="Lucida Console" TextWrapping="Wrap" HorizontalAlignment="Right" Text="OK" TextAlignment="Right" Grid.Column="2" Grid.Row="2" Foreground="#FF000000"/>
<TextBlock x:Name="networkType" FontFamily="Lucida Console" TextWrapping="Wrap" Text="PUBLIC" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="3" Foreground="#FF000000"/>
@@ -58,15 +64,21 @@
<TextBlock x:Name="broadcastEnabled" FontFamily="Lucida Console" TextWrapping="Wrap" Text="ENABLED" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="6" Foreground="#FF000000"/>
<TextBlock x:Name="bridgingEnabled" FontFamily="Lucida Console" TextWrapping="Wrap" Text="DISABLED" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="7" Background="#FFEEEEEE" Foreground="#FF000000"/>
<TextBlock x:Name="deviceName" FontFamily="Lucida Console" TextWrapping="Wrap" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="8" Foreground="#FF000000"><Span><Run Text="ethernet_32771"/></Span></TextBlock>
- <TextBlock x:Name="managedIps" TextWrapping="Wrap" FontFamily="Lucida Console" HorizontalAlignment="Right" TextAlignment="Right" Grid.Column="2" Grid.Row="9" Foreground="#FF000000"><Span><Run Text="28.2.169.248/7 "/></Span><LineBreak/><Span><Run Text="fd80:56c2:e21c:0000:0199:9383:4a02:a9f8/88"/></Span></TextBlock>
-
- <Separator Grid.Column="0" Grid.Row="10" Grid.ColumnSpan="3"/>
+ <TextBox x:Name="managedIps" TextWrapping="Wrap" FontFamily="Lucida Console" HorizontalAlignment="Right" TextAlignment="Right" Grid.Column="2" Grid.Row="9" Foreground="#FF000000" IsReadOnly="True" BorderThickness="0" Background="#FFEEEEEE" Text="28.2.169.248/7&#x0a;fd80:56c2:e21c:0000:0199:9383:4a02:a9f8/88"/>
+ <CheckBox x:Name="allowGlobal" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="10" />
+ <CheckBox x:Name="allowManaged" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="11" />
+ <CheckBox x:Name="allowDefault" HorizontalAlignment="Right" Grid.Column="2" Grid.Row="12" />
- <Grid Grid.Column="0" Grid.Row="11" Grid.ColumnSpan="3" Background="GhostWhite">
+ <Separator Grid.Column="0" Grid.Row="13" Grid.ColumnSpan="3"/>
+
+ <Grid Grid.Column="0" Grid.Row="14" Grid.ColumnSpan="3" Background="GhostWhite">
<Grid.ColumnDefinitions>
+ <ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
- <Button x:Name="leaveButton" Content="Leave" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="75" Background="#FFFFB354" Click="leaveButton_Click"/>
+ <Button x:Name="deleteButton" Grid.Column="0" Content="Delete" HorizontalAlignment="Left" VerticalAlignment="Center" Width="75" Background="#FFFFB354" Click="deleteButton_Click"/>
+ <CheckBox x:Name="connectedCheckBox" Grid.Column="2" Content="Connected" HorizontalAlignment="Right" VerticalAlignment="Center" Checked="connectedCheckBox_Checked" Unchecked="connectedCheckbox_Unchecked"/>
</Grid>
</Grid>
</Border>
diff --git a/windows/WinUI/NetworkInfoView.xaml.cs b/windows/WinUI/NetworkInfoView.xaml.cs
index ccdec288..51f76753 100644
--- a/windows/WinUI/NetworkInfoView.xaml.cs
+++ b/windows/WinUI/NetworkInfoView.xaml.cs
@@ -20,30 +20,50 @@ namespace WinUI
/// </summary>
public partial class NetworkInfoView : UserControl
{
- private APIHandler handler;
- private ZeroTierNetwork network;
+ public ZeroTierNetwork network;
- public NetworkInfoView(APIHandler handler, ZeroTierNetwork network)
+ public NetworkInfoView(ZeroTierNetwork network)
{
InitializeComponent();
- this.handler = handler;
this.network = network;
UpdateNetworkData();
+
+ allowDefault.Checked += AllowDefault_CheckStateChanged;
+ allowDefault.Unchecked += AllowDefault_CheckStateChanged;
+ allowGlobal.Checked += AllowGlobal_CheckStateChanged;
+ allowGlobal.Unchecked += AllowGlobal_CheckStateChanged;
+ allowManaged.Checked += AllowManaged_CheckStateChanged;
+ allowManaged.Unchecked += AllowManaged_CheckStateChanged;
}
private void UpdateNetworkData()
{
- this.networkId.Text = network.NetworkId;
- this.networkName.Text = network.NetworkName;
- this.networkStatus.Text = network.NetworkStatus;
- this.networkType.Text = network.NetworkType;
- this.macAddress.Text = network.MacAddress;
- this.mtu.Text = network.MTU.ToString();
+
+ if (this.networkId.Text != network.NetworkId)
+ this.networkId.Text = network.NetworkId;
+
+ if (this.networkName.Text != network.NetworkName)
+ this.networkName.Text = network.NetworkName;
+
+ if (this.networkStatus.Text != network.NetworkStatus)
+ this.networkStatus.Text = network.NetworkStatus;
+
+ if (this.networkType.Text != network.NetworkType)
+ this.networkType.Text = network.NetworkType;
+
+ if (this.macAddress.Text != network.MacAddress)
+ this.macAddress.Text = network.MacAddress;
+
+ if (this.mtu.Text != network.MTU.ToString())
+ this.mtu.Text = network.MTU.ToString();
+
this.broadcastEnabled.Text = (network.BroadcastEnabled ? "ENABLED" : "DISABLED");
this.bridgingEnabled.Text = (network.Bridge ? "ENABLED" : "DISABLED");
- this.deviceName.Text = network.DeviceName;
+
+ if (this.deviceName.Text != network.DeviceName)
+ this.deviceName.Text = network.DeviceName;
string iplist = "";
for (int i = 0; i < network.AssignedAddresses.Length; ++i)
@@ -53,8 +73,21 @@ namespace WinUI
iplist += "\n";
}
- this.managedIps.Text = iplist;
- }
+ if (this.managedIps.Text != iplist)
+ this.managedIps.Text = iplist;
+
+ this.allowDefault.IsChecked = network.AllowDefault;
+ this.allowGlobal.IsChecked = network.AllowGlobal;
+ this.allowManaged.IsChecked = network.AllowManaged;
+
+ this.connectedCheckBox.Checked -= connectedCheckBox_Checked;
+ this.connectedCheckBox.Unchecked -= connectedCheckbox_Unchecked;
+
+ this.connectedCheckBox.IsChecked = network.IsConnected;
+
+ this.connectedCheckBox.Checked += connectedCheckBox_Checked;
+ this.connectedCheckBox.Unchecked += connectedCheckbox_Unchecked;
+ }
public bool HasNetwork(ZeroTierNetwork network)
{
@@ -64,9 +97,70 @@ namespace WinUI
return false;
}
- private void leaveButton_Click(object sender, RoutedEventArgs e)
+ public void SetNetworkInfo(ZeroTierNetwork network)
{
- handler.LeaveNetwork(network.NetworkId);
+ this.network = network;
+
+ UpdateNetworkData();
+ }
+
+ private void deleteButton_Click(object sender, RoutedEventArgs e)
+ {
+ APIHandler.Instance.LeaveNetwork(this.Dispatcher, network.NetworkId);
+ NetworkMonitor.Instance.RemoveNetwork(network.NetworkId);
+ }
+
+ private void AllowManaged_CheckStateChanged(object sender, RoutedEventArgs e)
+ {
+ CheckBox cb = sender as CheckBox;
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, network.NetworkId,
+ allowManaged.IsChecked ?? false,
+ allowGlobal.IsChecked ?? false,
+ allowDefault.IsChecked ?? false);
+ }
+
+ private void AllowGlobal_CheckStateChanged(object sender, RoutedEventArgs e)
+ {
+ CheckBox cb = sender as CheckBox;
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, network.NetworkId,
+ allowManaged.IsChecked ?? false,
+ allowGlobal.IsChecked ?? false,
+ allowDefault.IsChecked ?? false);
+ }
+
+ private void AllowDefault_CheckStateChanged(object sender, RoutedEventArgs e)
+ {
+ CheckBox cb = sender as CheckBox;
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, network.NetworkId,
+ allowManaged.IsChecked ?? false,
+ allowGlobal.IsChecked ?? false,
+ allowDefault.IsChecked ?? false);
+ }
+
+ private void connectedCheckBox_Checked(object sender, RoutedEventArgs e)
+ {
+ onConnectedCheckboxUpdated(true);
+ }
+
+ private void connectedCheckbox_Unchecked(object sender, RoutedEventArgs e)
+ {
+ onConnectedCheckboxUpdated(false);
+ }
+
+ private void onConnectedCheckboxUpdated(bool isChecked)
+ {
+ if (isChecked)
+ {
+ bool global = allowGlobal.IsChecked.Value;
+ bool managed = allowManaged.IsChecked.Value;
+ bool defRoute = allowDefault.IsChecked.Value;
+
+ APIHandler.Instance.JoinNetwork(this.Dispatcher, networkId.Text, managed, global, defRoute);
+ }
+ else
+ {
+ APIHandler.Instance.LeaveNetwork(this.Dispatcher, networkId.Text);
+ }
}
}
}
diff --git a/windows/WinUI/MainWindow.xaml b/windows/WinUI/NetworkListView.xaml
index d71a90df..1dc774b7 100644
--- a/windows/WinUI/MainWindow.xaml
+++ b/windows/WinUI/NetworkListView.xaml
@@ -4,8 +4,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WinUI"
- mc:Ignorable="d" x:Class="WinUI.MainWindow"
- Title="ZeroTier One" Height="500" Width="425" Icon="ZeroTierIcon.ico">
+ mc:Ignorable="d" x:Class="WinUI.NetworkListView"
+ Title="ZeroTier One" SizeToContent="Width" Height="500" Width="Auto" Icon="ZeroTierIcon.ico">
<Window.Resources>
<SolidColorBrush x:Key="GreenBrush" Color="#ff91a2a3"/>
@@ -75,58 +75,14 @@
</Window.Resources>
<DockPanel>
- <StatusBar DockPanel.Dock="Bottom" Height="26" Background="#FF234447" Margin="0">
- <StatusBar.ItemsPanel>
- <ItemsPanelTemplate>
- <Grid>
- <Grid.RowDefinitions>
- <RowDefinition Height="*"/>
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="Auto"/>
- <ColumnDefinition Width="Auto"/>
- <ColumnDefinition Width="Auto"/>
- <ColumnDefinition Width="*"/>
- <ColumnDefinition Width="Auto"/>
- <ColumnDefinition Width="Auto"/>
- </Grid.ColumnDefinitions>
- </Grid>
- </ItemsPanelTemplate>
- </StatusBar.ItemsPanel>
- <StatusBarItem Grid.Column="0" x:Name="networkId" Content="deadbeef00" Foreground="White" FontFamily="Lucida Console"/>
- <StatusBarItem Grid.Column="1" x:Name="onlineStatus" Content="ONLINE" Foreground="White" FontFamily="Lucida Console"/>
- <StatusBarItem Grid.Column="2" x:Name="versionString" Content="1.0.5" Foreground="White" FontFamily="Lucida Console"/>
- <StatusBarItem Grid.Column="3" x:Name="blank" Content="" Height="43" Foreground="White"/>
- <StatusBarItem Grid.Column="4">
- <TextBox x:Name="joinNetworkID" TextWrapping="Wrap" Width="140" HorizontalAlignment="Right" ToolTip="Enter Network ID" PreviewTextInput="OnNetworkEntered" MaxLength="16" FontFamily="Lucida Console" FontSize="12" BorderThickness="1"/>
- </StatusBarItem>
- <StatusBarItem Grid.Column="5" x:Name="statusBarButton" Foreground="White" RenderTransformOrigin="0.789,0.442">
- <Button x:Name="joinButton" Content="Join" Background="#FFFFB354" Width="76" Click="joinButton_Click"/>
- </StatusBarItem>
- </StatusBar>
- <!--<TabControl Margin="0,0,0,0">
- <TabItem x:Name="Networks" Header="Networks" Foreground="White" IsSelected="True" IsManipulationEnabled="True">-->
- <Grid Background="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="*"/>
- </Grid.ColumnDefinitions>
- <Grid.RowDefinitions>
- <RowDefinition Height="*"/>
- </Grid.RowDefinitions>
- <local:NetworksPage x:Name="networksPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Grid.Row="0" Margin="0,0,0,0"/>
- </Grid>
- <!--</TabItem>-->
- <!--<TabItem x:Name="Peers" Header="Peers" Foreground="White">
- <Grid Background="#FFE5E5E5" HorizontalAlignment="Left" VerticalAlignment="Top">
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="*"/>
- </Grid.ColumnDefinitions>
- <Grid.RowDefinitions>
- <RowDefinition Height="*"/>
- </Grid.RowDefinitions>
- <local:PeersPage x:Name="peersPage" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="0" Grid.Row="0"/>
- </Grid>
- </TabItem>-->
- <!--</TabControl>-->
+ <Grid Background="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="*"/>
+ </Grid.RowDefinitions>
+ <local:NetworksPage x:Name="networksPage" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Column="0" Grid.Row="0" Margin="0,0,0,0"/>
+ </Grid>
</DockPanel>
</Window>
diff --git a/windows/WinUI/NetworkListView.xaml.cs b/windows/WinUI/NetworkListView.xaml.cs
new file mode 100644
index 00000000..26c40255
--- /dev/null
+++ b/windows/WinUI/NetworkListView.xaml.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Timers;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using System.ComponentModel;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for MainWindow.xaml
+ /// </summary>
+ public partial class NetworkListView : Window
+ {
+ Regex charRegex = new Regex("[0-9a-fxA-FX]");
+ Regex wholeStringRegex = new Regex("^[0-9a-fxA-FX]+$");
+
+ public NetworkListView()
+ {
+ InitializeComponent();
+
+ Closed += onClosed;
+
+ NetworkMonitor.Instance.SubscribeNetworkUpdates(updateNetworks);
+ }
+
+ ~NetworkListView()
+ {
+ }
+
+ protected override void OnClosing(CancelEventArgs e)
+ {
+ e.Cancel = true;
+ Hide();
+ }
+
+ private void onClosed(object sender, System.EventArgs e)
+ {
+ NetworkMonitor.Instance.UnsubscribeNetworkUpdates(updateNetworks);
+ }
+
+ private void updateNetworks(List<ZeroTierNetwork> networks)
+ {
+ if (networks != null)
+ {
+ networksPage.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ networksPage.setNetworks(networks);
+ }));
+ }
+ }
+
+ private void OnNetworkEntered(object sender, TextCompositionEventArgs e)
+ {
+ e.Handled = !charRegex.IsMatch(e.Text);
+ }
+
+ private void OnPaste(object sender, DataObjectPastingEventArgs e)
+ {
+ var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
+ if (!isText) return;
+
+ var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
+
+ if (!wholeStringRegex.IsMatch(text))
+ {
+ e.CancelCommand();
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/NetworkMonitor.cs b/windows/WinUI/NetworkMonitor.cs
new file mode 100644
index 00000000..ce722e45
--- /dev/null
+++ b/windows/WinUI/NetworkMonitor.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ class NetworkMonitor
+ {
+ public delegate void NetworkListCallback(List<ZeroTierNetwork> networks);
+ public delegate void StatusCallback(ZeroTierStatus status);
+
+ private Thread runThread;
+ private NetworkListCallback _nwCb;
+ private StatusCallback _stCb;
+
+
+ private List<ZeroTierNetwork> _knownNetworks = new List<ZeroTierNetwork>();
+
+ private static NetworkMonitor instance;
+ private static object syncRoot = new object();
+
+ public static NetworkMonitor Instance
+ {
+ get
+ {
+ if (instance == null)
+ {
+ lock (syncRoot)
+ {
+ if (instance == null)
+ {
+ instance = new NetworkMonitor();
+ }
+ }
+ }
+
+ return instance;
+ }
+ }
+
+ private NetworkMonitor()
+ {
+ runThread = new Thread(new ThreadStart(run));
+ loadNetworks();
+
+ runThread.Start();
+ }
+
+ ~NetworkMonitor()
+ {
+ runThread.Interrupt();
+ }
+
+ private void loadNetworks()
+ {
+ String dataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String dataFile = Path.Combine(dataPath, "networks.dat");
+
+ if (File.Exists(dataFile))
+ {
+ List<ZeroTierNetwork> netList;
+
+ using (Stream stream = File.Open(dataFile, FileMode.Open))
+ {
+ var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
+ netList = (List<ZeroTierNetwork>)bformatter.Deserialize(stream);
+ stream.Close();
+ }
+
+ lock (_knownNetworks)
+ {
+ _knownNetworks = netList;
+ }
+ }
+ }
+
+ private void writeNetworks()
+ {
+ String dataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
+ String dataFile = Path.Combine(dataPath, "networks.dat");
+
+ if (!Directory.Exists(dataPath))
+ {
+ Directory.CreateDirectory(dataPath);
+ }
+
+ using (Stream stream = File.Open(dataFile, FileMode.OpenOrCreate))
+ {
+ lock (_knownNetworks)
+ {
+ var bformatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
+ bformatter.Serialize(stream, _knownNetworks);
+ stream.Flush();
+ stream.Close();
+ }
+ }
+ }
+
+ private void apiNetworkCallback(List<ZeroTierNetwork> networks)
+ {
+ if (networks == null)
+ {
+ return;
+ }
+
+ lock (_knownNetworks)
+ {
+ _knownNetworks = _knownNetworks.Union(networks, new NetworkEqualityComparer()).ToList();
+
+ foreach (ZeroTierNetwork n in _knownNetworks)
+ {
+ if (networks.Contains(n))
+ {
+ n.IsConnected = true;
+ }
+ else
+ {
+ n.IsConnected = false;
+ }
+ }
+
+ _knownNetworks.Sort();
+ _nwCb(_knownNetworks);
+ }
+
+ writeNetworks();
+ }
+
+ private void apiStatusCallback(ZeroTierStatus status)
+ {
+ _stCb(status);
+ }
+
+ private void run()
+ {
+ try
+ {
+ while (runThread.IsAlive)
+ {
+ APIHandler handler = APIHandler.Instance;
+
+ if (handler != null)
+ {
+ handler.GetNetworks(apiNetworkCallback);
+ handler.GetStatus(apiStatusCallback);
+ }
+
+ Thread.Sleep(2000);
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Monitor Thread Exception: " + "\n" + e.StackTrace);
+ }
+ Console.WriteLine("Monitor Thread Ended");
+ }
+
+ public void SubscribeStatusUpdates(StatusCallback cb)
+ {
+ _stCb += cb;
+ }
+
+ public void UnsubscribeStatusUpdates(StatusCallback cb)
+ {
+ _stCb -= cb;
+ }
+
+ public void SubscribeNetworkUpdates(NetworkListCallback cb)
+ {
+ _nwCb += cb;
+ }
+
+ public void UnsubscribeNetworkUpdates(NetworkListCallback cb)
+ {
+ _nwCb -= cb;
+ }
+
+ public void RemoveNetwork(String networkID)
+ {
+ lock(_knownNetworks)
+ {
+ foreach (ZeroTierNetwork n in _knownNetworks)
+ {
+ if (n.NetworkId.Equals(networkID))
+ {
+ _knownNetworks.Remove(n);
+ writeNetworks();
+ break;
+ }
+ }
+ }
+ }
+
+ public void StopMonitor()
+ {
+ runThread.Abort();
+ }
+ }
+}
diff --git a/windows/WinUI/NetworkNameGenerator.cs b/windows/WinUI/NetworkNameGenerator.cs
new file mode 100644
index 00000000..8a75936d
--- /dev/null
+++ b/windows/WinUI/NetworkNameGenerator.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ class NetworkNameGenerator
+ {
+ public static string GenerateName()
+ {
+ Random r = new Random(DateTime.Now.Millisecond);
+ int firstIndex = r.Next(0, FIRST.Length);
+ int secondIndex = r.Next(0, SECOND.Length);
+ return FIRST[firstIndex] + "_" + SECOND[secondIndex];
+ }
+
+ private static string[] FIRST =
+ {
+ "admiring",
+ "adoring",
+ "agitated",
+ "amazing",
+ "angry",
+ "awesome",
+ "berserk",
+ "big",
+ "clever",
+ "compassionate",
+ "cranky",
+ "crazy",
+ "desperate",
+ "determined",
+ "distracted",
+ "dreamy",
+ "ecstatic",
+ "elated",
+ "elegant",
+ "fervent",
+ "focused",
+ "furious",
+ "gigantic",
+ "gloomy",
+ "goofy",
+ "grave",
+ "happy",
+ "high",
+ "hopeful",
+ "hungry",
+ "insane",
+ "jolly",
+ "jovial",
+ "lonely",
+ "loving",
+ "modest",
+ "nostalgic",
+ "pedantic",
+ "pensive",
+ "prickly",
+ "reverent",
+ "romantic",
+ "sad",
+ "serene",
+ "sharp",
+ "silly",
+ "sleepy",
+ "stoic",
+ "stupefied",
+ "suspicious",
+ "tender",
+ "thirsty",
+ "tiny",
+ "trusting"
+ };
+
+ private static string[] SECOND =
+ {
+ // constructed telephone-like devices in 1854
+ "meucci",
+
+ // prototype make-or-break telephones in 1860
+ "reis",
+
+ // Alexander Graham Bell
+ "bell",
+
+ // designed telephone using water microphone in 1876
+ "gray",
+
+ // Tivadar Puskás invented the telephone switchboard exchange in 1876.
+ "puskas",
+
+ // Thomas Edison, invented the carbon microphone which produced a strong telephone signal.
+ "edison",
+
+ // 1950s, Paul Baran developed the concept Distributed Adaptive Message Block Switching
+ "baran",
+
+ // Donald Davies coined the phrase 'packet switching network'
+ "davies",
+
+ // Robert Licklider helped get ARPANET funded
+ "licklider",
+
+ // Robert Taylor, ARPANET pioneer
+ "taylor",
+
+ // Lawrence Roberts, ARPANET
+ "roberts",
+
+ // Vint Cerf, TCP
+ "cerf",
+
+ // Bob Kahn, TCP
+ "kahn",
+
+ // David P Reed, UDP
+ "reed",
+
+ // Community Memory was created by Efrem Lipkin, Mark Szpakowski, and Lee Felsenstein, acting as The Community Memory Project within the Resource One computer center at Project One in San Francisco.
+ "lipkin",
+ "szpakowski",
+ "felsenstein",
+
+ // The first public dial-up BBS was developed by Ward Christensen and Randy Suess.
+ "christensen",
+ "suess",
+
+ // Joybubbles (May 25, 1949 – August 8, 2007), born Josef Carl Engressia, Jr. in Richmond, Virginia, USA, was an early phone phreak.
+ "engressia",
+ "joybubbles",
+
+ // John Thomas Draper (born 1943), also known as Captain Crunch, Crunch or Crunchman (after Cap'n Crunch breakfast cereal mascot), is an American computer programmer and former phone phreak
+ "draper",
+
+ // Dennis C. Hayes, founder of Hayes Microcomputer Products
+ // "The Modem of Dennis Hayes and Dale Heatherington."
+ "hayes",
+ "heatherington",
+
+ // "Ethernet was developed at Xerox PARC between 1973 and 1974.[7][8] It was inspired by ALOHAnet, which Robert Metcalfe had studied as part of his PhD dissertation."
+ "metcalfe",
+
+ // William Bradford Shockley Jr. (February 13, 1910 – August 12, 1989) was an American physicist and inventor. Shockley was the manager of a research group that included John Bardeen and Walter Brattain. The three scientists invented the point contact transistor in 1947
+ "shockley",
+ "bardeen",
+ "brattain",
+
+ // "Randall Erck invented the modern modem as we know it today. There were devices similar to modems used by the military, but they were designed more for the purpose of sending encripted nuclear launch codes to various bases around the world."
+ "erck",
+
+ // Leonard Kleinrock, packet switching network pioneer
+ "kleinrock",
+
+ // Tim Berners-Lee, WWW
+ "berners_lee",
+
+ // Steve Wozniak, early phone phreak
+ "wozniak",
+
+ // James Fields Smathers of Kansas City invented what is considered the first practical power-operated typewriter in 1914.
+ "smathers",
+
+ // The teleprinter evolved through a series of inventions by a number of engineers, including Royal Earl House, David Edward Hughes, Emile Baudot, Donald Murray, Charles L. Krum, Edward Kleinschmidt and Frederick G. Creed.
+ "house",
+ "hughes",
+ "baudot",
+ "murray",
+ "krum",
+ "kleinschmidt",
+ "creed",
+
+ // Ron Rosenbaum, author of "Secrets of the Little Blue Box" which mainstreamed phone phreaking
+ "rosenbaum",
+
+ // Bram Cohen. Bram Cohen (born October 12, 1975) is an American computer programmer, best known as the author of the peer-to-peer (P2P) BitTorrent protocol,
+ "cohen",
+
+ // Jarkko Oikarinen (born 16 August 1967, in Kuusamo, Finland) is the inventor of the first Internet chat network, called Internet Relay Chat (IRC), where he is known as WiZ.
+ "oikarinen",
+
+ // "What you probably didn't know is that the author of Trumpet Winsock — Peter Tattam from Tasmania, Australia — didn't see much money for his efforts."
+ "tattam",
+
+ // Satoshi Nakamoto
+ "nakamoto",
+
+ // Philo Farnsworth, inventor of the first practical TV tube
+ "farnsworth",
+
+ // Scottish inventor John Logie Baird employed the Nipkow disk in his prototype video systems. On 25 March 1925, Baird gave the first public demonstration of televised silhouette images in motion, at Selfridge's Department Store in London.
+ "baird",
+
+ // Beginning in 1836, the American artist Samuel F. B. Morse, the American physicist Joseph Henry, and Alfred Vail developed an electrical telegraph system.
+ "morse",
+ "henry",
+ "vail"
+ };
+ }
+}
diff --git a/windows/WinUI/NetworkRoute.cs b/windows/WinUI/NetworkRoute.cs
new file mode 100644
index 00000000..ce26ef7d
--- /dev/null
+++ b/windows/WinUI/NetworkRoute.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace WinUI
+{
+ [Serializable]
+ public class NetworkRoute : ISerializable
+ {
+ protected NetworkRoute(SerializationInfo info, StreamingContext ctx)
+ {
+ Target = info.GetString("target");
+ Via = info.GetString("via");
+ Flags = info.GetInt32("flags");
+ Metric = info.GetInt32("metric");
+ }
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext ctx)
+ {
+ info.AddValue("target", Target);
+ info.AddValue("via", Via);
+ info.AddValue("flags", Flags);
+ info.AddValue("metric", Metric);
+ }
+
+ [JsonProperty("target")]
+ public string Target { get; set; }
+
+ [JsonProperty("via")]
+ public string Via { get; set; }
+
+ [JsonProperty("flags")]
+ public int Flags { get; set; }
+
+ [JsonProperty("metric")]
+ public int Metric { get; set; }
+ }
+}
diff --git a/windows/WinUI/NetworksPage.xaml.cs b/windows/WinUI/NetworksPage.xaml.cs
index 5a0dc19d..39a2fefc 100644
--- a/windows/WinUI/NetworksPage.xaml.cs
+++ b/windows/WinUI/NetworksPage.xaml.cs
@@ -20,33 +20,80 @@ namespace WinUI
/// </summary>
public partial class NetworksPage : UserControl
{
- private APIHandler handler;
-
public NetworksPage()
{
InitializeComponent();
}
- public void SetAPIHandler(APIHandler handler)
- {
- this.handler = handler;
- }
-
public void setNetworks(List<ZeroTierNetwork> networks)
{
- this.wrapPanel.Children.Clear();
if (networks == null)
{
+ this.wrapPanel.Children.Clear();
return;
}
- for (int i = 0; i < networks.Count; ++i)
+ foreach (ZeroTierNetwork network in networks)
+ {
+ NetworkInfoView view = ChildWithNetwork(network);
+ if (view != null)
+ {
+ view.SetNetworkInfo(network);
+ }
+ else
+ {
+ wrapPanel.Children.Add(
+ new NetworkInfoView(
+ network));
+ }
+ }
+
+ // remove networks we're no longer joined to.
+ List<ZeroTierNetwork> tmpList = GetNetworksFromChildren();
+ foreach (ZeroTierNetwork n in networks)
+ {
+ if (tmpList.Contains(n))
+ {
+ tmpList.Remove(n);
+ }
+ }
+
+ foreach (ZeroTierNetwork n in tmpList)
{
- this.wrapPanel.Children.Add(
- new NetworkInfoView(
- handler,
- networks.ElementAt<ZeroTierNetwork>(i)));
+ NetworkInfoView view = ChildWithNetwork(n);
+ if (view != null)
+ {
+ wrapPanel.Children.Remove(view);
+ }
}
}
+
+ private NetworkInfoView ChildWithNetwork(ZeroTierNetwork network)
+ {
+ List<NetworkInfoView> list = wrapPanel.Children.OfType<NetworkInfoView>().ToList();
+
+ foreach (NetworkInfoView view in list)
+ {
+ if (view.HasNetwork(network))
+ {
+ return view;
+ }
+ }
+
+ return null;
+ }
+
+ private List<ZeroTierNetwork> GetNetworksFromChildren()
+ {
+ List<ZeroTierNetwork> networks = new List<ZeroTierNetwork>(wrapPanel.Children.Count);
+
+ List<NetworkInfoView> list = wrapPanel.Children.OfType<NetworkInfoView>().ToList();
+ foreach (NetworkInfoView n in list)
+ {
+ networks.Add(n.network);
+ }
+
+ return networks;
+ }
}
}
diff --git a/windows/WinUI/OnboardProcess/CreateAccount.xaml b/windows/WinUI/OnboardProcess/CreateAccount.xaml
new file mode 100644
index 00000000..ddf50252
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateAccount.xaml
@@ -0,0 +1,38 @@
+<UserControl x:Class="WinUI.OnboardProcess.CreateAccount"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+
+ <Label Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="10">Email Address:</Label>
+ <TextBox x:Name="EmailAddressTextBox" Grid.Column="1" Grid.Row="0" Width="150" Margin="10"></TextBox>
+
+ <Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="10">Password:</Label>
+ <PasswordBox x:Name="PasswordTextBox1" Grid.Column="1" Grid.Row="1" PasswordChar="*" Margin="10"/>
+ <Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="10">Repeat Password:</Label>
+ <PasswordBox x:Name="PasswordTextBox2" Grid.Column="1" Grid.Row="2" PasswordChar="*" Margin="10"/>
+
+ <Button Grid.Column="1" Grid.Row="3" Click="CreateAccount_Click" Margin="10" Content="Create Account" Background="#FFFFB354" Width="90" HorizontalAlignment="Right"/>
+
+ <Label x:Name="ErrorText" Grid.Row="4" Grid.ColumnSpan="2" HorizontalAlignment="Center"></Label>
+ <Label Grid.Row="5"></Label>
+ <Button Grid.Column="0" Grid.Row="6" Background="#FFFFB354" Click="BackButton_Click">Go Back</Button>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/CreateAccount.xaml.cs b/windows/WinUI/OnboardProcess/CreateAccount.xaml.cs
new file mode 100644
index 00000000..72ba2182
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateAccount.xaml.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for CreateAccount.xaml
+ /// </summary>
+ public partial class CreateAccount : UserControl, ISwitchable
+ {
+ public CreateAccount()
+ {
+ InitializeComponent();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void CreateAccount_Click(object sender, RoutedEventArgs e)
+ {
+ DoCreateAccount();
+ }
+
+ public void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new RegisterOrLogIn());
+ }
+
+ public async void DoCreateAccount()
+ {
+ if (PasswordTextBox1.Password.ToString() != PasswordTextBox2.Password.ToString())
+ {
+ ErrorText.Content = "Passwords do not match!";
+ }
+ else
+ {
+ CentralAPI api = CentralAPI.Instance;
+ bool accountCreated = await api.Login(EmailAddressTextBox.Text,
+ PasswordTextBox1.Password.ToString(), true);
+
+ if (accountCreated)
+ {
+ Switcher.Switch(new CreateOrJoin());
+ }
+ else
+ {
+ ErrorText.Content = "An error ocurred while creating your account.";
+ }
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/CreateOrJoin.xaml b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml
new file mode 100644
index 00000000..21413e84
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml
@@ -0,0 +1,49 @@
+<UserControl x:Class="WinUI.OnboardProcess.CreateOrJoin"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="400">
+ <Grid HorizontalAlignment="Stretch" Margin="15">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+
+ <Button Grid.Column="1" x:Name="CreateButton" Content="Create a Network" Background="#FFFFB354" Click="OnCreateButtonClick"/>
+
+ <Label Grid.Column="1" Grid.Row="1" Content="Or" HorizontalAlignment="Center"/>
+
+ <Label Grid.Column="1" Grid.Row="2" Content="Join a Network:" HorizontalAlignment="Center"/>
+
+ <ListBox Grid.ColumnSpan="3" Grid.Column="0" Grid.Row="3" Name="listViewDataBinding" HorizontalContentAlignment="Stretch">
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <Grid HorizontalAlignment="Stretch">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <TextBlock Grid.Column="0" Grid.Row="0" Margin="5" HorizontalAlignment="Left" Text="{Binding Id}"/>
+ <TextBlock Grid.Column="1" Grid.Row="0" Margin="5" HorizontalAlignment="Center" Text="{Binding Config.Name}"/>
+ <Button Grid.Column="2" Grid.Row="0" Margin="5" Content="Join" HorizontalAlignment="Right" Background="#FFFFB354" Tag="{Binding Id}" Click="OnJoinButtonClick"/>
+ </Grid>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs
new file mode 100644
index 00000000..91330540
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/CreateOrJoin.xaml.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for CreateOrJoin.xaml
+ /// </summary>
+ public partial class CreateOrJoin : UserControl, ISwitchable
+ {
+ private List<CentralNetwork> networkList = new List<CentralNetwork>();
+
+ public CreateOrJoin()
+ {
+ InitializeComponent();
+ listViewDataBinding.ItemsSource = networkList;
+
+ GetAvailableNetworks();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ private async void GetAvailableNetworks()
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ List<CentralNetwork> networks = await api.GetNetworkList();
+
+ foreach (CentralNetwork n in networks)
+ {
+ networkList.Add(n);
+ }
+
+ listViewDataBinding.Items.Refresh();
+ }
+
+ public void OnJoinButtonClick(object sender, RoutedEventArgs e)
+ {
+ Button button = sender as Button;
+ string networkId = button.Tag as string;
+
+ APIHandler handler = APIHandler.Instance;
+
+ handler.JoinNetwork(this.Dispatcher, networkId);
+
+ AuthorizeNetworkMember(networkId);
+ }
+
+ public void OnCreateButtonClick(object sender, RoutedEventArgs e)
+ {
+ CreateNewNetwork();
+ }
+
+ private async void CreateNewNetwork()
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ CentralNetwork newNetwork = await api.CreateNewNetwork();
+
+ APIHandler handler = APIHandler.Instance;
+
+ handler.JoinNetwork(this.Dispatcher, newNetwork.Id);
+
+ AuthorizeNetworkMember(newNetwork.Id);
+ }
+
+ private async void AuthorizeNetworkMember(string networkId)
+ {
+ string nodeId = APIHandler.Instance.NodeAddress();
+
+ bool authorized = await CentralAPI.Instance.AuthorizeNode(nodeId, networkId);
+
+ if (authorized)
+ {
+ Switcher.Switch(new Finished());
+ }
+ else
+ {
+
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/EnterToken.xaml b/windows/WinUI/OnboardProcess/EnterToken.xaml
new file mode 100644
index 00000000..1880167d
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/EnterToken.xaml
@@ -0,0 +1,29 @@
+<UserControl x:Class="WinUI.OnboardProcess.EnterToken"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="*"/>
+ </Grid.ColumnDefinitions>
+ <TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="10" TextWrapping="Wrap" HorizontalAlignment="Center" Text="Your API Token can be found or created on https://my.zerotier.com"/>
+ <TextBlock Grid.Column="0" Grid.Row="1" Text="API Token:" Margin="10"/>
+ <TextBox x:Name="APITokenInput" Grid.Column="1" Grid.Row="1" Margin="10"/>
+
+ <Button Grid.Column="1" Grid.Row="2" Background="#FFFFB354" Content="Next" HorizontalAlignment="Right" Click="Next_Click" Margin="10"/>
+ <Label Grid.Row="3"/>
+ <Button Grid.Column="0" Grid.Row="4" Background="#FFFFB354" Content="Go Back" Click="BackButton_Click" />
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/EnterToken.xaml.cs b/windows/WinUI/OnboardProcess/EnterToken.xaml.cs
new file mode 100644
index 00000000..5d2cc515
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/EnterToken.xaml.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for EnterToken.xaml
+ /// </summary>
+ public partial class EnterToken : UserControl, ISwitchable
+ {
+ public EnterToken()
+ {
+ InitializeComponent();
+
+ if (!string.IsNullOrEmpty(CentralAPI.Instance.Central.APIKey))
+ {
+ APITokenInput.Text = CentralAPI.Instance.Central.APIKey;
+ }
+ }
+
+ public void UtilizeState(object staqte)
+ {
+
+ }
+
+ private void Next_Click(object sender, RoutedEventArgs e)
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ if (api.Central.APIKey != APITokenInput.Text)
+ {
+ CentralServer server = new CentralServer();
+ server.APIKey = APITokenInput.Text;
+ api.Central = server;
+ }
+
+ Switcher.Switch(new CreateOrJoin());
+ }
+
+ private void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new RegisterOrLogIn());
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/Finished.xaml b/windows/WinUI/OnboardProcess/Finished.xaml
new file mode 100644
index 00000000..d29f536c
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/Finished.xaml
@@ -0,0 +1,27 @@
+<UserControl x:Class="WinUI.OnboardProcess.Finished"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto" />
+ </Grid.ColumnDefinitions>
+
+ <TextBlock Grid.Column="0" Grid.Row="0" Text="All Finished!" HorizontalAlignment="Center"/>
+ <TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" Text=""/>
+ <TextBlock Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="2" MaxWidth="270" HorizontalAlignment="Center" Margin="10" Text="You've now joined your first ZeroTier network. Now go to add your other machines!" TextWrapping="Wrap"/>
+ <TextBlock Grid.Column="0" Grid.Row="3" HorizontalAlignment="Center" Text=""/>
+ <Button Grid.Column="0" Grid.Row="4" MaxWidth="100" Click="DoneButton_Click" Background="#FFFFB354" Margin="10">Done!</Button>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/Finished.xaml.cs b/windows/WinUI/OnboardProcess/Finished.xaml.cs
new file mode 100644
index 00000000..a34abfe2
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/Finished.xaml.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for Finished.xaml
+ /// </summary>
+ public partial class Finished : UserControl, ISwitchable
+ {
+ public Finished()
+ {
+ InitializeComponent();
+ }
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void DoneButton_Click(object sender, RoutedEventArgs e)
+ {
+ Window.GetWindow(this).Close();
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/LogIn.xaml b/windows/WinUI/OnboardProcess/LogIn.xaml
new file mode 100644
index 00000000..501f7e0f
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/LogIn.xaml
@@ -0,0 +1,35 @@
+<UserControl x:Class="WinUI.OnboardProcess.LogIn"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10">
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="*" />
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+
+ <Label Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="10">Email Address:</Label>
+ <TextBox x:Name="EmailAddressTextBox" Grid.Column="1" Grid.Row="0" MinWidth="150" Margin="10"></TextBox>
+
+ <Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="10">Password:</Label>
+ <PasswordBox x:Name="PasswordTextBox" Grid.Column="1" Grid.Row="1" PasswordChar="*" Margin="10"/>
+
+ <Button Grid.Column="1" Grid.Row="2" Click="LoginButton_Click" Content="LogIn" Background="#FFFFB354" Width="90" Margin="10" HorizontalAlignment="Right"/>
+
+ <Label x:Name="ErrorText" Grid.Row="3" Grid.ColumnSpan="2" HorizontalAlignment="Center"></Label>
+ <Label Grid.Row="4"></Label>
+ <Button Grid.Column="0" Grid.Row="5" Background="#FFFFB354" Click="BackButton_Click">Go Back</Button>
+ </Grid>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/LogIn.xaml.cs b/windows/WinUI/OnboardProcess/LogIn.xaml.cs
new file mode 100644
index 00000000..8657b800
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/LogIn.xaml.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for LogIn.xaml
+ /// </summary>
+ public partial class LogIn : UserControl, ISwitchable
+ {
+ public LogIn()
+ {
+ InitializeComponent();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void LoginButton_Click(object sender, RoutedEventArgs e)
+ {
+ DoLogin();
+ }
+
+ public void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new RegisterOrLogIn());
+ }
+
+ private async void DoLogin()
+ {
+ CentralAPI api = CentralAPI.Instance;
+ bool didLogIn = await api.Login(EmailAddressTextBox.Text, PasswordTextBox.Password.ToString(), false);
+ if (didLogIn)
+ {
+ Switcher.Switch(new CreateOrJoin());
+ }
+ else
+ {
+ ErrorText.Content = "Invalid username or password";
+ }
+ }
+ }
+}
diff --git a/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml
new file mode 100644
index 00000000..01f3ba9d
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml
@@ -0,0 +1,27 @@
+<UserControl x:Class="WinUI.OnboardProcess.RegisterOrLogIn"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:local="clr-namespace:WinUI.OnboardProcess"
+ mc:Ignorable="d"
+ d:DesignHeight="300" d:DesignWidth="300">
+ <StackPanel>
+ <TextBlock HorizontalAlignment="Center" FontSize="20" FontWeight="Bold" FontFamily="Segoe UI">Welcome to ZeroTier</TextBlock>
+ <TextBlock HorizontalAlignment="Center" FontSize="16">Let's get started!</TextBlock>
+ <TextBlock HorizontalAlignment="Center"> </TextBlock>
+ <TextBlock HorizontalAlignment="Center" TextWrapping="Wrap">If you haven't yet created an account, click "Create Account" below. If you have an account, you can log in with your username/password, or simply enter an API key.</TextBlock>
+ <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
+ <StackPanel.Resources>
+ <Style TargetType="{x:Type Button}">
+ <Setter Property="Margin" Value="10,10,0,0"/>
+ </Style>
+ </StackPanel.Resources>
+ <Button x:Name="CreateAccountButton" Margin="10,5" Content="Create Account" Background="#FFFFB354" Click="CreateAccountButton_Click"/>
+
+ <Button x:Name="LogInButton" Margin="10,5" Content="Log In" Background="#FFFFB354" Click="LogInButton_Click"/>
+
+ <Button x:Name="TokenButton" Margin="10,5" Content="API Token" Background="#FFFFB354" Click="APIToken_Click"/>
+ </StackPanel>
+ </StackPanel>
+</UserControl>
diff --git a/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs
new file mode 100644
index 00000000..19c578dd
--- /dev/null
+++ b/windows/WinUI/OnboardProcess/RegisterOrLogIn.xaml.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace WinUI.OnboardProcess
+{
+ /// <summary>
+ /// Interaction logic for RegisterOrLogIn.xaml
+ /// </summary>
+ public partial class RegisterOrLogIn : UserControl, ISwitchable
+ {
+ public RegisterOrLogIn()
+ {
+ InitializeComponent();
+ }
+
+ public void UtilizeState(object state)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void CreateAccountButton_Click(object sender, System.Windows.RoutedEventArgs e)
+ {
+ Switcher.Switch(new CreateAccount());
+ }
+
+ private void LogInButton_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new LogIn());
+ }
+
+ public void APIToken_Click(object sender, RoutedEventArgs e)
+ {
+ Switcher.Switch(new EnterToken());
+ }
+ }
+}
diff --git a/windows/WinUI/PageSwitcher.xaml b/windows/WinUI/PageSwitcher.xaml
new file mode 100644
index 00000000..2e463e7a
--- /dev/null
+++ b/windows/WinUI/PageSwitcher.xaml
@@ -0,0 +1,13 @@
+<Window x:Class="WinUI.PageSwitcher"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="clr-namespace:WinUI"
+ mc:Ignorable="d"
+ Icon="ZeroTierIcon.ico"
+ Title="ZeroTier One" Height="300" Width="400">
+ <Grid>
+
+ </Grid>
+</Window>
diff --git a/windows/WinUI/PageSwitcher.xaml.cs b/windows/WinUI/PageSwitcher.xaml.cs
new file mode 100644
index 00000000..c9aa659f
--- /dev/null
+++ b/windows/WinUI/PageSwitcher.xaml.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for PageSwitcher.xaml
+ /// </summary>
+ public partial class PageSwitcher : Window
+ {
+ public PageSwitcher()
+ {
+ InitializeComponent();
+ Switcher.pageSwitcher = this;
+
+ CentralAPI api = CentralAPI.Instance;
+
+ if (api.HasAccessToken())
+ {
+ Switcher.Switch(new OnboardProcess.CreateOrJoin());
+ }
+ else
+ {
+ Switcher.Switch(new OnboardProcess.RegisterOrLogIn());
+ }
+ }
+
+ public void Navigate(UserControl nextPage)
+ {
+ this.Content = nextPage;
+ }
+
+ public void Navigate(UserControl nextPage, object state)
+ {
+ this.Content = nextPage;
+ ISwitchable s = nextPage as ISwitchable;
+
+ if (s != null)
+ s.UtilizeState(state);
+ else
+ throw new ArgumentException("NextPage is not ISwitchable! "
+ + nextPage.Name.ToString());
+ }
+ }
+}
diff --git a/windows/WinUI/PreferencesView.xaml b/windows/WinUI/PreferencesView.xaml
new file mode 100644
index 00000000..fd170493
--- /dev/null
+++ b/windows/WinUI/PreferencesView.xaml
@@ -0,0 +1,30 @@
+<Window x:Class="WinUI.PreferencesView"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="clr-namespace:WinUI"
+ mc:Ignorable="d"
+ Title="PreferencesView" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto" Icon="ZeroTierIcon.ico">
+ <Grid>
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="Auto"/>
+ <ColumnDefinition Width="Auto"/>
+ </Grid.ColumnDefinitions>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ <RowDefinition Height="Auto"/>
+ </Grid.RowDefinitions>
+ <CheckBox x:Name="startupCheckbox" Content="Launch ZeroTier On Startup" HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"/>
+
+ <TextBlock Text="Central Instance:" Grid.Row="1" Grid.Column="0" Margin="10"/>
+ <TextBox x:Name="CentralInstanceTextBox" Grid.Row="1" Grid.Column="1" MinWidth="200" Margin="10"/>
+
+ <TextBlock Text="API Key:" Grid.Row="2" Grid.Column="0" Margin="10"/>
+ <TextBox x:Name="APIKeyTextBox" Grid.Row="2" Grid.Column="1" MinWidth="200" Margin="10"/>
+
+ <Button x:Name="OKButton" Grid.Row="3" Grid.Column="1" Background="#FFFFB354" Content="OK" Margin="10" Width="90" HorizontalAlignment="Right" Click="OKButton_Clicked"/>
+ </Grid>
+</Window>
diff --git a/windows/WinUI/PreferencesView.xaml.cs b/windows/WinUI/PreferencesView.xaml.cs
new file mode 100644
index 00000000..1a5ed756
--- /dev/null
+++ b/windows/WinUI/PreferencesView.xaml.cs
@@ -0,0 +1,74 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for PreferencesView.xaml
+ /// </summary>
+ public partial class PreferencesView : Window
+ {
+ public static string AppName = "ZeroTier One";
+ private RegistryKey rk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
+ private string AppLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
+ public PreferencesView()
+ {
+ InitializeComponent();
+
+
+ string keyValue = rk.GetValue(AppName) as string;
+
+ if (keyValue != null && keyValue.Equals(AppLocation))
+ {
+ startupCheckbox.IsChecked = true;
+ }
+
+ CentralAPI api = CentralAPI.Instance;
+ CentralInstanceTextBox.Text = api.Central.ServerURL;
+ APIKeyTextBox.Text = api.Central.APIKey;
+ }
+
+ private void OKButton_Clicked(object sender, RoutedEventArgs e)
+ {
+ CentralAPI api = CentralAPI.Instance;
+
+ if (api.Central.ServerURL != CentralInstanceTextBox.Text ||
+ api.Central.APIKey != APIKeyTextBox.Text)
+ {
+ CentralServer newServer = new CentralServer();
+ newServer.ServerURL = CentralInstanceTextBox.Text;
+ newServer.APIKey = APIKeyTextBox.Text;
+
+ api.Central = newServer;
+ }
+
+ if (startupCheckbox.IsChecked.HasValue && (bool)startupCheckbox.IsChecked)
+ {
+ rk.SetValue(AppName, AppLocation);
+ }
+ else
+ {
+ string keyValue = rk.GetValue(AppName) as string;
+
+ if (keyValue != null && keyValue.Equals(AppLocation))
+ {
+ rk.DeleteValue(AppName);
+ }
+ }
+
+ Close();
+ }
+ }
+}
diff --git a/windows/WinUI/Properties/Resources.Designer.cs b/windows/WinUI/Properties/Resources.Designer.cs
index 57a56f7d..3e5c78ae 100644
--- a/windows/WinUI/Properties/Resources.Designer.cs
+++ b/windows/WinUI/Properties/Resources.Designer.cs
@@ -8,10 +8,10 @@
// </auto-generated>
//------------------------------------------------------------------------------
-namespace WinUI.Properties
-{
-
-
+namespace WinUI.Properties {
+ using System;
+
+
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@@ -22,50 +22,52 @@ namespace WinUI.Properties
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources
- {
-
+ internal class Resources {
+
private static global::System.Resources.ResourceManager resourceMan;
-
+
private static global::System.Globalization.CultureInfo resourceCulture;
-
+
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources()
- {
+ internal Resources() {
}
-
+
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager
- {
- get
- {
- if ((resourceMan == null))
- {
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinUI.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
-
+
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture
- {
- get
- {
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
return resourceCulture;
}
- set
- {
+ set {
resourceCulture = value;
}
}
+
+ /// <summary>
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ /// </summary>
+ internal static System.Drawing.Icon ZeroTierIcon {
+ get {
+ object obj = ResourceManager.GetObject("ZeroTierIcon", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
}
}
diff --git a/windows/WinUI/Properties/Resources.resx b/windows/WinUI/Properties/Resources.resx
index af7dbebb..0872fceb 100644
--- a/windows/WinUI/Properties/Resources.resx
+++ b/windows/WinUI/Properties/Resources.resx
@@ -46,7 +46,7 @@
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
- : System.Serialization.Formatters.Binary.BinaryFormatter
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
@@ -60,6 +60,7 @@
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
@@ -68,9 +69,10 @@
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" />
+ <xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
@@ -85,9 +87,10 @@
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
@@ -109,9 +112,13 @@
<value>2.0</value>
</resheader>
<resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="ZeroTierIcon" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>..\ZeroTierIcon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+ </data>
</root> \ No newline at end of file
diff --git a/windows/WinUI/Resources/ZeroTierIcon.ico b/windows/WinUI/Resources/ZeroTierIcon.ico
new file mode 100644
index 00000000..5d06b9f2
--- /dev/null
+++ b/windows/WinUI/Resources/ZeroTierIcon.ico
Binary files differ
diff --git a/windows/WinUI/Simple Styles.xaml b/windows/WinUI/Simple Styles.xaml
index f2ddedf9..a03348b3 100644
--- a/windows/WinUI/Simple Styles.xaml
+++ b/windows/WinUI/Simple Styles.xaml
@@ -1,4 +1,9 @@
-<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/interactivedesigner/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
+<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/interactivedesigner/2006"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d"
+ xmlns:tb="http://www.hardcodet.net/taskbar">
<!-- SimpleStyles.XAML defines a set of control styles which are simplified starting points for creating your own controls -->
@@ -1118,4 +1123,6 @@
</Setter.Value>
</Setter>
</Style>
+
+ <tb:TaskbarIcon x:Key="NotifyIcon" IconSource="ZeroTierIcon.ico" ToolTipText="ZeroTier One"/>
</ResourceDictionary>
diff --git a/windows/WinUI/Switcher.cs b/windows/WinUI/Switcher.cs
new file mode 100644
index 00000000..f48835df
--- /dev/null
+++ b/windows/WinUI/Switcher.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Threading.Tasks;
+
+namespace WinUI
+{
+ public static class Switcher
+ {
+ public static PageSwitcher pageSwitcher;
+
+ public static void Switch(UserControl newPage)
+ {
+ pageSwitcher.Navigate(newPage);
+ }
+
+ public static void Switch(UserControl newPage, object state)
+ {
+ pageSwitcher.Navigate(newPage, state);
+ }
+ }
+}
diff --git a/windows/WinUI/Themes/Generic.xaml b/windows/WinUI/Themes/Generic.xaml
index 2b47588d..689f0740 100644
--- a/windows/WinUI/Themes/Generic.xaml
+++ b/windows/WinUI/Themes/Generic.xaml
@@ -3,5 +3,4 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinUI">
-
</ResourceDictionary>
diff --git a/windows/WinUI/ToolbarItem.xaml b/windows/WinUI/ToolbarItem.xaml
new file mode 100644
index 00000000..85e4122a
--- /dev/null
+++ b/windows/WinUI/ToolbarItem.xaml
@@ -0,0 +1,62 @@
+<Window x:Class="WinUI.ToolbarItem"
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:local="clr-namespace:WinUI"
+ xmlns:tb="http://www.hardcodet.net/taskbar"
+ xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
+ mc:Ignorable="d"
+ Height="300" Width="300" Visibility="Hidden" Name="Toolbar">
+
+ <Window.Resources>
+ <CollectionViewSource Source="{Binding ElementName=Toolbar, Path=NetworkCollection}" x:Key="KnownNetworks">
+ <CollectionViewSource.SortDescriptions>
+ <scm:SortDescription PropertyName="Header" Direction="Ascending"/>
+ </CollectionViewSource.SortDescriptions>
+ </CollectionViewSource>
+ </Window.Resources>
+
+ <Grid>
+ <tb:TaskbarIcon x:Name="MyNotifyIcon"
+ IconSource="ZeroTierIcon.ico"
+ ToolTipText="ZeroTier One"
+ MenuActivation="LeftOrRightClick">
+ <tb:TaskbarIcon.ContextMenu>
+ <ContextMenu>
+ <ContextMenu.ItemsSource>
+ <CompositeCollection>
+ <MenuItem Header="Node ID: unknown"
+ Click="ToolbarItem_NodeIDClicked"
+ x:Name="nodeIdMenuItem"/>
+ <Separator/>
+ <MenuItem Header="Join Network..."
+ Click="ToolbarItem_JoinNetworkClicked"/>
+ <MenuItem Header="Show Networks..."
+ Click="ToolbarItem_ShowNetworksClicked"/>
+ <Separator/>
+
+ <CollectionContainer Collection="{Binding Source={StaticResource KnownNetworks}}">
+
+ </CollectionContainer>
+
+ <Separator/>
+ <MenuItem Header="ZeroTier Central"
+ Click="ToolbarItem_CentralClicked"/>
+
+ <MenuItem Header="About..."
+ Click="ToolbarItem_AboutClicked"/>
+ <MenuItem Header="Preferences..."
+ Click="ToolbarItem_PreferencesClicked"/>
+ <Separator/>
+ <MenuItem Header="Quit"
+ Click="ToolbarItem_QuitClicked"/>
+
+ </CompositeCollection>
+ </ContextMenu.ItemsSource>
+ </ContextMenu>
+ </tb:TaskbarIcon.ContextMenu>
+
+ </tb:TaskbarIcon>
+ </Grid>
+</Window>
diff --git a/windows/WinUI/ToolbarItem.xaml.cs b/windows/WinUI/ToolbarItem.xaml.cs
new file mode 100644
index 00000000..c16de3c3
--- /dev/null
+++ b/windows/WinUI/ToolbarItem.xaml.cs
@@ -0,0 +1,362 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Net.Http;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+using System.Text.RegularExpressions;
+using System.Timers;
+using System.Windows.Threading;
+using System.IO;
+using System.Diagnostics;
+using Microsoft.Win32;
+
+namespace WinUI
+{
+ /// <summary>
+ /// Interaction logic for ToolbarItem.xaml
+ /// </summary>
+ public partial class ToolbarItem : Window, INotifyPropertyChanged
+ {
+ private APIHandler handler = APIHandler.Instance;
+
+ private Point netListLocation = new Point(0, 0);
+ private Point joinNetLocation = new Point(0, 0);
+ private Point aboutViewLocation = new Point(0, 0);
+ private Point prefsViewLocation = new Point(0, 0);
+
+ private NetworkListView netListView = new NetworkListView();
+ private JoinNetworkView joinNetView = null;
+ private AboutView aboutView = null;
+ private PreferencesView prefsView = null;
+
+ private NetworkMonitor mon = NetworkMonitor.Instance;
+
+ private ObservableCollection<MenuItem> _networkCollection = new ObservableCollection<MenuItem>();
+
+ private static Boolean shouldShowOnboardProcess = true;
+#if DEBUG
+ private static bool isFirstRun = true;
+#endif
+
+ public ObservableCollection<MenuItem> NetworkCollection
+ {
+ get { return _networkCollection; }
+ set { _networkCollection = value; }
+ }
+
+ private string nodeId;
+
+ public ToolbarItem()
+ {
+ InitializeComponent();
+
+ mon.SubscribeNetworkUpdates(updateNetworks);
+ mon.SubscribeStatusUpdates(updateStatus);
+
+ SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
+ }
+
+ ~ToolbarItem()
+ {
+ mon.UnsubscribeNetworkUpdates(updateNetworks);
+ mon.UnsubscribeStatusUpdates(updateStatus);
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ private void updateNetworks(List<ZeroTierNetwork> networks)
+ {
+ if (networks != null)
+ {
+ if (networks.Count > 0)
+ {
+#if DEBUG
+ if (isFirstRun)
+ {
+ shouldShowOnboardProcess = true;
+ isFirstRun = false;
+ }
+ else
+ {
+ shouldShowOnboardProcess = false;
+ }
+#else
+ shouldShowOnboardProcess = false;
+#endif
+ }
+
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ NetworkCollection.Clear();
+ foreach (ZeroTierNetwork n in networks)
+ {
+ MenuItem item = new MenuItem();
+ item.Header = n.Title.Replace("_", "__");
+ item.DataContext = n;
+ item.IsChecked = n.IsConnected;
+ item.Click += ToolbarItem_NetworkClicked;
+
+ NetworkCollection.Add(item);
+ }
+ }));
+
+ if (shouldShowOnboardProcess)
+ {
+ // TODO: Show onboarding process window (on main thread
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ PageSwitcher ps = new PageSwitcher();
+ ps.Show();
+ }));
+
+ shouldShowOnboardProcess = false;
+ }
+ }
+ }
+
+ private void updateStatus(ZeroTierStatus status)
+ {
+ if (status != null)
+ {
+ Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
+ {
+ nodeIdMenuItem.Header = "Node ID: " + status.Address;
+ nodeIdMenuItem.IsEnabled = true;
+ nodeId = status.Address;
+ }));
+ }
+ }
+
+ private void ToolbarItem_NodeIDClicked(object sender, System.Windows.RoutedEventArgs e)
+ {
+ try
+ {
+ Clipboard.SetDataObject(nodeId);
+ }
+ catch (ArgumentNullException)
+ {
+ // tried to copy a null nodeId
+ Console.WriteLine("ArgumentNullException");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.ToString());
+ }
+ }
+
+ private void ToolbarItem_ShowNetworksClicked(object sender, System.Windows.RoutedEventArgs e)
+ {
+ if (netListView == null)
+ {
+ netListView = new WinUI.NetworkListView();
+ netListView.Closed += ShowNetworksClosed;
+ }
+
+ bool netListNeedsMoving = true;
+ if (netListLocation.X > 0 && netListLocation.Y > 0)
+ {
+ netListView.Left = netListLocation.X;
+ netListView.Top = netListLocation.Y;
+ netListNeedsMoving = false;
+ }
+
+ netListView.Show();
+
+ if (netListNeedsMoving)
+ {
+ setWindowPosition(netListView);
+ netListLocation.X = netListView.Left;
+ netListLocation.Y = netListView.Top;
+ }
+
+ netListView.Activate();
+ }
+
+ private void ShowNetworksClosed(object sender, System.EventArgs e)
+ {
+ netListView = null;
+ }
+
+ private void ToolbarItem_JoinNetworkClicked(object sender, System.EventArgs e)
+ {
+ if (joinNetView == null)
+ {
+ joinNetView = new JoinNetworkView();
+ joinNetView.Closed += JoinNetworkClosed;
+
+ bool needsMove = true;
+ if (joinNetLocation.X > 0 && joinNetLocation.Y > 0)
+ {
+ joinNetView.Left = joinNetLocation.X;
+ joinNetView.Top = joinNetLocation.Y;
+ needsMove = false;
+ }
+
+ joinNetView.Show();
+
+ if (needsMove)
+ {
+ setWindowPosition(joinNetView);
+ joinNetLocation.X = joinNetView.Left;
+ joinNetLocation.Y = joinNetView.Top;
+ }
+ }
+ else
+ {
+ joinNetView.Activate();
+ }
+ }
+
+ private void JoinNetworkClosed(object sender, System.EventArgs e)
+ {
+ joinNetView = null;
+ }
+
+ private void ToolbarItem_CentralClicked(object sender, System.EventArgs e)
+ {
+ Process.Start("https://my.zerotier.com");
+ }
+
+ private void ToolbarItem_AboutClicked(object sender, System.EventArgs e)
+ {
+ if (aboutView == null)
+ {
+ aboutView = new AboutView();
+ aboutView.Closed += AboutClosed;
+
+ bool needsMove = true;
+ if (aboutViewLocation.X > 0 && aboutViewLocation.Y > 0)
+ {
+ aboutView.Left = aboutViewLocation.X;
+ aboutView.Top = aboutViewLocation.Y;
+ needsMove = false;
+ }
+
+ aboutView.Show();
+
+ if (needsMove)
+ {
+ setWindowPosition(aboutView);
+ aboutViewLocation.X = aboutView.Left;
+ aboutViewLocation.Y = aboutView.Top;
+ }
+ }
+ else
+ {
+ aboutView.Activate();
+ }
+ }
+
+ private void AboutClosed(object sender, System.EventArgs e)
+ {
+ aboutView = null;
+ }
+
+ private void ToolbarItem_PreferencesClicked(object sender, System.EventArgs e)
+ {
+ if (prefsView == null)
+ {
+ prefsView = new PreferencesView();
+ prefsView.Closed += PreferencesClosed;
+
+ bool needsMove = true;
+ if (prefsViewLocation.X > 0 && prefsViewLocation.Y > 0)
+ {
+ prefsView.Left = prefsViewLocation.X;
+ prefsView.Top = prefsViewLocation.Y;
+ needsMove = false;
+ }
+
+ prefsView.Show();
+
+ if (needsMove)
+ {
+ setWindowPosition(prefsView);
+ prefsViewLocation.X = prefsView.Left;
+ prefsViewLocation.Y = prefsView.Top;
+ }
+ }
+ else
+ {
+ prefsView.Activate();
+ }
+ }
+
+ private void PreferencesClosed(object sender, System.EventArgs e)
+ {
+ prefsView = null;
+ }
+
+ private void ToolbarItem_QuitClicked(object sender, System.EventArgs e)
+ {
+ NetworkMonitor.Instance.StopMonitor();
+ Close();
+ Application.Current.Shutdown();
+ }
+
+ private void ToolbarItem_NetworkClicked(object sender, System.Windows.RoutedEventArgs e)
+ {
+ if(sender.GetType() == typeof(MenuItem))
+ {
+ MenuItem item = e.Source as MenuItem;
+ if (item.DataContext != null)
+ {
+ ZeroTierNetwork network = item.DataContext as ZeroTierNetwork;
+ if (item.IsChecked)
+ {
+ APIHandler.Instance.LeaveNetwork(Dispatcher, network.NetworkId);
+ }
+ else
+ {
+ APIHandler.Instance.JoinNetwork(Dispatcher, network.NetworkId, network.AllowManaged, network.AllowGlobal, network.AllowDefault);
+ }
+ }
+ }
+ }
+
+ private void setWindowPosition(Window w)
+ {
+ double width = w.ActualWidth;
+ double height = w.ActualHeight;
+
+ double screenHeight = SystemParameters.PrimaryScreenHeight;
+ double screenWidth = SystemParameters.PrimaryScreenWidth;
+
+ double top = screenHeight - height - 40;
+ double left = screenWidth - width - 20;
+
+ w.Top = top;
+ w.Left = left;
+ }
+
+ private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
+ {
+ // reset cached locations to (0, 0) when display size changes
+ netListLocation.X = 0;
+ netListLocation.Y = 0;
+ joinNetLocation.X = 0;
+ joinNetLocation.Y = 0;
+ aboutViewLocation.X = 0;
+ aboutViewLocation.Y = 0;
+ prefsViewLocation.X = 0;
+ prefsViewLocation.Y = 0;
+ }
+ }
+}
diff --git a/windows/WinUI/WinUI.csproj b/windows/WinUI/WinUI.csproj
index c3eeaba4..6bbc1cd7 100644
--- a/windows/WinUI/WinUI.csproj
+++ b/windows/WinUI/WinUI.csproj
@@ -63,13 +63,15 @@
<PropertyGroup>
<SignManifests>false</SignManifests>
</PropertyGroup>
- <PropertyGroup>
- <ApplicationManifest>app.manifest</ApplicationManifest>
- </PropertyGroup>
+ <PropertyGroup />
<ItemGroup>
<Reference Include="Accessibility" />
- <Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
- <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+ <Reference Include="Hardcodet.Wpf.TaskbarNotification, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\Hardcodet.NotifyIcon.Wpf.1.0.8\lib\net45\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+ <HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationUI, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
@@ -78,6 +80,7 @@
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
+ <Reference Include="System.Net.Http" />
<Reference Include="System.Printing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
@@ -99,17 +102,69 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
+ <Compile Include="AboutView.xaml.cs">
+ <DependentUpon>AboutView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="CentralAPI.cs" />
+ <Compile Include="CentralLogin.cs" />
+ <Compile Include="CentralNetwork.cs" />
+ <Compile Include="CentralServer.cs" />
+ <Compile Include="CentralToken.cs" />
+ <Compile Include="CentralUser.cs" />
+ <Compile Include="ISwitchable.cs" />
+ <Compile Include="JoinNetworkView.xaml.cs">
+ <DependentUpon>JoinNetworkView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="NetworkMonitor.cs" />
+ <Compile Include="NetworkNameGenerator.cs" />
+ <Compile Include="NetworkRoute.cs" />
<Compile Include="NetworksPage.xaml.cs">
<DependentUpon>NetworksPage.xaml</DependentUpon>
</Compile>
+ <Compile Include="OnboardProcess\CreateAccount.xaml.cs">
+ <DependentUpon>CreateAccount.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\CreateOrJoin.xaml.cs">
+ <DependentUpon>CreateOrJoin.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\EnterToken.xaml.cs">
+ <DependentUpon>EnterToken.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\Finished.xaml.cs">
+ <DependentUpon>Finished.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\LogIn.xaml.cs">
+ <DependentUpon>LogIn.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="OnboardProcess\RegisterOrLogIn.xaml.cs">
+ <DependentUpon>RegisterOrLogIn.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="PageSwitcher.xaml.cs">
+ <DependentUpon>PageSwitcher.xaml</DependentUpon>
+ </Compile>
<Compile Include="PeersPage.xaml.cs">
<DependentUpon>PeersPage.xaml</DependentUpon>
</Compile>
+ <Compile Include="PreferencesView.xaml.cs">
+ <DependentUpon>PreferencesView.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="Switcher.cs" />
+ <Compile Include="ToolbarItem.xaml.cs">
+ <DependentUpon>ToolbarItem.xaml</DependentUpon>
+ </Compile>
<Compile Include="ZeroTierPeerPhysicalPath.cs" />
<Compile Include="ZeroTierPeer.cs" />
<Compile Include="ZeroTierNetwork.cs" />
<Compile Include="ZeroTierStatus.cs" />
- <Page Include="MainWindow.xaml">
+ <Page Include="AboutView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="JoinNetworkView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="NetworkListView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
@@ -118,8 +173,8 @@
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
- <Compile Include="MainWindow.xaml.cs">
- <DependentUpon>MainWindow.xaml</DependentUpon>
+ <Compile Include="NetworkListView.xaml.cs">
+ <DependentUpon>NetworkListView.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Page Include="NetworkInfoView.xaml">
@@ -130,10 +185,42 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
+ <Page Include="OnboardProcess\CreateAccount.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\CreateOrJoin.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\EnterToken.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\Finished.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\LogIn.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="OnboardProcess\RegisterOrLogIn.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="PageSwitcher.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="PeersPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
+ <Page Include="PreferencesView.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
<Page Include="Simple Styles.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -142,6 +229,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
+ <Page Include="ToolbarItem.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
</ItemGroup>
<ItemGroup>
<Compile Include="NetworkInfoView.xaml.cs">
@@ -215,8 +306,14 @@
</BlendEmbeddedFont>
<Resource Include="ZeroTierIcon.ico" />
</ItemGroup>
+ <ItemGroup>
+ <None Include="Resources\ZeroTierIcon.ico" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Expression\Blend\.NETFramework\v4.5\Microsoft.Expression.Blend.WPF.targets" />
+ <PropertyGroup>
+ <PostBuildEvent>copy "$(SolutionDir)\copyutil\bin\$(ConfigurationName)\copyutil.exe" "$(ProjectDir)\$(OutDir)\copyutil.exe"</PostBuildEvent>
+ </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/windows/WinUI/ZeroTierNetwork.cs b/windows/WinUI/ZeroTierNetwork.cs
index cce65441..d6802385 100644
--- a/windows/WinUI/ZeroTierNetwork.cs
+++ b/windows/WinUI/ZeroTierNetwork.cs
@@ -1,54 +1,494 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
- public class ZeroTierNetwork
+ [Serializable]
+ public class ZeroTierNetwork : ISerializable, IEquatable<ZeroTierNetwork>, IComparable<ZeroTierNetwork>, INotifyPropertyChanged
{
+ private string networkId;
+ private string macAddress;
+ private string networkName;
+ private string networkStatus;
+ private string networkType;
+ private Int32 mtu;
+ private bool dhcp;
+ private bool bridge;
+ private bool broadcastEnabled;
+ private Int32 portError;
+ private Int32 netconfRevision;
+ private string[] assignedAddresses;
+ private NetworkRoute[] routes;
+ private string deviceName;
+ private bool allowManaged;
+ private bool allowGlobal;
+ private bool allowDefault;
+ private bool isConnected;
+
+ protected ZeroTierNetwork(SerializationInfo info, StreamingContext ctx)
+ {
+ try
+ {
+ NetworkId = info.GetString("nwid");
+ MacAddress = info.GetString("mac");
+ NetworkName = info.GetString("name");
+ NetworkStatus = info.GetString("status");
+ NetworkType = info.GetString("type");
+ MTU = info.GetInt32("mtu");
+ DHCP = info.GetBoolean("dhcp");
+ Bridge = info.GetBoolean("bridge");
+ BroadcastEnabled = info.GetBoolean("broadcastEnabled");
+ PortError = info.GetInt32("portError");
+ NetconfRevision = info.GetInt32("netconfRevision");
+ AssignedAddresses = (string[])info.GetValue("assignedAddresses", typeof(string[]));
+ Routes = (NetworkRoute[])info.GetValue("routes", typeof(NetworkRoute[]));
+ DeviceName = info.GetString("portDeviceName");
+ AllowManaged = info.GetBoolean("allowManaged");
+ AllowGlobal = info.GetBoolean("allowGlobal");
+ AllowDefault = info.GetBoolean("allowDefault");
+ }
+ catch { }
+ IsConnected = false;
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext ctx)
+ {
+ info.AddValue("nwid", NetworkId);
+ info.AddValue("mac", MacAddress);
+ info.AddValue("name", NetworkName);
+ info.AddValue("status", NetworkStatus);
+ info.AddValue("type", NetworkType);
+ info.AddValue("mtu", MTU);
+ info.AddValue("dhcp", DHCP);
+ info.AddValue("bridge", Bridge);
+ info.AddValue("broadcastEnabled", BroadcastEnabled);
+ info.AddValue("portError", PortError);
+ info.AddValue("netconfRevision", NetconfRevision);
+ info.AddValue("assignedAddresses", AssignedAddresses);
+ info.AddValue("routes", Routes);
+ info.AddValue("portDeviceName", DeviceName);
+ info.AddValue("allowManaged", AllowManaged);
+ info.AddValue("allowGlobal", AllowGlobal);
+ info.AddValue("allowDefault", AllowDefault);
+ }
+
+ public void UpdateNetwork(ZeroTierNetwork network)
+ {
+ if (network == null)
+ return;
+
+ if (!NetworkId.Equals(network.NetworkId))
+ {
+ NetworkId = network.NetworkId;
+ }
+
+ if (!MacAddress.Equals(network.MacAddress))
+ {
+ MacAddress = network.MacAddress;
+ }
+
+ if (!NetworkName.Equals(network.NetworkName))
+ {
+ NetworkName = network.NetworkName;
+ }
+
+ if (!NetworkStatus.Equals(network.NetworkStatus))
+ {
+ NetworkStatus = network.NetworkStatus;
+ }
+
+ if (!NetworkType.Equals(network.NetworkType))
+ {
+ NetworkType = network.NetworkType;
+ }
+
+ if (MTU != network.MTU)
+ {
+ MTU = network.MTU;
+ }
+
+ if (DHCP != network.DHCP)
+ {
+ DHCP = network.DHCP;
+ }
+
+ if (Bridge != network.Bridge)
+ {
+ Bridge = network.Bridge;
+ }
+
+ if (BroadcastEnabled != network.BroadcastEnabled)
+ {
+ BroadcastEnabled = network.BroadcastEnabled;
+ }
+
+ if (PortError != network.PortError)
+ {
+ PortError = network.PortError;
+ }
+
+ if (NetconfRevision != network.NetconfRevision)
+ {
+ NetconfRevision = network.NetconfRevision;
+ }
+
+ AssignedAddresses = network.AssignedAddresses;
+
+ Routes = network.Routes;
+
+ if (!DeviceName.Equals(network.DeviceName))
+ {
+ DeviceName = network.DeviceName;
+ }
+
+ if (AllowManaged != network.AllowManaged)
+ {
+ AllowManaged = network.AllowManaged;
+ }
+
+ if (AllowGlobal != network.AllowGlobal)
+ {
+ AllowGlobal = network.AllowGlobal;
+ }
+
+ if (AllowDefault != network.AllowDefault)
+ {
+ AllowDefault = network.AllowDefault;
+ }
+
+ if (IsConnected != network.IsConnected)
+ {
+ IsConnected = network.IsConnected;
+ }
+ }
+
+ protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
[JsonProperty("nwid")]
- public string NetworkId { get; set; }
+ public string NetworkId {
+ get
+ {
+ return networkId;
+ }
+ set
+ {
+ networkId = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("mac")]
- public string MacAddress { get; set; }
+ public string MacAddress
+ {
+ get
+ {
+ return macAddress;
+ }
+ set
+ {
+ macAddress = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("name")]
- public string NetworkName { get; set; }
+ public string NetworkName
+ {
+ get
+ {
+ return networkName;
+ }
+ set
+ {
+ networkName = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("status")]
- public string NetworkStatus { get; set; }
+ public string NetworkStatus
+ {
+ get
+ {
+ return networkStatus;
+ }
+ set
+ {
+ networkStatus = value;
+ NotifyPropertyChanged();
+ }
+
+ }
[JsonProperty("type")]
- public string NetworkType { get; set; }
+ public string NetworkType
+ {
+ get
+ {
+ return networkType;
+ }
+ set
+ {
+ networkType = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("mtu")]
- public int MTU { get; set; }
+ public int MTU
+ {
+ get
+ {
+ return mtu;
+ }
+ set
+ {
+ mtu = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("dhcp")]
- public bool DHCP { get; set; }
+ public bool DHCP
+ {
+ get
+ {
+ return dhcp;
+ }
+ set
+ {
+ dhcp = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("bridge")]
- public bool Bridge { get; set ; }
+ public bool Bridge
+ {
+ get
+ {
+ return bridge;
+ }
+ set
+ {
+ bridge = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("broadcastEnabled")]
- public bool BroadcastEnabled { get ; set; }
+ public bool BroadcastEnabled
+ {
+ get
+ {
+ return broadcastEnabled;
+ }
+ set
+ {
+ broadcastEnabled = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("portError")]
- public int PortError { get; set; }
+ public int PortError
+ {
+ get
+ {
+ return portError;
+ }
+ set
+ {
+ portError = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("netconfRevision")]
- public int NetconfRevision { get; set; }
-
- [JsonProperty("multicastSubscriptions")]
- public string[] MulticastSubscriptions { get; set; }
+ public int NetconfRevision
+ {
+ get
+ {
+ return netconfRevision;
+ }
+ set
+ {
+ netconfRevision = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("assignedAddresses")]
- public string[] AssignedAddresses { get; set; }
+ public string[] AssignedAddresses
+ {
+ get
+ {
+ return assignedAddresses;
+ }
+ set
+ {
+ assignedAddresses = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("routes")]
+ public NetworkRoute[] Routes
+ {
+ get
+ {
+ return routes;
+ }
+ set
+ {
+ routes = value;
+ NotifyPropertyChanged();
+ }
+ }
[JsonProperty("portDeviceName")]
- public string DeviceName { get; set; }
+ public string DeviceName
+ {
+ get
+ {
+ return deviceName;
+ }
+ set
+ {
+ deviceName = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("allowManaged")]
+ public bool AllowManaged
+ {
+ get
+ {
+ return allowManaged;
+ }
+ set
+ {
+ allowManaged = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("allowGlobal")]
+ public bool AllowGlobal
+ {
+ get
+ {
+ return allowGlobal;
+ }
+ set
+ {
+ allowGlobal = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ [JsonProperty("allowDefault")]
+ public bool AllowDefault
+ {
+ get
+ {
+ return allowDefault;
+ }
+ set
+ {
+ allowDefault = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public bool IsConnected
+ {
+ get
+ {
+ return isConnected;
+ }
+ set
+ {
+ isConnected = value;
+ NotifyPropertyChanged();
+ }
+ }
+
+ public String Title
+ {
+ get
+ {
+
+ if (NetworkName != null && NetworkName.Length > 0)
+ {
+ return NetworkId + " (" + NetworkName + ")";
+ }
+ else
+ {
+ return NetworkId;
+ }
+ }
+ }
+
+ public bool Equals(ZeroTierNetwork network)
+ {
+ if (NetworkId == null || network == null)
+ return false;
+
+ return NetworkId.Equals(network.NetworkId);
+ }
+
+ public int CompareTo(ZeroTierNetwork network)
+ {
+ if (NetworkId == null || network == null)
+ return -1;
+
+ UInt64 thisNwid = UInt64.Parse(NetworkId, System.Globalization.NumberStyles.HexNumber);
+ UInt64 otherNwid = UInt64.Parse(network.NetworkId, System.Globalization.NumberStyles.HexNumber);
+
+ if (thisNwid > otherNwid)
+ {
+ return 1;
+ }
+ else if (thisNwid < otherNwid)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ public class NetworkEqualityComparer : IEqualityComparer<ZeroTierNetwork>
+ {
+ public bool Equals(ZeroTierNetwork lhs, ZeroTierNetwork rhs)
+ {
+ if (lhs.NetworkId.Equals(rhs.NetworkId))
+ {
+ lhs.UpdateNetwork(rhs);
+ return true;
+ }
+ return false;
+ }
+
+ public int GetHashCode(ZeroTierNetwork obj)
+ {
+ return obj.NetworkId.GetHashCode();
+ }
}
}
diff --git a/windows/WinUI/packages.config b/windows/WinUI/packages.config
index 505e5883..e6803f84 100644
--- a/windows/WinUI/packages.config
+++ b/windows/WinUI/packages.config
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
+ <package id="Hardcodet.NotifyIcon.Wpf" version="1.0.8" targetFramework="net45" />
+ <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/windows/ZeroTierOne.sln b/windows/ZeroTierOne.sln
index 68596187..05f498fb 100644
--- a/windows/ZeroTierOne.sln
+++ b/windows/ZeroTierOne.sln
@@ -1,11 +1,18 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroTierOne", "ZeroTierOne\ZeroTierOne.vcxproj", "{B00A4957-5977-4AC1-9EF4-571DC27EADA2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TapDriver6", "TapDriver6\TapDriver6.vcxproj", "{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinUI", "WinUI\WinUI.csproj", "{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}"
+ ProjectSection(ProjectDependencies) = postProject
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E} = {6D27214A-087B-4484-B898-AD2A13FA3B9E}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copyutil", "copyutil\copyutil.csproj", "{6D27214A-087B-4484-B898-AD2A13FA3B9E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -13,57 +20,50 @@ Global
CD_ROM|Mixed Platforms = CD_ROM|Mixed Platforms
CD_ROM|Win32 = CD_ROM|Win32
CD_ROM|x64 = CD_ROM|x64
- CD_ROM|x86 = CD_ROM|x86
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
DVD-5|Any CPU = DVD-5|Any CPU
DVD-5|Mixed Platforms = DVD-5|Mixed Platforms
DVD-5|Win32 = DVD-5|Win32
DVD-5|x64 = DVD-5|x64
- DVD-5|x86 = DVD-5|x86
+ Profile|Any CPU = Profile|Any CPU
+ Profile|Mixed Platforms = Profile|Mixed Platforms
+ Profile|Win32 = Profile|Win32
+ Profile|x64 = Profile|x64
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
Release|x64 = Release|x64
- Release|x86 = Release|x86
SingleImage|Any CPU = SingleImage|Any CPU
SingleImage|Mixed Platforms = SingleImage|Mixed Platforms
SingleImage|Win32 = SingleImage|Win32
SingleImage|x64 = SingleImage|x64
- SingleImage|x86 = SingleImage|x86
Vista Debug|Any CPU = Vista Debug|Any CPU
Vista Debug|Mixed Platforms = Vista Debug|Mixed Platforms
Vista Debug|Win32 = Vista Debug|Win32
Vista Debug|x64 = Vista Debug|x64
- Vista Debug|x86 = Vista Debug|x86
Vista Release|Any CPU = Vista Release|Any CPU
Vista Release|Mixed Platforms = Vista Release|Mixed Platforms
Vista Release|Win32 = Vista Release|Win32
Vista Release|x64 = Vista Release|x64
- Vista Release|x86 = Vista Release|x86
Win7 Debug|Any CPU = Win7 Debug|Any CPU
Win7 Debug|Mixed Platforms = Win7 Debug|Mixed Platforms
Win7 Debug|Win32 = Win7 Debug|Win32
Win7 Debug|x64 = Win7 Debug|x64
- Win7 Debug|x86 = Win7 Debug|x86
Win7 Release|Any CPU = Win7 Release|Any CPU
Win7 Release|Mixed Platforms = Win7 Release|Mixed Platforms
Win7 Release|Win32 = Win7 Release|Win32
Win7 Release|x64 = Win7 Release|x64
- Win7 Release|x86 = Win7 Release|x86
Win8 Debug|Any CPU = Win8 Debug|Any CPU
Win8 Debug|Mixed Platforms = Win8 Debug|Mixed Platforms
Win8 Debug|Win32 = Win8 Debug|Win32
Win8 Debug|x64 = Win8 Debug|x64
- Win8 Debug|x86 = Win8 Debug|x86
Win8 Release|Any CPU = Win8 Release|Any CPU
Win8 Release|Mixed Platforms = Win8 Release|Mixed Platforms
Win8 Release|Win32 = Win8 Release|Win32
Win8 Release|x64 = Win8 Release|x64
- Win8 Release|x86 = Win8 Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|Any CPU.ActiveCfg = Release|Win32
@@ -76,9 +76,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|x64.ActiveCfg = Release|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|x64.Build.0 = Release|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|x64.Deploy.0 = Release|x64
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.CD_ROM|x86.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|Any CPU.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|Mixed Platforms.Build.0 = Debug|Win32
@@ -88,9 +85,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|Win32.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|x64.ActiveCfg = Debug|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|x64.Build.0 = Debug|x64
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Debug|x86.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|Any CPU.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|Mixed Platforms.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|Mixed Platforms.Build.0 = Debug|Win32
@@ -101,9 +95,13 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|x64.ActiveCfg = Debug|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|x64.Build.0 = Debug|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|x64.Deploy.0 = Debug|x64
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.DVD-5|x86.Deploy.0 = Debug|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|Any CPU.ActiveCfg = Profile|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|Mixed Platforms.ActiveCfg = Profile|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|Mixed Platforms.Build.0 = Profile|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|Win32.ActiveCfg = Profile|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|Win32.Build.0 = Profile|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|x64.ActiveCfg = Profile|x64
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Profile|x64.Build.0 = Profile|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|Any CPU.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|Mixed Platforms.Build.0 = Release|Win32
@@ -112,9 +110,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|Win32.Build.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|Win32.Deploy.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|x64.ActiveCfg = Release|x64
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|x86.ActiveCfg = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|x86.Build.0 = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Release|x86.Deploy.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|Any CPU.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|Mixed Platforms.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|Mixed Platforms.Build.0 = Release|Win32
@@ -125,9 +120,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|x64.ActiveCfg = Release|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|x64.Build.0 = Release|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|x64.Deploy.0 = Release|x64
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.SingleImage|x86.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|Any CPU.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|Mixed Platforms.Build.0 = Debug|Win32
@@ -136,9 +128,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|Win32.Build.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|Win32.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|x64.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Debug|x86.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|Any CPU.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|Mixed Platforms.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|Mixed Platforms.Build.0 = Release|Win32
@@ -147,9 +136,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|Win32.Build.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|Win32.Deploy.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|x64.ActiveCfg = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|x86.ActiveCfg = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|x86.Build.0 = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Vista Release|x86.Deploy.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|Any CPU.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|Mixed Platforms.Build.0 = Debug|Win32
@@ -157,10 +143,8 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|Win32.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|Win32.Build.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|Win32.Deploy.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|x64.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|x86.Deploy.0 = Debug|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|x64.ActiveCfg = Debug|x64
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Debug|x64.Build.0 = Debug|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|Any CPU.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|Mixed Platforms.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|Mixed Platforms.Build.0 = Release|Win32
@@ -169,9 +153,7 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|Win32.Build.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|Win32.Deploy.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|x64.ActiveCfg = Release|x64
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|x86.ActiveCfg = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|x86.Build.0 = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|x86.Deploy.0 = Release|Win32
+ {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win7 Release|x64.Build.0 = Release|x64
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|Any CPU.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|Mixed Platforms.Build.0 = Debug|Win32
@@ -180,9 +162,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|Win32.Build.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|Win32.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|x64.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|x86.ActiveCfg = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|x86.Build.0 = Debug|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Debug|x86.Deploy.0 = Debug|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|Any CPU.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|Mixed Platforms.ActiveCfg = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|Mixed Platforms.Build.0 = Release|Win32
@@ -191,9 +170,6 @@ Global
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|Win32.Build.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|Win32.Deploy.0 = Release|Win32
{B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x64.ActiveCfg = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x86.ActiveCfg = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x86.Build.0 = Release|Win32
- {B00A4957-5977-4AC1-9EF4-571DC27EADA2}.Win8 Release|x86.Deploy.0 = Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Any CPU.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Mixed Platforms.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|Mixed Platforms.Build.0 = Win8 Release|Win32
@@ -204,9 +180,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x64.ActiveCfg = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x64.Build.0 = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x64.Deploy.0 = Win8 Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x86.ActiveCfg = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x86.Build.0 = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.CD_ROM|x86.Deploy.0 = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Any CPU.ActiveCfg = Win7 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Mixed Platforms.ActiveCfg = Win7 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|Mixed Platforms.Build.0 = Win7 Debug|Win32
@@ -217,9 +190,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x64.ActiveCfg = Win7 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x64.Build.0 = Win7 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x64.Deploy.0 = Win7 Debug|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x86.ActiveCfg = Win7 Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x86.Build.0 = Win7 Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Debug|x86.Deploy.0 = Win7 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Any CPU.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Mixed Platforms.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|Mixed Platforms.Build.0 = Win8 Release|Win32
@@ -230,9 +200,10 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x64.ActiveCfg = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x64.Build.0 = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x64.Deploy.0 = Win8 Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x86.ActiveCfg = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x86.Build.0 = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.DVD-5|x86.Deploy.0 = Win8 Release|Win32
+ {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Profile|Any CPU.ActiveCfg = Win8 Debug|x64
+ {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Profile|Mixed Platforms.ActiveCfg = Win8 Debug|Win32
+ {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Profile|Win32.ActiveCfg = Win8 Debug|Win32
+ {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Profile|x64.ActiveCfg = Win8 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Any CPU.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Mixed Platforms.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|Mixed Platforms.Build.0 = Win8 Release|Win32
@@ -243,9 +214,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x64.ActiveCfg = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x64.Build.0 = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x64.Deploy.0 = Win8 Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x86.ActiveCfg = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x86.Build.0 = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Release|x86.Deploy.0 = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Any CPU.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Mixed Platforms.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|Mixed Platforms.Build.0 = Win8 Release|Win32
@@ -256,9 +224,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x64.ActiveCfg = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x64.Build.0 = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x64.Deploy.0 = Win8 Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x86.ActiveCfg = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x86.Build.0 = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.SingleImage|x86.Deploy.0 = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Any CPU.ActiveCfg = Vista Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Mixed Platforms.ActiveCfg = Vista Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|Mixed Platforms.Build.0 = Vista Debug|Win32
@@ -269,9 +234,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x64.ActiveCfg = Vista Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x64.Build.0 = Vista Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x64.Deploy.0 = Vista Debug|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x86.ActiveCfg = Vista Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x86.Build.0 = Vista Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Debug|x86.Deploy.0 = Vista Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Any CPU.ActiveCfg = Vista Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Mixed Platforms.ActiveCfg = Vista Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|Mixed Platforms.Build.0 = Vista Release|Win32
@@ -282,9 +244,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x64.ActiveCfg = Vista Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x64.Build.0 = Vista Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x64.Deploy.0 = Vista Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x86.ActiveCfg = Vista Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x86.Build.0 = Vista Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Vista Release|x86.Deploy.0 = Vista Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Any CPU.ActiveCfg = Win7 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Mixed Platforms.ActiveCfg = Win7 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|Mixed Platforms.Build.0 = Win7 Debug|Win32
@@ -295,9 +254,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x64.ActiveCfg = Win7 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x64.Build.0 = Win7 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x64.Deploy.0 = Win7 Debug|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x86.ActiveCfg = Win7 Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x86.Build.0 = Win7 Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Debug|x86.Deploy.0 = Win7 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Any CPU.ActiveCfg = Win7 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Mixed Platforms.ActiveCfg = Win7 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|Mixed Platforms.Build.0 = Win7 Release|Win32
@@ -308,9 +264,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x64.ActiveCfg = Win7 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x64.Build.0 = Win7 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x64.Deploy.0 = Win7 Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x86.ActiveCfg = Win7 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x86.Build.0 = Win7 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win7 Release|x86.Deploy.0 = Win7 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Any CPU.ActiveCfg = Win8 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Mixed Platforms.ActiveCfg = Win8 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|Mixed Platforms.Build.0 = Win8 Debug|Win32
@@ -321,9 +274,6 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x64.ActiveCfg = Win8 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x64.Build.0 = Win8 Debug|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x64.Deploy.0 = Win8 Debug|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x86.ActiveCfg = Win8 Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x86.Build.0 = Win8 Debug|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Debug|x86.Deploy.0 = Win8 Debug|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Any CPU.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Mixed Platforms.ActiveCfg = Win8 Release|Win32
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|Mixed Platforms.Build.0 = Win8 Release|Win32
@@ -334,86 +284,178 @@ Global
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x64.ActiveCfg = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x64.Build.0 = Win8 Release|x64
{43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x64.Deploy.0 = Win8 Release|x64
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x86.ActiveCfg = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x86.Build.0 = Win8 Release|Win32
- {43BA7584-D4DB-4F7C-90FC-E2B18A68A213}.Win8 Release|x86.Deploy.0 = Win8 Release|Win32
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|Any CPU.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|Any CPU.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|Mixed Platforms.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|Mixed Platforms.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|Win32.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|x64.ActiveCfg = Release|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.CD_ROM|x86.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|Win32.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|x64.ActiveCfg = Debug|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Debug|x86.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|Any CPU.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|Any CPU.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|Mixed Platforms.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|Win32.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|x64.ActiveCfg = Debug|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.DVD-5|x86.ActiveCfg = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|Any CPU.ActiveCfg = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|Any CPU.Build.0 = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|Mixed Platforms.Build.0 = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|Win32.ActiveCfg = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|Win32.Build.0 = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|x64.ActiveCfg = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Profile|x64.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|Any CPU.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|Win32.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|x64.ActiveCfg = Release|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Release|x86.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|Any CPU.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|Any CPU.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|Mixed Platforms.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|Mixed Platforms.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|Win32.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|x64.ActiveCfg = Release|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.SingleImage|x86.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|Any CPU.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|Win32.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|x64.ActiveCfg = Debug|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Debug|x86.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|Any CPU.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|Any CPU.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|Mixed Platforms.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|Win32.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|x64.ActiveCfg = Release|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Vista Release|x86.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|Any CPU.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|Win32.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|x64.ActiveCfg = Debug|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|x86.ActiveCfg = Debug|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Debug|x64.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|Any CPU.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|Any CPU.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|Mixed Platforms.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|Win32.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|x64.ActiveCfg = Release|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|x86.ActiveCfg = Release|Any CPU
+ {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win7 Release|x64.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|Any CPU.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|Win32.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|x64.ActiveCfg = Debug|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Debug|x86.ActiveCfg = Debug|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|Any CPU.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|Any CPU.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|Mixed Platforms.Build.0 = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|Win32.ActiveCfg = Release|Any CPU
{4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|x64.ActiveCfg = Release|Any CPU
- {4CCA6B98-5E64-45BF-AC34-19B3E2570DB1}.Win8 Release|x86.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|Any CPU.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|Any CPU.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|Mixed Platforms.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|Win32.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|Win32.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|x64.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.CD_ROM|x64.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|Win32.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Debug|x64.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|Any CPU.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|Win32.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|Win32.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|x64.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.DVD-5|x64.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|Any CPU.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|Win32.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|Win32.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|x64.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Profile|x64.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|Win32.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|Win32.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|x64.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Release|x64.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|Any CPU.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|Any CPU.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|Mixed Platforms.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|Win32.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|Win32.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|x64.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.SingleImage|x64.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|Win32.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|Win32.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|x64.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Debug|x64.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|Any CPU.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|Win32.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|Win32.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|x64.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Vista Release|x64.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|Win32.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|Win32.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|x64.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Debug|x64.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|Any CPU.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|Win32.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|Win32.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|x64.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win7 Release|x64.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|Win32.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|Win32.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|x64.ActiveCfg = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Debug|x64.Build.0 = Debug|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|Any CPU.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|Win32.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|Win32.Build.0 = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|x64.ActiveCfg = Release|Any CPU
+ {6D27214A-087B-4484-B898-AD2A13FA3B9E}.Win8 Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj
index ed022134..105ea127 100644
--- a/windows/ZeroTierOne/ZeroTierOne.vcxproj
+++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj
@@ -1,330 +1,409 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Debug|x64">
- <Configuration>Debug</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|x64">
- <Configuration>Release</Configuration>
- <Platform>x64</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="..\..\ext\http-parser\http_parser.c" />
- <ClCompile Include="..\..\ext\json-parser\json.c" />
- <ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
- <ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
- <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c" />
- <ClCompile Include="..\..\ext\lz4\lz4.c" />
- <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c" />
- <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c" />
- <ClCompile Include="..\..\ext\miniupnpc\minisoap.c" />
- <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c" />
- <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c" />
- <ClCompile Include="..\..\ext\miniupnpc\miniwget.c" />
- <ClCompile Include="..\..\ext\miniupnpc\minixml.c" />
- <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c" />
- <ClCompile Include="..\..\ext\miniupnpc\receivedata.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c" />
- <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c" />
- <ClCompile Include="..\..\node\C25519.cpp" />
- <ClCompile Include="..\..\node\CertificateOfMembership.cpp" />
- <ClCompile Include="..\..\node\Cluster.cpp" />
- <ClCompile Include="..\..\node\DeferredPackets.cpp" />
- <ClCompile Include="..\..\node\Identity.cpp" />
- <ClCompile Include="..\..\node\IncomingPacket.cpp" />
- <ClCompile Include="..\..\node\InetAddress.cpp" />
- <ClCompile Include="..\..\node\Multicaster.cpp" />
- <ClCompile Include="..\..\node\Network.cpp" />
- <ClCompile Include="..\..\node\NetworkConfig.cpp" />
- <ClCompile Include="..\..\node\Node.cpp" />
- <ClCompile Include="..\..\node\OutboundMulticast.cpp" />
- <ClCompile Include="..\..\node\Packet.cpp" />
- <ClCompile Include="..\..\node\Path.cpp" />
- <ClCompile Include="..\..\node\Peer.cpp" />
- <ClCompile Include="..\..\node\Poly1305.cpp" />
- <ClCompile Include="..\..\node\Salsa20.cpp" />
- <ClCompile Include="..\..\node\SelfAwareness.cpp" />
- <ClCompile Include="..\..\node\SHA512.cpp" />
- <ClCompile Include="..\..\node\Switch.cpp" />
- <ClCompile Include="..\..\node\Topology.cpp" />
- <ClCompile Include="..\..\node\Utils.cpp" />
- <ClCompile Include="..\..\one.cpp">
- <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
- </ClCompile>
- <ClCompile Include="..\..\osdep\BackgroundResolver.cpp" />
- <ClCompile Include="..\..\osdep\Http.cpp" />
- <ClCompile Include="..\..\osdep\ManagedRoute.cpp" />
- <ClCompile Include="..\..\osdep\OSUtils.cpp" />
- <ClCompile Include="..\..\osdep\PortMapper.cpp" />
- <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
- <ClCompile Include="..\..\service\ControlPlane.cpp" />
- <ClCompile Include="..\..\service\OneService.cpp" />
- <ClCompile Include="ServiceBase.cpp" />
- <ClCompile Include="ServiceInstaller.cpp" />
- <ClCompile Include="ZeroTierOneService.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\codelength.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\connecthostport.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\igd_desc_parse.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\minisoap.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\minissdpc.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpc.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpcstrings.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpctypes.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpc_declspec.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniwget.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\minixml.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\portlistingparse.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\receivedata.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\upnpcommands.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\upnperrors.h" />
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\upnpreplyparse.h" />
- <ClInclude Include="..\..\ext\http-parser\http_parser.h" />
- <ClInclude Include="..\..\ext\json-parser\json.h" />
- <ClInclude Include="..\..\ext\libnatpmp\getgateway.h" />
- <ClInclude Include="..\..\ext\libnatpmp\natpmp.h" />
- <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h" />
- <ClInclude Include="..\..\ext\lz4\lz4.h" />
- <ClInclude Include="..\..\ext\miniupnpc\codelength.h" />
- <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h" />
- <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h" />
- <ClInclude Include="..\..\ext\miniupnpc\minisoap.h" />
- <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h" />
- <ClInclude Include="..\..\ext\miniupnpc\miniwget.h" />
- <ClInclude Include="..\..\ext\miniupnpc\minixml.h" />
- <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h" />
- <ClInclude Include="..\..\ext\miniupnpc\receivedata.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h" />
- <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h" />
- <ClInclude Include="..\..\include\ZeroTierOne.h" />
- <ClInclude Include="..\..\node\Address.hpp" />
- <ClInclude Include="..\..\node\Array.hpp" />
- <ClInclude Include="..\..\node\AtomicCounter.hpp" />
- <ClInclude Include="..\..\node\BandwidthAccount.hpp" />
- <ClInclude Include="..\..\node\BinarySemaphore.hpp" />
- <ClInclude Include="..\..\node\Buffer.hpp" />
- <ClInclude Include="..\..\node\C25519.hpp" />
- <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
- <ClInclude Include="..\..\node\Cluster.hpp" />
- <ClInclude Include="..\..\node\CMWC4096.hpp" />
- <ClInclude Include="..\..\node\Constants.hpp" />
- <ClInclude Include="..\..\node\DeferredPackets.hpp" />
- <ClInclude Include="..\..\node\Dictionary.hpp" />
- <ClInclude Include="..\..\node\Hashtable.hpp" />
- <ClInclude Include="..\..\node\Identity.hpp" />
- <ClInclude Include="..\..\node\IncomingPacket.hpp" />
- <ClInclude Include="..\..\node\InetAddress.hpp" />
- <ClInclude Include="..\..\node\MAC.hpp" />
- <ClInclude Include="..\..\node\Multicaster.hpp" />
- <ClInclude Include="..\..\node\MulticastGroup.hpp" />
- <ClInclude Include="..\..\node\Mutex.hpp" />
- <ClInclude Include="..\..\node\Network.hpp" />
- <ClInclude Include="..\..\node\NetworkConfig.hpp" />
- <ClInclude Include="..\..\node\NetworkController.hpp" />
- <ClInclude Include="..\..\node\Node.hpp" />
- <ClInclude Include="..\..\node\NonCopyable.hpp" />
- <ClInclude Include="..\..\node\OutboundMulticast.hpp" />
- <ClInclude Include="..\..\node\Packet.hpp" />
- <ClInclude Include="..\..\node\Path.hpp" />
- <ClInclude Include="..\..\node\Peer.hpp" />
- <ClInclude Include="..\..\node\Poly1305.hpp" />
- <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
- <ClInclude Include="..\..\node\Salsa20.hpp" />
- <ClInclude Include="..\..\node\SelfAwareness.hpp" />
- <ClInclude Include="..\..\node\SHA512.hpp" />
- <ClInclude Include="..\..\node\SharedPtr.hpp" />
- <ClInclude Include="..\..\node\Switch.hpp" />
- <ClInclude Include="..\..\node\Topology.hpp" />
- <ClInclude Include="..\..\node\Utils.hpp" />
- <ClInclude Include="..\..\node\World.hpp" />
- <ClInclude Include="..\..\osdep\BackgroundResolver.hpp" />
- <ClInclude Include="..\..\osdep\Binder.hpp" />
- <ClInclude Include="..\..\osdep\Http.hpp" />
- <ClInclude Include="..\..\osdep\ManagedRoute.hpp" />
- <ClInclude Include="..\..\osdep\OSUtils.hpp" />
- <ClInclude Include="..\..\osdep\Phy.hpp" />
- <ClInclude Include="..\..\osdep\PortMapper.hpp" />
- <ClInclude Include="..\..\osdep\Thread.hpp" />
- <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
- <ClInclude Include="..\..\service\ControlPlane.hpp" />
- <ClInclude Include="..\..\service\ControlPlaneSubsystem.hpp" />
- <ClInclude Include="..\..\service\OneService.hpp" />
- <ClInclude Include="..\..\version.h" />
- <ClInclude Include="resource.h" />
- <ClInclude Include="ServiceBase.h" />
- <ClInclude Include="ServiceInstaller.h" />
- <ClInclude Include="ZeroTierOneService.h" />
- </ItemGroup>
- <ItemGroup>
- <ResourceCompile Include="ZeroTierOne.rc" />
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <ProjectGuid>{B00A4957-5977-4AC1-9EF4-571DC27EADA2}</ProjectGuid>
- <RootNamespace>ZeroTierOne</RootNamespace>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v110</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v110</PlatformToolset>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v110</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
- <ConfigurationType>Application</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v110</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>MultiByte</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x86</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x86</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x64</TargetName>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <TargetExt>.exe</TargetExt>
- <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
- <TargetName>zerotier-one_x64</TargetName>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>Disabled</Optimization>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <MultiProcessorCompilation>false</MultiProcessorCompilation>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>STATICLIB;ZT_OFFICIAL_RELEASE;ZT_AUTO_UPDATE;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
- <StringPooling>true</StringPooling>
- <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
- <OmitFramePointers>true</OmitFramePointers>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <Optimization>MaxSpeed</Optimization>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- <AdditionalIncludeDirectories>
- </AdditionalIncludeDirectories>
- <PreprocessorDefinitions>STATICLIB;ZT_OFFICIAL_RELEASE;ZT_AUTO_UPDATE;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
- <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
- <StringPooling>true</StringPooling>
- <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
- <OmitFramePointers>true</OmitFramePointers>
- </ClCompile>
- <Link>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
- <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
- </Link>
- </ItemDefinitionGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Profile|Win32">
+ <Configuration>Profile</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Profile|x64">
+ <Configuration>Profile</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\controller\DB.cpp" />
+ <ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp" />
+ <ClCompile Include="..\..\controller\FileDB.cpp" />
+ <ClCompile Include="..\..\controller\RethinkDB.cpp" />
+ <ClCompile Include="..\..\ext\http-parser\http_parser.c" />
+ <ClCompile Include="..\..\ext\libnatpmp\getgateway.c" />
+ <ClCompile Include="..\..\ext\libnatpmp\natpmp.c" />
+ <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\minisoap.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\miniwget.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\minixml.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\receivedata.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c" />
+ <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c" />
+ <ClCompile Include="..\..\node\C25519.cpp" />
+ <ClCompile Include="..\..\node\Capability.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp" />
+ <ClCompile Include="..\..\node\Identity.cpp" />
+ <ClCompile Include="..\..\node\IncomingPacket.cpp" />
+ <ClCompile Include="..\..\node\InetAddress.cpp" />
+ <ClCompile Include="..\..\node\Membership.cpp" />
+ <ClCompile Include="..\..\node\Multicaster.cpp" />
+ <ClCompile Include="..\..\node\Network.cpp" />
+ <ClCompile Include="..\..\node\NetworkConfig.cpp" />
+ <ClCompile Include="..\..\node\Node.cpp" />
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp" />
+ <ClCompile Include="..\..\node\Packet.cpp" />
+ <ClCompile Include="..\..\node\Path.cpp" />
+ <ClCompile Include="..\..\node\Peer.cpp" />
+ <ClCompile Include="..\..\node\Poly1305.cpp" />
+ <ClCompile Include="..\..\node\Revocation.cpp" />
+ <ClCompile Include="..\..\node\Salsa20.cpp" />
+ <ClCompile Include="..\..\node\SelfAwareness.cpp" />
+ <ClCompile Include="..\..\node\SHA512.cpp" />
+ <ClCompile Include="..\..\node\Switch.cpp" />
+ <ClCompile Include="..\..\node\Tag.cpp" />
+ <ClCompile Include="..\..\node\Topology.cpp" />
+ <ClCompile Include="..\..\node\Trace.cpp" />
+ <ClCompile Include="..\..\node\Utils.cpp" />
+ <ClCompile Include="..\..\one.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">false</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\Http.cpp" />
+ <ClCompile Include="..\..\osdep\ManagedRoute.cpp" />
+ <ClCompile Include="..\..\osdep\OSUtils.cpp" />
+ <ClCompile Include="..\..\osdep\PortMapper.cpp" />
+ <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp" />
+ <ClCompile Include="..\..\selftest.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="..\..\service\OneService.cpp" />
+ <ClCompile Include="..\..\service\SoftwareUpdater.cpp" />
+ <ClCompile Include="ServiceBase.cpp" />
+ <ClCompile Include="ServiceInstaller.cpp" />
+ <ClCompile Include="ZeroTierOneService.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\controller\DB.hpp" />
+ <ClInclude Include="..\..\controller\EmbeddedNetworkController.hpp" />
+ <ClInclude Include="..\..\controller\FileDB.hpp" />
+ <ClInclude Include="..\..\controller\RethinkDB.hpp" />
+ <ClInclude Include="..\..\ext\http-parser\http_parser.h" />
+ <ClInclude Include="..\..\ext\json\json.hpp" />
+ <ClInclude Include="..\..\ext\libnatpmp\getgateway.h" />
+ <ClInclude Include="..\..\ext\libnatpmp\natpmp.h" />
+ <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\codelength.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\minisoap.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\miniwget.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\minixml.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\receivedata.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h" />
+ <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h" />
+ <ClInclude Include="..\..\ext\x64-salsa2012-asm\salsa2012.h" />
+ <ClInclude Include="..\..\include\ZeroTierOne.h" />
+ <ClInclude Include="..\..\node\Address.hpp" />
+ <ClInclude Include="..\..\node\AtomicCounter.hpp" />
+ <ClInclude Include="..\..\node\Buffer.hpp" />
+ <ClInclude Include="..\..\node\C25519.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp" />
+ <ClInclude Include="..\..\node\Constants.hpp" />
+ <ClInclude Include="..\..\node\Credential.hpp" />
+ <ClInclude Include="..\..\node\Dictionary.hpp" />
+ <ClInclude Include="..\..\node\Hashtable.hpp" />
+ <ClInclude Include="..\..\node\Identity.hpp" />
+ <ClInclude Include="..\..\node\IncomingPacket.hpp" />
+ <ClInclude Include="..\..\node\InetAddress.hpp" />
+ <ClInclude Include="..\..\node\MAC.hpp" />
+ <ClInclude Include="..\..\node\Multicaster.hpp" />
+ <ClInclude Include="..\..\node\MulticastGroup.hpp" />
+ <ClInclude Include="..\..\node\Mutex.hpp" />
+ <ClInclude Include="..\..\node\Network.hpp" />
+ <ClInclude Include="..\..\node\NetworkConfig.hpp" />
+ <ClInclude Include="..\..\node\NetworkController.hpp" />
+ <ClInclude Include="..\..\node\Node.hpp" />
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp" />
+ <ClInclude Include="..\..\node\Packet.hpp" />
+ <ClInclude Include="..\..\node\Path.hpp" />
+ <ClInclude Include="..\..\node\Peer.hpp" />
+ <ClInclude Include="..\..\node\Poly1305.hpp" />
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
+ <ClInclude Include="..\..\node\Salsa20.hpp" />
+ <ClInclude Include="..\..\node\SelfAwareness.hpp" />
+ <ClInclude Include="..\..\node\SHA512.hpp" />
+ <ClInclude Include="..\..\node\SharedPtr.hpp" />
+ <ClInclude Include="..\..\node\Switch.hpp" />
+ <ClInclude Include="..\..\node\Topology.hpp" />
+ <ClInclude Include="..\..\node\Trace.hpp" />
+ <ClInclude Include="..\..\node\Utils.hpp" />
+ <ClInclude Include="..\..\node\World.hpp" />
+ <ClInclude Include="..\..\osdep\Binder.hpp" />
+ <ClInclude Include="..\..\osdep\Http.hpp" />
+ <ClInclude Include="..\..\osdep\ManagedRoute.hpp" />
+ <ClInclude Include="..\..\osdep\OSUtils.hpp" />
+ <ClInclude Include="..\..\osdep\Phy.hpp" />
+ <ClInclude Include="..\..\osdep\PortMapper.hpp" />
+ <ClInclude Include="..\..\osdep\Thread.hpp" />
+ <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp" />
+ <ClInclude Include="..\..\service\OneService.hpp" />
+ <ClInclude Include="..\..\service\SoftwareUpdater.hpp" />
+ <ClInclude Include="..\..\version.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="ServiceBase.h" />
+ <ClInclude Include="ServiceInstaller.h" />
+ <ClInclude Include="ZeroTierOneService.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ZeroTierOne.rc" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{B00A4957-5977-4AC1-9EF4-571DC27EADA2}</ProjectGuid>
+ <RootNamespace>ZeroTierOne</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x64</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x64</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetExt>.exe</TargetExt>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>zerotier-one_x64</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>false</MultiProcessorCompilation>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MultiProcessorCompilation>false</MultiProcessorCompilation>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalOptions>"notelemetry.obj" %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
+ <StringPooling>true</StringPooling>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>ZT_EXPORT;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ <StringPooling>true</StringPooling>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <OmitFramePointers>true</OmitFramePointers>
+ <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <EnableParallelCodeGeneration>false</EnableParallelCodeGeneration>
+ <CallingConvention>VectorCall</CallingConvention>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters
index eeeb8d2f..d07c0638 100644
--- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters
+++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters
@@ -1,551 +1,506 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- <Filter Include="Source Files\node">
- <UniqueIdentifier>{67b1c0f8-b018-4169-9c14-7032ed12c786}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\include">
- <UniqueIdentifier>{40761a4c-e8db-4a91-9cab-7afef332f4a8}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\node">
- <UniqueIdentifier>{da3b8126-840c-45db-8abe-9d7e7976f8be}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\osdep">
- <UniqueIdentifier>{6054dfae-4ed2-4d69-8cf5-d6f27646f2d7}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\service">
- <UniqueIdentifier>{9944293a-4a1a-40e9-b92a-eff31fe87e2c}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\osdep">
- <UniqueIdentifier>{ca21bd6b-ff4e-4f9e-bedd-c9f603d2d0d6}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\service">
- <UniqueIdentifier>{e1743b3c-1d18-47f1-ab5a-f5703c19f1df}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext">
- <UniqueIdentifier>{71865460-d693-4c73-84f6-dbff42f49df6}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\http-parser">
- <UniqueIdentifier>{17ae9a01-d39f-4c6d-a800-8f2cd0804c96}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\json-parser">
- <UniqueIdentifier>{736aad7f-8d95-4602-88df-3bb970869c6f}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\lz4">
- <UniqueIdentifier>{3636527c-bc03-4852-bd3c-20ee25e56d82}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext">
- <UniqueIdentifier>{7784af31-5b60-4300-b07e-44cf864c54db}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\lz4">
- <UniqueIdentifier>{29164186-10fc-45f5-b253-6d03f0ddd4db}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\http-parser">
- <UniqueIdentifier>{f8a1c208-15b8-4d85-a4cb-11d2b82f2d1e}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\json-parser">
- <UniqueIdentifier>{da28e961-1761-41d8-9a59-65b00dfb1302}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\windows">
- <UniqueIdentifier>{43f75f84-c70d-4d44-a0ef-28a7a399abd4}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\windows\ZeroTierOne">
- <UniqueIdentifier>{0da07a2f-8922-4827-ac51-29ca3f30f881}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\windows">
- <UniqueIdentifier>{b74916eb-bb6c-4449-a2a2-fa0b17f60121}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\windows\ZeroTierOne">
- <UniqueIdentifier>{bf604491-14c4-4a74-81a6-6105d07c5c7c}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\bin">
- <UniqueIdentifier>{5939db69-ab17-47c6-97fb-185e2c678737}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\bin\miniupnpc">
- <UniqueIdentifier>{3666f510-b6da-47cb-8039-56441f2dac3e}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\bin\miniupnpc\include">
- <UniqueIdentifier>{1a47071e-e51b-4535-89ae-858946f03118}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\miniupnpc">
- <UniqueIdentifier>{5423fb64-896b-432e-a19d-88d4467f89f9}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\miniupnpc">
- <UniqueIdentifier>{56cc3ab8-3336-4a22-9471-c267ee46cd54}</UniqueIdentifier>
- </Filter>
- <Filter Include="Header Files\ext\libnatpmp">
- <UniqueIdentifier>{d7292d0d-72a0-4ed6-b717-21debb120737}</UniqueIdentifier>
- </Filter>
- <Filter Include="Source Files\ext\libnatpmp">
- <UniqueIdentifier>{409ec37e-ff36-4c13-b18d-52d6052e0ca2}</UniqueIdentifier>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="..\..\service\ControlPlane.cpp">
- <Filter>Source Files\service</Filter>
- </ClCompile>
- <ClCompile Include="..\..\service\OneService.cpp">
- <Filter>Source Files\service</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\Http.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\OSUtils.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\C25519.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\CertificateOfMembership.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Identity.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\IncomingPacket.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\InetAddress.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Multicaster.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Network.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\NetworkConfig.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Node.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\OutboundMulticast.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Packet.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Peer.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Poly1305.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Salsa20.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\SelfAwareness.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\SHA512.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Switch.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Topology.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Utils.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\lz4\lz4.c">
- <Filter>Source Files\ext\lz4</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\http-parser\http_parser.c">
- <Filter>Source Files\ext\http-parser</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\json-parser\json.c">
- <Filter>Source Files\ext\json-parser</Filter>
- </ClCompile>
- <ClCompile Include="..\..\one.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="ServiceBase.cpp">
- <Filter>Source Files\windows\ZeroTierOne</Filter>
- </ClCompile>
- <ClCompile Include="ServiceInstaller.cpp">
- <Filter>Source Files\windows\ZeroTierOne</Filter>
- </ClCompile>
- <ClCompile Include="ZeroTierOneService.cpp">
- <Filter>Source Files\windows\ZeroTierOne</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\BackgroundResolver.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Path.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\DeferredPackets.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\node\Cluster.cpp">
- <Filter>Source Files\node</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\minisoap.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\miniwget.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\minixml.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\receivedata.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c">
- <Filter>Source Files\ext\miniupnpc</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\PortMapper.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\libnatpmp\getgateway.c">
- <Filter>Source Files\ext\libnatpmp</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\libnatpmp\natpmp.c">
- <Filter>Source Files\ext\libnatpmp</Filter>
- </ClCompile>
- <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c">
- <Filter>Source Files\ext\libnatpmp</Filter>
- </ClCompile>
- <ClCompile Include="..\..\osdep\ManagedRoute.cpp">
- <Filter>Source Files\osdep</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="resource.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="..\..\version.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="..\..\include\ZeroTierOne.h">
- <Filter>Header Files\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Http.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\OSUtils.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Phy.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Thread.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\service\ControlPlane.hpp">
- <Filter>Header Files\service</Filter>
- </ClInclude>
- <ClInclude Include="..\..\service\ControlPlaneSubsystem.hpp">
- <Filter>Header Files\service</Filter>
- </ClInclude>
- <ClInclude Include="..\..\service\OneService.hpp">
- <Filter>Header Files\service</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Address.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Array.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\AtomicCounter.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\BandwidthAccount.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Buffer.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\C25519.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\CertificateOfMembership.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\CMWC4096.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Constants.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Dictionary.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Identity.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\IncomingPacket.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\InetAddress.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\MAC.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Multicaster.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\MulticastGroup.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Mutex.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Network.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\NetworkConfig.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\NetworkController.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Node.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\NonCopyable.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\OutboundMulticast.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Packet.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Path.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Peer.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Poly1305.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\RuntimeEnvironment.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Salsa20.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\SelfAwareness.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\SHA512.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\SharedPtr.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Switch.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Topology.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Utils.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\lz4\lz4.h">
- <Filter>Header Files\ext\lz4</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\json-parser\json.h">
- <Filter>Header Files\ext\json-parser</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\http-parser\http_parser.h">
- <Filter>Header Files\ext\http-parser</Filter>
- </ClInclude>
- <ClInclude Include="ServiceBase.h">
- <Filter>Header Files\windows\ZeroTierOne</Filter>
- </ClInclude>
- <ClInclude Include="ServiceInstaller.h">
- <Filter>Header Files\windows\ZeroTierOne</Filter>
- </ClInclude>
- <ClInclude Include="ZeroTierOneService.h">
- <Filter>Header Files\windows\ZeroTierOne</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\BackgroundResolver.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\codelength.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\connecthostport.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\igd_desc_parse.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\minisoap.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\minissdpc.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpc.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpc_declspec.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpcstrings.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniupnpctypes.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\miniwget.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\minixml.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\portlistingparse.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\receivedata.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\upnpcommands.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\upnperrors.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\bin\miniupnpc\include\miniupnpc\upnpreplyparse.h">
- <Filter>Header Files\ext\bin\miniupnpc\include</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\BinarySemaphore.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Cluster.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\Hashtable.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\DeferredPackets.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\node\World.hpp">
- <Filter>Header Files\node</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\codelength.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\minisoap.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\miniwget.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\minixml.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\receivedata.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h">
- <Filter>Header Files\ext\miniupnpc</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\PortMapper.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\libnatpmp\getgateway.h">
- <Filter>Header Files\ext\libnatpmp</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\libnatpmp\natpmp.h">
- <Filter>Header Files\ext\libnatpmp</Filter>
- </ClInclude>
- <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h">
- <Filter>Header Files\ext\libnatpmp</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\Binder.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- <ClInclude Include="..\..\osdep\ManagedRoute.hpp">
- <Filter>Header Files\osdep</Filter>
- </ClInclude>
- </ItemGroup>
- <ItemGroup>
- <ResourceCompile Include="ZeroTierOne.rc">
- <Filter>Resource Files</Filter>
- </ResourceCompile>
- </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ <Filter Include="Source Files\node">
+ <UniqueIdentifier>{67b1c0f8-b018-4169-9c14-7032ed12c786}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\include">
+ <UniqueIdentifier>{40761a4c-e8db-4a91-9cab-7afef332f4a8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\node">
+ <UniqueIdentifier>{da3b8126-840c-45db-8abe-9d7e7976f8be}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\osdep">
+ <UniqueIdentifier>{6054dfae-4ed2-4d69-8cf5-d6f27646f2d7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\service">
+ <UniqueIdentifier>{9944293a-4a1a-40e9-b92a-eff31fe87e2c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\osdep">
+ <UniqueIdentifier>{ca21bd6b-ff4e-4f9e-bedd-c9f603d2d0d6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\service">
+ <UniqueIdentifier>{e1743b3c-1d18-47f1-ab5a-f5703c19f1df}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext">
+ <UniqueIdentifier>{71865460-d693-4c73-84f6-dbff42f49df6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\http-parser">
+ <UniqueIdentifier>{17ae9a01-d39f-4c6d-a800-8f2cd0804c96}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext">
+ <UniqueIdentifier>{7784af31-5b60-4300-b07e-44cf864c54db}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext\http-parser">
+ <UniqueIdentifier>{f8a1c208-15b8-4d85-a4cb-11d2b82f2d1e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\windows">
+ <UniqueIdentifier>{43f75f84-c70d-4d44-a0ef-28a7a399abd4}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\windows\ZeroTierOne">
+ <UniqueIdentifier>{0da07a2f-8922-4827-ac51-29ca3f30f881}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\windows">
+ <UniqueIdentifier>{b74916eb-bb6c-4449-a2a2-fa0b17f60121}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\windows\ZeroTierOne">
+ <UniqueIdentifier>{bf604491-14c4-4a74-81a6-6105d07c5c7c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\miniupnpc">
+ <UniqueIdentifier>{5423fb64-896b-432e-a19d-88d4467f89f9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext\miniupnpc">
+ <UniqueIdentifier>{56cc3ab8-3336-4a22-9471-c267ee46cd54}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\libnatpmp">
+ <UniqueIdentifier>{d7292d0d-72a0-4ed6-b717-21debb120737}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\ext\libnatpmp">
+ <UniqueIdentifier>{409ec37e-ff36-4c13-b18d-52d6052e0ca2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\controller">
+ <UniqueIdentifier>{3cad34c8-c436-43ae-8323-57803637c832}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\json">
+ <UniqueIdentifier>{ff20532b-d9a2-440d-a7b4-b49e26a9b2f8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\ext\x64-salsa2012-asm">
+ <UniqueIdentifier>{05d9cde8-03ae-4e37-b9f7-7417de98cbe9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\controller">
+ <UniqueIdentifier>{7dc22e9c-f869-41e7-b43d-f07f5b94f6fb}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\service\OneService.cpp">
+ <Filter>Source Files\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\WindowsEthernetTap.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\Http.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\OSUtils.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\C25519.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Identity.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\IncomingPacket.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\InetAddress.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Multicaster.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Network.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\NetworkConfig.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Node.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Packet.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Peer.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Poly1305.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Salsa20.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SelfAwareness.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SHA512.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Switch.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Topology.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Utils.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\http-parser\http_parser.c">
+ <Filter>Source Files\ext\http-parser</Filter>
+ </ClCompile>
+ <ClCompile Include="ServiceBase.cpp">
+ <Filter>Source Files\windows\ZeroTierOne</Filter>
+ </ClCompile>
+ <ClCompile Include="ServiceInstaller.cpp">
+ <Filter>Source Files\windows\ZeroTierOne</Filter>
+ </ClCompile>
+ <ClCompile Include="ZeroTierOneService.cpp">
+ <Filter>Source Files\windows\ZeroTierOne</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Path.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\connecthostport.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\igd_desc_parse.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\minisoap.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\minissdpc.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\miniupnpc.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\miniwget.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\minixml.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\portlistingparse.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\receivedata.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnpcommands.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnpdev.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnperrors.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\miniupnpc\upnpreplyparse.c">
+ <Filter>Source Files\ext\miniupnpc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\PortMapper.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\libnatpmp\getgateway.c">
+ <Filter>Source Files\ext\libnatpmp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\libnatpmp\natpmp.c">
+ <Filter>Source Files\ext\libnatpmp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\ext\libnatpmp\wingettimeofday.c">
+ <Filter>Source Files\ext\libnatpmp</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\osdep\ManagedRoute.cpp">
+ <Filter>Source Files\osdep</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Membership.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Capability.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Revocation.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Tag.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\EmbeddedNetworkController.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\service\SoftwareUpdater.cpp">
+ <Filter>Source Files\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\one.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\selftest.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Trace.cpp">
+ <Filter>Source Files\node</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\DB.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\FileDB.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\controller\RethinkDB.cpp">
+ <Filter>Source Files\controller</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\ZeroTierOne.h">
+ <Filter>Header Files\include</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Http.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\OSUtils.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Phy.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Thread.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\WindowsEthernetTap.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\service\OneService.hpp">
+ <Filter>Header Files\service</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Address.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\AtomicCounter.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Buffer.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\C25519.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Constants.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Dictionary.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Identity.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\IncomingPacket.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\InetAddress.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MAC.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Multicaster.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MulticastGroup.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Mutex.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Network.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkConfig.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkController.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Node.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Packet.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Path.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Peer.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Poly1305.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Salsa20.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SelfAwareness.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SHA512.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SharedPtr.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Switch.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Topology.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Utils.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\http-parser\http_parser.h">
+ <Filter>Header Files\ext\http-parser</Filter>
+ </ClInclude>
+ <ClInclude Include="ServiceBase.h">
+ <Filter>Header Files\windows\ZeroTierOne</Filter>
+ </ClInclude>
+ <ClInclude Include="ServiceInstaller.h">
+ <Filter>Header Files\windows\ZeroTierOne</Filter>
+ </ClInclude>
+ <ClInclude Include="ZeroTierOneService.h">
+ <Filter>Header Files\windows\ZeroTierOne</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Hashtable.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\World.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\codelength.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\connecthostport.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\igd_desc_parse.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\minisoap.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\minissdpc.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpc_declspec.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniupnpctypes.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\miniwget.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\minixml.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\portlistingparse.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\receivedata.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnpcommands.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnpdev.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnperrors.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\miniupnpc\upnpreplyparse.h">
+ <Filter>Header Files\ext\miniupnpc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\PortMapper.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\libnatpmp\getgateway.h">
+ <Filter>Header Files\ext\libnatpmp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\libnatpmp\natpmp.h">
+ <Filter>Header Files\ext\libnatpmp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\libnatpmp\wingettimeofday.h">
+ <Filter>Header Files\ext\libnatpmp</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\Binder.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\osdep\ManagedRoute.hpp">
+ <Filter>Header Files\osdep</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\service\SoftwareUpdater.hpp">
+ <Filter>Header Files\service</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\json\json.hpp">
+ <Filter>Header Files\ext\json</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Credential.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\ext\x64-salsa2012-asm\salsa2012.h">
+ <Filter>Header Files\ext\x64-salsa2012-asm</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\EmbeddedNetworkController.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Trace.hpp">
+ <Filter>Header Files\node</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\DB.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\FileDB.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\controller\RethinkDB.hpp">
+ <Filter>Header Files\controller</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ZeroTierOne.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOneSDK.sln b/windows/ZeroTierOneSDK.sln
new file mode 100644
index 00000000..3c5c077a
--- /dev/null
+++ b/windows/ZeroTierOneSDK.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZeroTierOneSDK", "ZeroTierOneSDK\ZeroTierOneSDK.vcxproj", "{05E8E6B2-4067-4660-AD85-C8F6130FAFC5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x64.ActiveCfg = Debug|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x64.Build.0 = Debug|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x86.ActiveCfg = Debug|Win32
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Debug|x86.Build.0 = Debug|Win32
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x64.ActiveCfg = Release|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x64.Build.0 = Release|x64
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x86.ActiveCfg = Release|Win32
+ {05E8E6B2-4067-4660-AD85-C8F6130FAFC5}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj
new file mode 100644
index 00000000..bc719b6e
--- /dev/null
+++ b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{05E8E6B2-4067-4660-AD85-C8F6130FAFC5}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>ZeroTierOneSDK</RootNamespace>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(Platform)\$(Configuration)\</IntDir>
+ <TargetName>ZeroTierOne_x86d</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>ZeroTierOne_x64d</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(Platform)\$(Configuration)\</IntDir>
+ <TargetName>ZeroTierOne_x86</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)\Build\$(Platform)\$(Configuration)\</OutDir>
+ <TargetName>ZeroTierOne_x64</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;NOMINMAX;ZT_EXPORT;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;WIN32;NOMINMAX;ZT_EXPORT;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING_NO;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;ZT_EXPORT;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;ZT_EXPORT;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ImportLibrary>$(SolutionDir)lib\$(TargetName).lib</ImportLibrary>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Text Include="ReadMe.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\include\ZeroTierOne.h" />
+ <ClInclude Include="..\..\node\Address.hpp" />
+ <ClInclude Include="..\..\node\Array.hpp" />
+ <ClInclude Include="..\..\node\AtomicCounter.hpp" />
+ <ClInclude Include="..\..\node\Buffer.hpp" />
+ <ClInclude Include="..\..\node\C25519.hpp" />
+ <ClInclude Include="..\..\node\Capability.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp" />
+ <ClInclude Include="..\..\node\CertificateOfRepresentation.hpp" />
+ <ClInclude Include="..\..\node\Cluster.hpp" />
+ <ClInclude Include="..\..\node\Constants.hpp" />
+ <ClInclude Include="..\..\node\Credential.hpp" />
+ <ClInclude Include="..\..\node\Dictionary.hpp" />
+ <ClInclude Include="..\..\node\Hashtable.hpp" />
+ <ClInclude Include="..\..\node\Identity.hpp" />
+ <ClInclude Include="..\..\node\IncomingPacket.hpp" />
+ <ClInclude Include="..\..\node\InetAddress.hpp" />
+ <ClInclude Include="..\..\node\MAC.hpp" />
+ <ClInclude Include="..\..\node\Membership.hpp" />
+ <ClInclude Include="..\..\node\Multicaster.hpp" />
+ <ClInclude Include="..\..\node\MulticastGroup.hpp" />
+ <ClInclude Include="..\..\node\Mutex.hpp" />
+ <ClInclude Include="..\..\node\Network.hpp" />
+ <ClInclude Include="..\..\node\NetworkConfig.hpp" />
+ <ClInclude Include="..\..\node\NetworkController.hpp" />
+ <ClInclude Include="..\..\node\Node.hpp" />
+ <ClInclude Include="..\..\node\NonCopyable.hpp" />
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp" />
+ <ClInclude Include="..\..\node\Packet.hpp" />
+ <ClInclude Include="..\..\node\Path.hpp" />
+ <ClInclude Include="..\..\node\Peer.hpp" />
+ <ClInclude Include="..\..\node\Poly1305.hpp" />
+ <ClInclude Include="..\..\node\Revocation.hpp" />
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
+ <ClInclude Include="..\..\node\Salsa20.hpp" />
+ <ClInclude Include="..\..\node\SelfAwareness.hpp" />
+ <ClInclude Include="..\..\node\SHA512.hpp" />
+ <ClInclude Include="..\..\node\SharedPtr.hpp" />
+ <ClInclude Include="..\..\node\Switch.hpp" />
+ <ClInclude Include="..\..\node\Tag.hpp" />
+ <ClInclude Include="..\..\node\Topology.hpp" />
+ <ClInclude Include="..\..\node\Utils.hpp" />
+ <ClInclude Include="..\..\node\World.hpp" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\node\C25519.cpp" />
+ <ClCompile Include="..\..\node\Capability.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp" />
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp" />
+ <ClCompile Include="..\..\node\Cluster.cpp" />
+ <ClCompile Include="..\..\node\Identity.cpp" />
+ <ClCompile Include="..\..\node\IncomingPacket.cpp" />
+ <ClCompile Include="..\..\node\InetAddress.cpp" />
+ <ClCompile Include="..\..\node\Membership.cpp" />
+ <ClCompile Include="..\..\node\Multicaster.cpp" />
+ <ClCompile Include="..\..\node\Network.cpp" />
+ <ClCompile Include="..\..\node\NetworkConfig.cpp" />
+ <ClCompile Include="..\..\node\Node.cpp" />
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp" />
+ <ClCompile Include="..\..\node\Packet.cpp" />
+ <ClCompile Include="..\..\node\Path.cpp" />
+ <ClCompile Include="..\..\node\Peer.cpp" />
+ <ClCompile Include="..\..\node\Poly1305.cpp" />
+ <ClCompile Include="..\..\node\Revocation.cpp" />
+ <ClCompile Include="..\..\node\Salsa20.cpp" />
+ <ClCompile Include="..\..\node\SelfAwareness.cpp" />
+ <ClCompile Include="..\..\node\SHA512.cpp" />
+ <ClCompile Include="..\..\node\Switch.cpp" />
+ <ClCompile Include="..\..\node\Tag.cpp" />
+ <ClCompile Include="..\..\node\Topology.cpp" />
+ <ClCompile Include="..\..\node\Utils.cpp" />
+ <ClCompile Include="dllmain.cpp">
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters
new file mode 100644
index 00000000..d7e947f3
--- /dev/null
+++ b/windows/ZeroTierOneSDK/ZeroTierOneSDK.vcxproj.filters
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Text Include="ReadMe.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="targetver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\include\ZeroTierOne.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Node.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Utils.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Address.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Array.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\AtomicCounter.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Buffer.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\C25519.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Capability.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfMembership.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfOwnership.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\CertificateOfRepresentation.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Cluster.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Constants.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Credential.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Dictionary.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Hashtable.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Identity.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\IncomingPacket.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\InetAddress.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MAC.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Membership.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Multicaster.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\MulticastGroup.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Mutex.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Network.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkConfig.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NetworkController.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\NonCopyable.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\OutboundMulticast.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Packet.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Path.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Peer.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Poly1305.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Revocation.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\RuntimeEnvironment.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Salsa20.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SelfAwareness.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SHA512.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\SharedPtr.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Switch.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Tag.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\Topology.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\node\World.hpp">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Node.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\C25519.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Capability.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfMembership.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\CertificateOfOwnership.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Cluster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Identity.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\IncomingPacket.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\InetAddress.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Membership.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Multicaster.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Network.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\NetworkConfig.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\OutboundMulticast.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Packet.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Path.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Peer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Poly1305.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Revocation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Salsa20.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SelfAwareness.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\SHA512.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Switch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Tag.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\node\Topology.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/ZeroTierOneSDK/dllmain.cpp b/windows/ZeroTierOneSDK/dllmain.cpp
new file mode 100644
index 00000000..e263848d
--- /dev/null
+++ b/windows/ZeroTierOneSDK/dllmain.cpp
@@ -0,0 +1,22 @@
+// dllmain.cpp : Defines the entry point for the DLL application.
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+#include "targetver.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/windows/ZeroTierOneSDK/targetver.h b/windows/ZeroTierOneSDK/targetver.h
new file mode 100644
index 00000000..90e767bf
--- /dev/null
+++ b/windows/ZeroTierOneSDK/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/windows/copyutil/App.config b/windows/copyutil/App.config
new file mode 100644
index 00000000..88fa4027
--- /dev/null
+++ b/windows/copyutil/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
+ </startup>
+</configuration> \ No newline at end of file
diff --git a/windows/copyutil/Program.cs b/windows/copyutil/Program.cs
new file mode 100644
index 00000000..f65a5771
--- /dev/null
+++ b/windows/copyutil/Program.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace copyutil
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ if (args.Length != 2)
+ {
+ Console.WriteLine("Not enough arguments");
+ return;
+ }
+
+ if (!Directory.Exists(args[0]))
+ {
+ Console.WriteLine("Source directory doesn't exist!");
+ return;
+ }
+
+ Console.WriteLine("Creating: " + args[1]);
+ DirectoryInfo di = Directory.CreateDirectory(args[1]);
+
+ String authTokenSrc = args[0] + "\\authtoken.secret";
+ String authTokenDest = args[1] + "\\authtoken.secret";
+
+ String portSrc = args[0] + "\\zerotier-one.port";
+ String portDest = args[1] + "\\zerotier-one.port";
+
+ File.Copy(authTokenSrc, authTokenDest, true);
+ File.Copy(portSrc, portDest, true);
+ }
+ }
+}
+ \ No newline at end of file
diff --git a/windows/copyutil/Properties/AssemblyInfo.cs b/windows/copyutil/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..5c4c6466
--- /dev/null
+++ b/windows/copyutil/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("copyutil")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("copyutil")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6d27214a-087b-4484-b898-ad2a13fa3b9e")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/windows/copyutil/copyutil.csproj b/windows/copyutil/copyutil.csproj
new file mode 100644
index 00000000..099208fd
--- /dev/null
+++ b/windows/copyutil/copyutil.csproj
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{6D27214A-087B-4484-B898-AD2A13FA3B9E}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>copyutil</RootNamespace>
+ <AssemblyName>copyutil</AssemblyName>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/zerotier-one.spec b/zerotier-one.spec
index 9f242ee1..2e78cba9 100644
--- a/zerotier-one.spec
+++ b/zerotier-one.spec
@@ -1,27 +1,24 @@
Name: zerotier-one
-Version: 1.1.14
-Release: 0.1%{?dist}
+Version: 1.2.6
+Release: 1%{?dist}
Summary: ZeroTier One network virtualization service
License: GPLv3
URL: https://www.zerotier.com
-Source0: %{name}-%{version}.tar.gz
%if 0%{?rhel} >= 7
BuildRequires: systemd
%endif
%if 0%{?fedora} >= 21
-BuildRequires: lz4-devel
-BuildRequires: libnatpmp-devel
BuildRequires: systemd
-BuildRequires: json-parser-devel
%endif
Requires: iproute
%if 0%{?rhel} >= 7
Requires: systemd
+Requires(pre): /usr/sbin/useradd, /usr/bin/getent
%endif
%if 0%{?rhel} <= 6
@@ -29,19 +26,8 @@ Requires: chkconfig
%endif
%if 0%{?fedora} >= 21
-Requires: lz4
-Requires: libnatpmp
Requires: systemd
-Requires: json-parser
-%endif
-
-Provides: bundled(http-parser) = 2.7.0
-Provides: bundled(miniupnpc) = 2.0
-
-%if 0%{?rhel} >= 6
-Provides: bundled(json-parser) = 1.1.0
-Provides: bundled(lz4) = 1.7.1
-Provides: bundled(libnatpmp) = 20131126
+Requires(pre): /usr/sbin/useradd, /usr/bin/getent
%endif
%description
@@ -57,33 +43,43 @@ like conventional VPNs or VLANs. It can run on native systems, VMs, or
containers (Docker, OpenVZ, etc.).
%prep
-rm -rf *
-ln -s %{getenv:PWD} %{name}-%{version}
-tar --exclude=%{name}-%{version}/.git --exclude=%{name}-%{version}/%{name}-%{version} -czf %{_sourcedir}/%{name}-%{version}.tar.gz %{name}-%{version}/*
-rm -f %{name}-%{version}
-cp -a %{getenv:PWD}/* .
+#rm -rf *
+#ln -s %{getenv:PWD} %{name}-%{version}
+#tar --exclude=%{name}-%{version}/.git --exclude=%{name}-%{version}/%{name}-%{version} -czf %{_sourcedir}/%{name}-%{version}.tar.gz %{name}-%{version}/*
+#rm -f %{name}-%{version}
+#cp -a %{getenv:PWD}/* .
%build
-%if 0%{?rhel} <= 7
-make CFLAGS="`echo %{optflags} | sed s/stack-protector-strong/stack-protector/`" CXXFLAGS="`echo %{optflags} | sed s/stack-protector-strong/stack-protector/`" ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one manpages selftest
-%else
-make CFLAGS="%{optflags}" CXXFLAGS="%{optflags}" ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one manpages selftest
+#%if 0%{?rhel} <= 7
+#make CFLAGS="`echo %{optflags} | sed s/stack-protector-strong/stack-protector/`" CXXFLAGS="`echo %{optflags} | sed s/stack-protector-strong/stack-protector/`" ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one manpages selftest
+#%else
+#make CFLAGS="%{optflags}" CXXFLAGS="%{optflags}" ZT_USE_MINIUPNPC=1 %{?_smp_mflags} one manpages selftest
+#%endif
+
+%pre
+%if 0%{?rhel} >= 7
+/usr/bin/getent passwd zerotier-one || /usr/sbin/useradd -r -d /var/lib/zerotier-one -s /sbin/nologin zerotier-one
+%endif
+%if 0%{?fedora} >= 21
+/usr/bin/getent passwd zerotier-one || /usr/sbin/useradd -r -d /var/lib/zerotier-one -s /sbin/nologin zerotier-one
%endif
%install
rm -rf $RPM_BUILD_ROOT
+pushd %{getenv:PWD}
make install DESTDIR=$RPM_BUILD_ROOT
+popd
%if 0%{?rhel} >= 7
mkdir -p $RPM_BUILD_ROOT%{_unitdir}
-cp debian/zerotier-one.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service
+cp %{getenv:PWD}/debian/zerotier-one.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service
%endif
%if 0%{?fedora} >= 21
mkdir -p $RPM_BUILD_ROOT%{_unitdir}
-cp debian/zerotier-one.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service
+cp ${getenv:PWD}/debian/zerotier-one.service $RPM_BUILD_ROOT%{_unitdir}/%{name}.service
%endif
%if 0%{?rhel} <= 6
mkdir -p $RPM_BUILD_ROOT/etc/init.d
-cp ext/installfiles/linux/zerotier-one.init.rhel6 $RPM_BUILD_ROOT/etc/init.d/zerotier-one
+cp %{getenv:PWD}/ext/installfiles/linux/zerotier-one.init.rhel6 $RPM_BUILD_ROOT/etc/init.d/zerotier-one
chmod 0755 $RPM_BUILD_ROOT/etc/init.d/zerotier-one
%endif
@@ -149,6 +145,15 @@ esac
%endif
%changelog
+* Mon Apr 24 2017 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.2.2-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
+* Fri Mar 17 2017 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.2.2-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
+* Tue Mar 14 2017 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.2.0-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
* Tue Jul 12 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.10-0.1
- see https://github.com/zerotier/ZeroTierOne for release notes