Archived
Refactor node ID handling, use check digits (fixes #269)
New node ID:s contain four Luhn check digits and are grouped differently. Code uses NodeID type instead of string, so it's formatted homogenously everywhere.
This commit is contained in:
1 parent
fee8289c0a
commit
8f3effed32
35 files changed
+563
-478
No files matched your search
+2
-2
@@ -18,7 +18,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["angular.min.js"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec3d6b73db3892dffd2b30bacc887264ca4e66e7f6e238531927b3ebcdcb15277357e7f154512224714c910a1f7154b6fffb750320892749c54976b7ea54bbb104341a8d46a3d1dd68602613729cae3759b45816c43b1e9107fb073f927f0497e994fc92660b122421498b25cdc82c4d8a2c9a96459ae53e791ac784b5ca4946739a7da4a1bf03d8dee794a473522ca39ce46999cd28340c29819f8bf423cd121a92e906d0925727eff6f26213531247339a40bb621914640655538aa8e669099d47099453f2f2e4f8f9ebb3e7641ec5d4dfd999ecfe99c751529069965e41f78f48919574cc888c929256bfd77199e3fff96fb23b81968b389d0631b9f788cc833807a0205994719089df08b4332ce14b0ee39d15c3c39d9d8f4146f24d328361250b7254b5f0576958c6d41bd675c33139bf181db20665164f0340734486c02286a786f381d079b4f0e62514446942bc7bcba2589f66e9c728a4d9885cef10f828857e48e7411917b9ff29cfe67fa70194bd0e56ac83ffd93b3e7bfbebdebbf49226d05147dbe334bd8c68d55669793bd2c92cb2348e69e60dcfaad2e3228b61a012edf92c5d0323599715edc88275463f3e0b0aec66ffb02e5dd0e2cd0b28c229694a914741567006b399004ad84018762425a1acc31c00ae6f0fb54a60a759beda9c3cc3310e95d2042412919c5f58909c243850853c51bfced2229da5f1f112e61fe4b8265382a15906ebc3c49d539a3cc73a931a10221a9ba467749d5a468ac559014c0d5c75a7c0f4885ed9b998501a3e6db8c8aaf033cc56c34764f88cc2ccca8561948972e2c1f7915c8b628295c82fb9bc48cbd9122bdeaf43987c5175ab937132731091d115a80a2b1d66554544985e25711a84564282bca059945fd6a4b03fa063ce6089278b1c34ce3ccd2899a6699c931896079414d0469dc2a260c030b9acfc3a0a01f7cb0890278008960488d52c131c21a7425c0887204fc310643ca73900169b35e8a861413f15c3dbb184ed55f0e98c26e18be93a97f0bd298b458a2be32d2ea697d12a026dfd22fa65928f1a6449b99ad24c45f7161004c9490243f918c467124a5e43aa2ae275a3122bd08e4d546e81f034c802d02cf15bfaa184b52f8f17b84060cca01092900dbb01694108adf8d2d4b887d87e857d83f05acec416f6c9939bce82f869929cc2ba9230b262f22c02c1803d6d4344750b6d15a2e749308d69e8c6d5a041615491fc8ded5c562cbcaa1f9a33d4b3bff08d53165a2c264db9b3fdfbd3e4d4a4809710ac6c6dfcd66cfa344993cd2a2d73301f029c20a6c7f8766a4174a1aab445199dd997a65871329bde9f742dc771b513719b41255ee518627b5a82790246c72c607be17b85779d084f833cbf4ab3b01da9042510af9b92566adfbd9457291a677f7ff7eef40c6cab8c40471a7fdb903d3d3d794137f2a49d9e105e225004ebe8127e6b93545b09b0eb9f95b319287e1a7a2349eb4773e27dc76c02b9549ae028890a6f74a85679c3ff4868013cb864dbea7084fb68107bc325d83b430ddab4389a1da922a1b13fb624a369d84e04c2e6cbb2c09daa1dd2660aa944df9accfd3500156772d63dac8c166596b8d8619f1017d7afa7c1ec32ccd235c801f407920b7201d2304d832c1486f5ad6352da06581b367318c712816ba353a68d199e3e20f42aa3fb3e194ef20d2cf415d098a3dce5b9646d835d12e8835305f4d026021c239081ed259247dceef3ecd409e4d504c9ed9aef6fa67fc2f6e903cf724fb600473eacd5e7c16c29218f421d3dce182e4370bd64abf23c0a2fc8d111989ce04ad17904ded7506f8a1fb0845e53d836c89c16b325ba68cc1e32e0ec7c665dfd8cd41e0da18026e8f1bd7f7b729caed669027a0c09ee3b0b1ac7a561684cb73191fd2614bd38136b0bf113187b0e247de941e0b460bd2ff09327c6b8aa2a178a6fccf6add9ef9a86babc7bc2acebc13e66c909ecbdaed1b94c52f487d01505e7efca1b8d1588027d390f61f66a97754426e4607f7f5f858c42b1ad551fc9c385f6eac070938539c010868be56cef6312b20cf2375709f80c6b9a151b36758e29aa621c96a5609414609cdab160a74cfea204ac6520ff55502cfd55f0c9db1f93bf925dce4b067192fcb22968fe2e2dc0c2dcb3f8e3061432af081d1251e34dcba2ab6b7002faf4ad80393bbf25604fcd96c4a32ed61a4cd9ef3b040be0ad43caf1638d69e89b4ad762e0b186deeb408f50d8bbbb3ddcd1430ae0181465ae6cbe58ac5b1ab6fd07e13a77206e8880799a5c26681bc9b680d283891a260a7c4d5863df61174ecc6745ba5e43cf56cc2cb857f0353c444d6ae986d537ad2b70f6f77cffc22fd2f7d041760c53035bff7d5e01f332cd8bcc3b181daae310cd8fc8000305609a0dc8cd0d694a4fc2980ef4b1f0eafb504dbc414325c63f4e6936037d0fae939898fb64f0fd68601dad60893420cb8c1fc7e05f7cfd098f9279fa15663b442f3ffb6293adccd83082b971f62c16e2d065543768723ef14e4ceb2c5a05e0c6db3059d8a7cda12a145f7d22619f748dd8c25e1e7f67fa9ae1dfef875599c2f5ac80510104ec165639c185c5bb98d800241a0e75beb2dd681ea760cf433776b58811648b5ac4e2e3b9e26921b5a8e601ceb2798906fe6bf87bf2ec42153a04b419fa58eea3951753d62d32115861dbd46ad5ba2645caec9f614f6bb9569d5c5089878b45ef1976a1ef473ac616ad3364f12936fc5a1b5b388b91e17f0fbea697dbf253b8b2db306d05d65eeee696a9acff65d965eac75e3c33956127cfdcba11c78ab1bf6fcc314118639a083db6e9f69f5be65b62f93f6d089a1268dda7dc43f98d764e0312a352ca044d3adf73902920847b6da3ef6bb2278e60effdcddd77c73cc3761b62c70677d4e1e20856e8d3b0e34cf9741330c405952352486fd56dd2b09270ac878abf81431478fd98268b6209261739708cb8b60f5a062ab081adec1406711edd290dc25a11b53d4d94c1c065a1d4f30cdd3b1acb202d635425a6b2fec1a7fdc96e446092c46bdbc0f5294eb82468cd3c6dc224bef4658a5772978b351ab5b2a88b3f7d98d39b33348c0ae94cc7ce99c9044cb54b4a0282f168b451a0eda6aeaeb8b55abf59571e7695b781809e72eaef0b20d9e9d611f8f5d915868b6cd54f617b5b8369439e80556bc384873bad640080dc108f0c041ffac6dad9c98d9dad79f011760f91295131d590b6192e2af28fb337affd9c1d8f47738d4a89426c90ae0b768ebf642929f923723d845e0a50807bef4026d901fc7a1d8bd3acc99f799a0c6f6ff5e8c63acd8d581ff405630382c6ac135ba4c38872b8e286806bc29205b60989da334358c04e2ed30c134b88df3213bd053c2f33f8275d519e7e34e3e927ea9cd52929df55c2453f94419cdba57c6c0af788dcdc18c3e79f769420b16355c235bd2468d3d98ba9674b3abb4498921dfbLine truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec3cfd73dbb692bffbaf40756945d93265277dbd77519c4eeaa4eff9e5cb1327bd9b71dd194a8424d614a9f023b6cef6ff7ebb0048e293a2e224f7deccd3b4b1042c168bc562b1bb58603422c7e96a9d45f34541bce301797870f823f94770994ec82f693627411292b458d08c4cd3a4c8a24959a459ee9367714c58ab9c6434a7d9271afa3b80ed434e493a23c522ca499e96d99442c39012f8394f3fd12ca12199ac012d797df27e3f2fd631257134a509b42b164141a65035a1886a9696d07994403925af4e8e5fbc397b4166514cfd9d9dd1ee9f791c25059964e91574ff98145949878cc8282969f57b159739fecf7f93dd11b49cc7e92488c983c76416c4390005c9bc8c834cfc46a09d7e095f7218efb4e88f77763e0519c9d7c9148695ccc951d5c25fa6611953af5fd7f587e4fc6230660dca2c9e0480e688f481450c4f0de703a1b368eecd4a2888d284780f1645b13acdd24f5148b301b9d921f0510afd90ce82322e72ff3acf667fa70194bd0996ac83ffd93f3e7bf7ebfefbf49226d0d186b6c7697a19d1aaadd2f26ea0935964691cd3cceb9f55a5c74516c34025daf369ba0246b22e2bda9105ab8c7e7a1e14d8cdc1b82e9dd3e2ed4b28c229694a914741567006b399004ad84018762425a1acc31c006eeec65a25b0d32c5fae4f9ee318fb4a6902128948ce2f2c484e121ca8429ea85f6569914ed3f87801f30f725c9329c1d02c83f561e2ce294d5e609d490d08118d4dd233ba4a2d23c5e2ac00a606aeba53607a44afec5c4c280d9f355c6455f8e967cbfe63d27f4e6166e5c230ca4439f1e0fb40ae4531c14ae4975c5ea4e57481151f56214cbea8bad3c938993a88c8e8125485950eb3aa22224caf92380d422b21415ed02cca2f6b52d81fd03167b0c493790e1a679666944cd234ce490ccb034a0a68a34e61513060985c567e138580fb5504c81340044b02c46a9a098e9053212e844390676108329ed31c008bf50a7454bfa0d745ff6e28617b1d5c9fd1247c3959e512beb765314f7165bcc3c5f42a5a46a0ad5f46bf8cf241832c2997139aa9e8de018220394960289f82f84c42c96b485545bccda8c40ab46313955b203c0db200344bfc8e7e2c61edcbe3052e10183328842464c36e405a10422bbe3435ee21b65f61df20bc9633b1857df2e4a6d3207e9624a7b0ae248cac983c8f4030604f5b1351dd425b85e845124c621aba713568501855247f633b97150bafea86e60cf5ec2f7ce39485168b4953ee6cffe134393529e025042b5b1bbf339b3e4bd264bd4ccb1ccc87002788e931be9d5a105da82a6d5e4667f6a529569ccca60f279b96e3b0da89b8cda012af720cb13d2bc13c01a3631ab0bdf083c2bb8d084f833cbf4ab3b01da9042510af9a92566adfbf9257291a677f7ffffef40c6cab8c40471a7fdb903d3b3d7949d7f2a49d9e105e225004abe8127e6b93545b09b0eb9f95d329287e1a7a0349eb4733e27dc76c02b9549ae028890a6f3056abbcfe7f24b4001e5cb26db53fc07d3488bdfe02ec9dbe066d5a1ccd8e5491d0d81f5b92d1346c270261f34559e04ed50e69338554a2ef4ce6fe1a808a3339eb1e56468b324b5cecb04f888beb3793607a1966e90ae400fa03c905b9006998a441160ac3face31296d03ac0d9b198c6381c0b5d129d3c60c4f1f107a95d1bd47faa37c0d0b7d0934e62877792e59db609704fae054011ddb4480630432b0bd44f280db7d9e9d3a81bc9a20b95df3fdede44fd83e7de059eec916e0c087b5fa22982e24e451a8a3c719c36508ae976c559e47e105393a0293135c293a8bc0fbeaeb4df10396d01b0adb0699d162ba40178dd943069c9dcfacab9f91daa33e14d0043dbe0fef4e8ed3e52a4d408f21c15d6741e3b8340c8de93626b2df84a21767626d217e0463cf81a42f3d089c16acf7057ef2d4185755e542f18dd9be35fb5dd350976f9e30eb7ab08f5972023baf6b742e9314fd217445c1f9bbf2064305a2405fce4398fdda651d9011393c38385021a3506c6bd547f270a1bd3a30dc64610e3084e16239dbfb98842c82fced55023ec38a66c59a4d9d638aaa1887652918250518a7762cd82993bf28016b19c87f1d140b7f195c7b0743f257b2cb79c9204e925fd605cddfa7055898fb167fdc8042e615a143226abc69596cea1a9c802e7d2b60ceceef08d853d305f1a88bb506530eba0ec10278e79072fc58631afaa6b26931f05843e775a04728ecdddd8d77f49002380645992b9b2f16eb96866dff41b88d3b103744c03c4d2e13b48d645b40e9c1440d1305be26acb1efb00b27e6b3225dada0672b6616dc2bf81aeea326b574c3ea9bd61538fb7b7e70e117e907e8203b86a981ad7f8f57c0bc4cf222f30e0763751ca2f911e961a0004cb31eb9bd254de94918d39e3e165ebd07d5c4eb355462fce3946653d0f7e03a8989d923bdef073deb68054ba4015966fc3806ffe2eb4f7894ccd2af30db217af9d9179b6c65c6fa11cc8db367b110fb2ea3ba4193f38977625a65d1320037de86c9c23e6d0e55a1f8ea1309fba46bc416f6f2f83bd3d70cff4137acca14aea6058c0a2060b7b0ca092e2cdec5c80620d130d6f9ca76a3599c823d0fddd8d52246902d6a118b8f678aa785d4a29a0738cbe6251af86fe0efc9f30b55e810d066e863b98f565e4c59b7c84460856d53ab55eb8a1429b37ffa1dade55a757241251e2e16bd67d885be1fe8185bb44e9fc5a7d8f06b6d6ce12c4686ff35f89a5e6ecb4fe1ca6ec3b425587bb99b5ba6b2fea76597a91f3bf1cc54861b79e6d68d38568cfd7d638e09c218d344e8b14db7ffdc32df12cbffdf86a02981d67dca3d94dfe8c6694062544a99a049e77b0e32058470af6df47d4df6c411ecbdbfb9fbde30cfb0dd86d8b1c11d75b8388225fa34ec38533edd040c7141e58814d25b759b34ac241ceb58f137708802af1fd3645e2cc0e422878e11d7f641cb400536b0959dc220cea3374a83b056446d4713a5d7735928f53c43f78ec63248cb185589a9ac7ff0697fb21b119824f1c636707d8a132e095a334f9b30892f5d99e295dce5628d06ad2cdac49f2ecce9cc191a468574a663e7cc6804a6da252501c17834da28d0765d5757dc5aaedeae2a0fbbcadb40404f39f5f70590ec74eb08fcfaec0ac345b6ea67b0bdadc0b4214fc1aab561c2c39d563200406e884706820f5d63edece4c6ced63cf804bb87c894a8986a48db141715f9c7d9db377ece8ec7a39946a54421364857053bc75fb09494fc31b9e9432f0528c0fdf72093ec007eb58ac569d6e8cf3c4dfa77777a746395e646ac0ffa82b1014143d6892dd26144395c7143c03562c902db8444ed99212c60279769868925c46f9989ce029e9719fc932e294f3f9af2f41375ceea9494ef2ae1a21fcb20ceed523e34857b406e6f8de1f34f3b4a90d8a12ae19a5e12b4e9ecc5d4b3059d5e224cc98e7db3Line truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["app.js"] = bs
|
||||
@@ -63,7 +63,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["favicon.png"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec3d6977dc3872dffd2be0dec958de886cf918ef46963a9125cfae32e3e35976369b79933c3489eec68824680294dc2b6b7f7baa0abc8f6eaa25d9da9df960ab490085425da82a1cdcbb7ff4e6f0fd5fdfbe640b1306937b7bf71de7dea18a97899c2f0cdb3a7cc81eef3c7acafe939faa297ba19239e391cf94598884792a32899ca64625da650741c0a8956689d0223913be7bef83164ccd985948cdb44a134f402b5f30789cab339144c267d325c064af8edf3bda2c03c102e98908da990537cc83a2a9b8375329742b237829d88fc7872f5f9fbc64331908f79ee300da883d0b7834df1f8968c4a2b9c3e3787fa49791077d47737a45f8aa2010c9fee8242f39344930625ec0b5de1f61a540f1d3118214dc9fdc636c2f1486336fc1132dccfe283533e78fa3b260614cec888fa93cdb1ffdb7f3e1c0395461cc8d9c066244141211b43a7eb92ffcb9a8b48b7828f64767529cc72a3195aae7d2378b7d5f9c01191c7ad886814b2379e0688f0762ff91bbd302e40bed25323652451558ad6a3c350b95b46a04323a05ae05403128365e6a98f410d22211b3fdd18c9fe1a31b032127f7b081912610938288ec33bbb84026bf06e6be869eb61e5e5eee8d6dada2030b6caa94d126e1f1d8d37a5c3cb9a18c5c7833caf04051d00b218cc5d08a8659c63008233e196c4c258c4d95bf6417f493b198fb3e20e44c95312adc657fd8893f3dcfca66306867c643192c77d9e8cf223813467a9cbd16a9186db3e2c5363b4880dadb4cf3483b20ca7266415cdea33f69f0af8bef8a1e439ecc65e41815efb247ee7722acd5751159275491d23107e9bfe8c2e5958802b5cd5ea9887bf0f750455a81406eb3d121e88c04557b2dce01c3024ca30b0ec286d2eda3def81343a26b92098a4ebb14e9d55f3a0386f4971690fd95903b4b0bc87e4185a94a7c9158da452a6a8c2b5073d520f32edb795ee774e50d8171be2b191e2b2d51237651a64027cf9a1d486d9c4839d3148c8229baa2d724700e0a9c45add1100616a46154b4f1a58e030ebc9411c8ba70a681f24e733c40b4ad26833ce6f251080ed94c109db260cabdd37982160f7b5109a03f9f6e3d7ef26c9b3d7eba83ff3d7af8bc4ec184fb32d5bbec4965f0397d1ec59fd8d3f27d4ec8c7f0fa71fefab2392e10b3c8f539188e869c076206d8ee94825e1bdea39df235493e0fe41ce98f837cbe9e5639817343dca62ff282dd97215a4d1e990e4d8039a26876be904638a433d8f41c0c4dcd1a9c0b4bfda73b3b9d90fc9661b1e37f0c66651d16beab431e044e8d8abd08658dff2314bee46c2be49f329afee1d91fe24f0f0b00995ec1141b839900919ed83755ed2b2a3336fe3dc83e4cb4867156d85a06339941b32ddcb9bb5bd465bf07a2246065a630b1b2780194d6a0540c06a1ce19b26a9a087eaa711e0e14344f449c28681380088e35cc90309b9f4bb3a842b4f204fec1efc7c5eb0611122053ce95cb421cf7c6a48230138fed547c6f8f4648f30fb829ecbd8a415512860e00be8bf859318ff3332cb17fd0bce43f7d31e36900d32df80182eac939a749d3ce257bbe2c80e01cc961d0495686b3106845bd0f679a8043349aecc9709e97a0dd1a319d78388d39f8e43c7afc479a3d999ddd474f1ec3dc4ab2677f8f27ac984cf7486c72600be98301753ec16c57efdf4e2ca911d0fb672016144d3a67610237c96ae4234983ca3872ea547e92c21603a7193c6fe0272af6d579542985729e4defbf1b35eb01fde773f4865011b2872a9497be34df46531d3fdf9b16b4075982eef7c653f8c76b1d9598171d84224a6bd810be930227ebfd815f790a1e90efbf13b1da7a08d0abe49c07cb78815e0e2b7e390b1f396b09f7ad0801c303df67d81ce615952c11b5bd31f434b86be4cca0ae61f8e7c292a0d13d82e8edb8208d3c933e4aee15d003c3634eac61d08370f4d4bc815fdefc8a8491c3e9f231c1f8a1d9ed02ecd3f1d1ad50452f52834236083b359bb550b3cdaf481130ef862766a0a8cca0faa2d1f13b0ba1abdfbd711a5434bb525a96ec8d8158e4798fc1225803db631b6d7db4c8dfcb441b30ade7db4c45c1924130711e31396391f084d63c593e67195eec9c27114e4199f5cec003052490e23ec09fc9f9718446b1302800b834c535640227f4c1c656ed55a51c88220246ff3b59b775dbd5acebe08443b5f6164fea2514d28c26f9285e0be10b1f66a827938262fd6071fe6adaa978f27e8141318e374d682e620bae21e815e085f1330c90211c8b14cce01eb8b01c0cbe5b4e162c4c91e4193a3859679528528ec4791db4bb378eeba2b0166974db2b7360560d703280a90dc8ec43c1a7a98198dd448e0ee94f36e7b218bcec6c5eb91d49b7683410adca49207832939f461dbcaabfa83d561eb29f2d8917f334007f0304b421cf35c9cde161c37212a170836da19382bee5c31242a7943f5b25e40e860df188491f099bf520855e2dee996a5c5c6093432cd8c25feef111f80fc42b7814dc5898e8fde1df1f016de45a2fb91baa54ab07357b34ab518d3c8b9ae300740878ac737f22064f011319bfab8d377746f0250cec1b0951e827184c1b7cc3ab5be706304b25f74826c223f67d66943379cbcde2f2721df8520758c5b1b3304f0c376995f47597ad2aacbc454db440754e34453c634e2e1a55aad41991139815946ef536c4b2352b36e3966e5ed8d8a9da84351209d93304323246dfb7030ac2b1b142571996267d4558b8586f890c6fba3e1585464f0480ace8c1af39f199af9d49d6f111a5cefc5ee4c7fdd85f7f60369873542ca2c600bfa792eb8cacd0996b0d30f71142f003839f3292fdeccae88c07b24f1e068f3ff30f1c2de74d02bc4c12b5e9f8bb91fd9a8cf65408615333c040bf6291a848fe8ddc856b70bbc39a7dada1ce03356d860c7f82773ca8c57237c1d83981fd5e0642c3c4c08373bed4afd3702a92cb4b260d74bdcd7a1abd581a6a349511471579f1f508b65061935e3f2aef16c81520d42b528bdadc216279814a7d0763bc40f166bee00db8ee6a46eefa4614ebafcf7bec60041189a5e884edd40359758ee14aa192b993d4c19712c860b660930eaeb43c95ca1088b2eb46b1bf0fc398ece4fdeeb0177d6e5101f7ab8902e6b11b02f08a6b889eaeab38bdf51b5424fabd038ffb0d84dfa3c95f855e47ac0680fb0d08afd5dda5760a5395f04e4553e78ee7914a047b2b92506a0db398be7d9adb3eb14bbd39d96b40ee32e529d5eff0a0e53dd82580bf48b3d8707e20c89a12b448928ddd0628e90b00a088d64b3ac2aa56c444afbbe3b7eef085f7e440e244866012473d69d772c003b220e09a7b32683aa700a5c7daf622e5f3682e9251a7f160df7ecb86ce2cb8912391bee89859d62b71dc356b6600d9e10251d49d03eb568faeb07783644f355583b9e94a9286b87f63599a08800f4bcf144b57957c0cb6Line truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec3d6b77dbb692dff32b10dd6ee2dc3529e7d1dcbb8eac5dc74e5b6f9bc78993ed767bba7b20129250930443807654c7f7b7efcc806f91122ddb896fdb0f894502180ce68599c183a3bb87af0fdefdf4e6059b9b3018df19dd759c3b072a5e247236376cebe0017bb4f3f009fb4f7ea226ecb94a668c473e53662e12e6a9c82472921a956897ed0701a3569a25428be454f8ee9df75a303565662e35d32a4d3c01ad7cc1e071a64e4512099f4d160093bd3c7ae768b308040ba427226867e6dc300f8a26e2ce54a5d0ad8ce0a5603f1c1dbc7875fc824d6520dc3b8e036823f62ce0d16c6f20a2018b660e8fe3bd815e441ef41dcde815e1ab8240247b83e3bce4c024c1807901d77a6f809502c54f060852707f7c87b151280c67de9c275a98bd416aa6cedf0765c1dc98d8111f5279ba37f86fe7fdbe73a0c2981b3909c4802824226875f4624ff833516917f150ec0d4ea5388b55622a55cfa46fe67bbe38053238f4b00d039746f2c0d11e0fc4de43776709902fb497c8d8481555602d55e3a999ab64a94620a313e05a00148362e3a586490f21cd1331dd1b4cf9293eba3110727c071b186902312e88c83eb1f37364f22b60ee2be869ebc1c5c568686b151d586013a58c36098f879ed6c3e2c90d65e4c29b4186078a829e0b612c865634cc22864118f1d160632a616ca2fc053ba79f8cc5dcf7012167a28c51e12efbdb4efcf159563685413b531eca60b1cb06df89e05418e971f64aa462b0cd8a17db6c3f016a6f33cd23ed8028cba905717187fea4c1bfcebf2e7a0c793293916354bccb1eba5f8bb056d745649d50454ac71ca4ffbc0d9797220ad4367ba922eec1df0315690502b9cd0607a0331254ed9538030c0b308d2e38081b4ab78f7ae38f0d89ae49c6283acba548afeed22930a4bbb480ecaf84dc5a5a40f60b2a4c54e28bc4d22e5251635c819aa9069977d9ceb33aa72b6f088cf375c9f05869891ab18b32053a79daec406ae344ca99a460144cd115bd26817350e02c6a8d8630b0200da3a28d2f751c70e0a58c40d6853309947792e301a26d3519e431978f4270c86682e8940513ee9dcc12b478d88b4a00fdd964ebd1e3a7dbecd1931dfcefe18367750a26dc97a9de658f2b83cfe9f330fec89e94ef73423e82d78ff2d717cd71819845aecfc17034e43c1053c076a714f4daf01eee94af49f2792067487f1ce4b3f5b4ca099c1be265fa222fd85d19a2d5e49169d10498238a6667736984433a834dcfc0d0d4acc199b0d47fb2b3d30ac95f322c76fc8fc0acacc3c27775c883c0a951b113a1acf17f84c2979c6d85fc6346d3bf3dfd5bfcf1410120d32b986263301320d263fba6aa7d4565c6867f05d98789d630ce0a5bcb60263368b6853b73778bbaecaf409404accc04265616cf81d21a948ac120d41943564d12c14f34cec38182e6898813056d0210c1a186191266f33369e65588569ec03ff8ebb078dd20420264cab9725188e368482a0833f1d04ec5774634429a7fc04d61ef540caa92307400f05dc44f8b799c9f6289fd83e625ffe98b294f03986ec10f10544fce384d9a762e19f9b2008273248741275919ce42a015f53e9c49020ed1603c92e12c2f41bb35603af1701a73f0c979f8e8ef347b323bbb0f1e3f82b99564cffe1e8e5931998e486c726073e98301753ec26c57efdf4e2ca911d0fb272016148d5b67610237ce6ae4234983ca3872ea547e92c21603a7193c6fe0272af6d559542985729e4def7f1934eb01fd6733f4865011b2872a9417be34f7a2898e9f8d2605ed4196a0fbd17002ff78ada312f3a2835044690d1bc2775ce064bd3ff02b4fc003f2fdb722565b0f007a959cb36011cfd1cb61c52f67ee23672de1ee891030dcf77d86cd615e51c902511b0da1a7de5d23677a750dc33f1396048dee114467c70569e4a9f451722f811e181e736c0d83ee85a3a7660dfcf2e697248cec4f970f09c60fcd6ee7609f8e0e6f842a7a9e1a14b25ed8a9e9740935dbfc921401f36e78627a8aca14aacf1b1dbfb510dafa1d0dd3a0a2d995d2b26434046291e73d048b600d6c876db4f5d1227f23136dc0b49e6d3315050b06c1c459c4e49445c2135af364f18c6578b1339e44380565d63b030f1490408abb007f2a6747111ac5c2a000e0d214d790099cd0071b5bb5579572208a0818fdef64ddd66d57b3ae83130ed51acd1fd74b28a4198cf351bc12c2173ecc508fc705c5bac1e2fcd5b453f1f8dd1c83621c6f9ad05cc4e65c43d02bc00be3a718204338162998c13d706139187cb79c2c589822c9337470b2ce2a51a41c89b33a6877348ceba2b0166974db2b7360560d703280a90dc8ec43c1a7898198dd448e0ee94f36e7b218bcec6c5eb91949b7683410adca49207832951f072dbcaabfa83d561eb29f4b122f666900fe060868439e6b929bc3c386e52442e106db4227057dcb07258456297fba4ac81d0c1be201933e1236eb410abd5adc33d5383fc7260758b085bfdca343f01f8857f028b8b130d1fbc3bf3f00dac8b54e723754a9560f6a766856a31a791635c701e810f058e7fe440c9e022632fe521b6fee8ce04b18d85712a2d08f309865f00daf6e9d1bc02c95dc4399088fd8f78951cee40d37f38b8b75e04b1d6015c7cec23c36dca455d2d75db6aab0f2256aa205aa73a229e2197372d1a852a5ce889cc0aca0f4526f7d2c5bb362336e69e7858d9daa4d582391903d43202363f47d5ba0201c1b2bb4956169d2558485f3f596c8f0a6eb535168f44400c88a1efc9a139ff9da99641d1d52eaccef447ed88dfdd50766833947c5226a0cf01b2ab9cac80a9db9d200731f21043f30f83923d92fae8c4e7920bbe4a1f7f833ffc0d172d624c08b24519b8ebf1dd92fc9684f85103635030cf42be6898ae46fe42e5c81db2dd6ec4b0d7516a8493364f816def1a016cb5d07636704f61b19080d13030fcef842bf4ac389482e2e9834d0f536eb68f47c61a8d144461c55e4f99723d85c854d7afda0bc1b205780502f492d6a738b88e5052af51d8cf102c59bf982d7e0baab29b9eb1b51acbb3eefb08311442496a263b6530f64d519862b854ae64e520b5f4a20bdd9824d5ab8b2e4a9548640945d378abd3d18c67827ef77873def728b0ab85f4c14308fdd1080975c43f47455c5e9acdfa022d1ef2d78dcaf21fc1e8c7f127a1db11a00ee3620bc52b797da294c55c23b114d9d3b9a452a11ec8d4842a935cc62fae6696efbc42ef5e664af01b9cd94a754bfc38325efc12e01fc28cd7cc3f981206b4ad0224936761ba0a42b0080225a2f6909ab9622267add1ebfb5872fbc2307122732049338e848bb9603ee910501d7dc9341d33905281dd6b613299f4733910c5a8d07bb778ff59d59702347227dd132b3ac57e2b86dd6cc00b28339a2a85b07d6ae1e6d61ef06c99e6aaa0673d395240d71ffdab2341100ef979e2996ae2af9186c7d30Line truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["index.html"] = bs
|
||||
|
||||
+20
-15
@@ -5,27 +5,32 @@
|
||||
// Package cid provides a manager for mappings between node ID:s and connection ID:s.
|
||||
package cid
|
||||
|
||||
import "sync"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
)
|
||||
|
||||
type Map struct {
|
||||
sync.Mutex
|
||||
toCid map[string]uint
|
||||
toName []string
|
||||
toCid map[protocol.NodeID]uint
|
||||
toName []protocol.NodeID
|
||||
}
|
||||
|
||||
var (
|
||||
LocalName = "<local>"
|
||||
LocalID uint = 0
|
||||
LocalNodeID = protocol.NodeID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
LocalID uint = 0
|
||||
emptyNodeID protocol.NodeID
|
||||
)
|
||||
|
||||
func NewMap() *Map {
|
||||
return &Map{
|
||||
toCid: map[string]uint{"<local>": 0},
|
||||
toName: []string{"<local>"},
|
||||
toCid: map[protocol.NodeID]uint{LocalNodeID: LocalID},
|
||||
toName: []protocol.NodeID{LocalNodeID},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Map) Get(name string) uint {
|
||||
func (m *Map) Get(name protocol.NodeID) uint {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
@@ -36,7 +41,7 @@ func (m *Map) Get(name string) uint {
|
||||
|
||||
// Find a free slot to get a new ID
|
||||
for i, n := range m.toName {
|
||||
if n == "" {
|
||||
if n == emptyNodeID {
|
||||
m.toName[i] = name
|
||||
m.toCid[name] = uint(i)
|
||||
return uint(i)
|
||||
@@ -50,19 +55,19 @@ func (m *Map) Get(name string) uint {
|
||||
return cid
|
||||
}
|
||||
|
||||
func (m *Map) Name(cid uint) string {
|
||||
func (m *Map) Name(cid uint) protocol.NodeID {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
return m.toName[cid]
|
||||
}
|
||||
|
||||
func (m *Map) Names() []string {
|
||||
func (m *Map) Names() []protocol.NodeID {
|
||||
m.Lock()
|
||||
|
||||
var names []string
|
||||
var names []protocol.NodeID
|
||||
for _, name := range m.toName {
|
||||
if name != "" {
|
||||
if name != emptyNodeID {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
@@ -71,11 +76,11 @@ func (m *Map) Names() []string {
|
||||
return names
|
||||
}
|
||||
|
||||
func (m *Map) Clear(name string) {
|
||||
func (m *Map) Clear(name protocol.NodeID) {
|
||||
m.Lock()
|
||||
cid, ok := m.toCid[name]
|
||||
if ok {
|
||||
m.toName[cid] = ""
|
||||
m.toName[cid] = emptyNodeID
|
||||
delete(m.toCid, name)
|
||||
}
|
||||
m.Unlock()
|
||||
|
||||
+14
-7
@@ -4,28 +4,35 @@
|
||||
|
||||
package cid
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
m := NewMap()
|
||||
|
||||
if i := m.Get("foo"); i != 1 {
|
||||
fooID := protocol.NewNodeID([]byte("foo"))
|
||||
barID := protocol.NewNodeID([]byte("bar"))
|
||||
|
||||
if i := m.Get(fooID); i != 1 {
|
||||
t.Errorf("Unexpected id %d != 1", i)
|
||||
}
|
||||
if i := m.Get("bar"); i != 2 {
|
||||
if i := m.Get(barID); i != 2 {
|
||||
t.Errorf("Unexpected id %d != 2", i)
|
||||
}
|
||||
if i := m.Get("foo"); i != 1 {
|
||||
if i := m.Get(fooID); i != 1 {
|
||||
t.Errorf("Unexpected id %d != 1", i)
|
||||
}
|
||||
if i := m.Get("bar"); i != 2 {
|
||||
if i := m.Get(barID); i != 2 {
|
||||
t.Errorf("Unexpected id %d != 2", i)
|
||||
}
|
||||
|
||||
if LocalID != 0 {
|
||||
t.Error("LocalID should be 0")
|
||||
}
|
||||
if i := m.Get(LocalName); i != LocalID {
|
||||
t.Errorf("Unexpected id %d != %c", i, LocalID)
|
||||
if i := m.Get(LocalNodeID); i != LocalID {
|
||||
t.Errorf("Unexpected id %d != %d", i, LocalID)
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -46,12 +46,12 @@ func connect(target string) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
myID := string(certID(cert.Certificate[0]))
|
||||
myID := protocol.NewNodeID(cert.Certificate[0])
|
||||
|
||||
tlsCfg := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
NextProtos: []string{"bep/1.0"},
|
||||
ServerName: myID,
|
||||
ServerName: myID.String(),
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
SessionTicketsDisabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
@@ -63,7 +63,7 @@ func connect(target string) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
remoteID := certID(conn.ConnectionState().PeerCertificates[0].Raw)
|
||||
remoteID := protocol.NewNodeID(conn.ConnectionState().PeerCertificates[0].Raw)
|
||||
|
||||
pc = protocol.NewConnection(remoteID, conn, conn, Model{})
|
||||
|
||||
@@ -82,7 +82,7 @@ func prtIndex(files []protocol.FileInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) Index(nodeID string, repo string, files []protocol.FileInfo) {
|
||||
func (m Model) Index(nodeID protocol.NodeID, repo string, files []protocol.FileInfo) {
|
||||
log.Printf("Received index for repo %q", repo)
|
||||
if cmd == "idx" {
|
||||
prtIndex(files)
|
||||
@@ -121,7 +121,7 @@ func getFile(f protocol.FileInfo) {
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
func (m Model) IndexUpdate(nodeID string, repo string, files []protocol.FileInfo) {
|
||||
func (m Model) IndexUpdate(nodeID protocol.NodeID, repo string, files []protocol.FileInfo) {
|
||||
log.Printf("Received index update for repo %q", repo)
|
||||
if cmd == "idx" {
|
||||
prtIndex(files)
|
||||
@@ -131,16 +131,16 @@ func (m Model) IndexUpdate(nodeID string, repo string, files []protocol.FileInfo
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) ClusterConfig(nodeID string, config protocol.ClusterConfigMessage) {
|
||||
func (m Model) ClusterConfig(nodeID protocol.NodeID, config protocol.ClusterConfigMessage) {
|
||||
log.Println("Received cluster config")
|
||||
log.Printf("%#v", config)
|
||||
}
|
||||
|
||||
func (m Model) Request(nodeID, repo string, name string, offset int64, size int) ([]byte, error) {
|
||||
func (m Model) Request(nodeID protocol.NodeID, repo string, name string, offset int64, size int) ([]byte, error) {
|
||||
log.Println("Received request")
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (m Model) Close(nodeID string, err error) {
|
||||
func (m Model) Close(nodeID protocol.NodeID, err error) {
|
||||
log.Println("Received close")
|
||||
}
|
||||
+3
-11
@@ -5,20 +5,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/base32"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func loadCert(dir string) (tls.Certificate, error) {
|
||||
return tls.LoadX509KeyPair(filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem"))
|
||||
}
|
||||
|
||||
func certID(bs []byte) string {
|
||||
hf := sha256.New()
|
||||
hf.Write(bs)
|
||||
id := hf.Sum(nil)
|
||||
return strings.Trim(base32.StdEncoding.EncodeToString(id), "=")
|
||||
cf := filepath.Join(dir, "cert.pem")
|
||||
kf := filepath.Join(dir, "key.pem")
|
||||
return tls.LoadX509KeyPair(cf, kf)
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func restGetSystem(w http.ResponseWriter) {
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
res := make(map[string]interface{})
|
||||
res["myID"] = myID
|
||||
res["myID"] = myID.String()
|
||||
res["goroutines"] = runtime.NumGoroutine()
|
||||
res["alloc"] = m.Alloc
|
||||
res["sys"] = m.Sys
|
||||
|
||||
@@ -61,7 +61,7 @@ func init() {
|
||||
|
||||
var (
|
||||
cfg config.Configuration
|
||||
myID string
|
||||
myID protocol.NodeID
|
||||
confDir string
|
||||
logFlags int = log.Ltime
|
||||
rateBucket *ratelimit.Bucket
|
||||
@@ -181,8 +181,8 @@ func main() {
|
||||
l.FatalErr(err)
|
||||
}
|
||||
|
||||
myID = certID(cert.Certificate[0])
|
||||
l.SetPrefix(fmt.Sprintf("[%s] ", myID[:5]))
|
||||
myID = protocol.NewNodeID(cert.Certificate[0])
|
||||
l.SetPrefix(fmt.Sprintf("[%s] ", myID.String()[:5]))
|
||||
|
||||
l.Infoln(LongVersion)
|
||||
l.Infoln("My ID:", myID)
|
||||
@@ -263,7 +263,7 @@ func main() {
|
||||
tlsCfg := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
NextProtos: []string{"bep/1.0"},
|
||||
ServerName: myID,
|
||||
ServerName: myID.String(),
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
SessionTicketsDisabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
@@ -567,7 +567,7 @@ func saveConfig() {
|
||||
saveConfigCh <- struct{}{}
|
||||
}
|
||||
|
||||
func listenConnect(myID string, m *model.Model, tlsCfg *tls.Config) {
|
||||
func listenConnect(myID protocol.NodeID, m *model.Model, tlsCfg *tls.Config) {
|
||||
var conns = make(chan *tls.Conn)
|
||||
|
||||
// Listen
|
||||
@@ -673,7 +673,7 @@ next:
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
remoteID := certID(certs[0].Raw)
|
||||
remoteID := protocol.NewNodeID(certs[0].Raw)
|
||||
|
||||
if remoteID == myID {
|
||||
l.Infof("Connected to myself (%s) - should not happen", remoteID)
|
||||
|
||||
+3
-10
@@ -11,14 +11,12 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
mr "math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -28,14 +26,9 @@ const (
|
||||
)
|
||||
|
||||
func loadCert(dir string, prefix string) (tls.Certificate, error) {
|
||||
return tls.LoadX509KeyPair(filepath.Join(dir, prefix+"cert.pem"), filepath.Join(dir, prefix+"key.pem"))
|
||||
}
|
||||
|
||||
func certID(bs []byte) string {
|
||||
hf := sha256.New()
|
||||
hf.Write(bs)
|
||||
id := hf.Sum(nil)
|
||||
return strings.Trim(base32.StdEncoding.EncodeToString(id), "=")
|
||||
cf := filepath.Join(dir, prefix+"cert.pem")
|
||||
kf := filepath.Join(dir, prefix+"key.pem")
|
||||
return tls.LoadX509KeyPair(cf, kf)
|
||||
}
|
||||
|
||||
func certSeed(bs []byte) int64 {
|
||||
|
||||
@@ -23,7 +23,7 @@ var stopUsageReportingCh = make(chan struct{})
|
||||
|
||||
func reportData(m *model.Model) map[string]interface{} {
|
||||
res := make(map[string]interface{})
|
||||
res["uniqueID"] = strings.ToLower(certID([]byte(myID)))[:6]
|
||||
res["uniqueID"] = strings.ToLower(myID.String()[:6])
|
||||
res["version"] = Version
|
||||
res["longVersion"] = LongVersion
|
||||
res["platform"] = runtime.GOOS + "-" + runtime.GOARCH
|
||||
|
||||
+15
-30
@@ -14,10 +14,10 @@ import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.crypto/bcrypt"
|
||||
"github.com/calmh/syncthing/logger"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
@@ -69,7 +69,7 @@ type RepositoryConfiguration struct {
|
||||
Versioning VersioningConfiguration `xml:"versioning"`
|
||||
SyncOrderPatterns []SyncOrderPattern `xml:"syncorder>pattern"`
|
||||
|
||||
nodeIDs []string
|
||||
nodeIDs []protocol.NodeID
|
||||
}
|
||||
|
||||
type VersioningConfiguration struct {
|
||||
@@ -113,7 +113,7 @@ func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartEl
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RepositoryConfiguration) NodeIDs() []string {
|
||||
func (r *RepositoryConfiguration) NodeIDs() []protocol.NodeID {
|
||||
if r.nodeIDs == nil {
|
||||
for _, n := range r.Nodes {
|
||||
r.nodeIDs = append(r.nodeIDs, n.NodeID)
|
||||
@@ -138,9 +138,9 @@ func (r RepositoryConfiguration) FileRanker() func(scanner.File) int {
|
||||
}
|
||||
|
||||
type NodeConfiguration struct {
|
||||
NodeID string `xml:"id,attr"`
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Addresses []string `xml:"address,omitempty"`
|
||||
NodeID protocol.NodeID `xml:"id,attr"`
|
||||
Name string `xml:"name,attr,omitempty"`
|
||||
Addresses []string `xml:"address,omitempty"`
|
||||
}
|
||||
|
||||
type OptionsConfiguration struct {
|
||||
@@ -174,8 +174,8 @@ type GUIConfiguration struct {
|
||||
APIKey string `xml:"apikey,omitempty"`
|
||||
}
|
||||
|
||||
func (cfg *Configuration) NodeMap() map[string]NodeConfiguration {
|
||||
m := make(map[string]NodeConfiguration, len(cfg.Nodes))
|
||||
func (cfg *Configuration) NodeMap() map[protocol.NodeID]NodeConfiguration {
|
||||
m := make(map[protocol.NodeID]NodeConfiguration, len(cfg.Nodes))
|
||||
for _, n := range cfg.Nodes {
|
||||
m[n.NodeID] = n
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func uniqueStrings(ss []string) []string {
|
||||
return us
|
||||
}
|
||||
|
||||
func Load(rd io.Reader, myID string) (Configuration, error) {
|
||||
func Load(rd io.Reader, myID protocol.NodeID) (Configuration, error) {
|
||||
var cfg Configuration
|
||||
|
||||
setDefaults(&cfg)
|
||||
@@ -297,15 +297,6 @@ func Load(rd io.Reader, myID string) (Configuration, error) {
|
||||
cfg.Repositories = []RepositoryConfiguration{}
|
||||
}
|
||||
|
||||
// Sanitize node IDs
|
||||
for i := range cfg.Nodes {
|
||||
node := &cfg.Nodes[i]
|
||||
// Strip spaces and dashes
|
||||
node.NodeID = strings.Replace(node.NodeID, "-", "", -1)
|
||||
node.NodeID = strings.Replace(node.NodeID, " ", "", -1)
|
||||
node.NodeID = strings.ToUpper(node.NodeID)
|
||||
}
|
||||
|
||||
// Check for missing, bad or duplicate repository ID:s
|
||||
var seenRepos = map[string]*RepositoryConfiguration{}
|
||||
var uniqueCounter int
|
||||
@@ -321,13 +312,6 @@ func Load(rd io.Reader, myID string) (Configuration, error) {
|
||||
repo.ID = "default"
|
||||
}
|
||||
|
||||
for i := range repo.Nodes {
|
||||
node := &repo.Nodes[i]
|
||||
// Strip spaces and dashes
|
||||
node.NodeID = strings.Replace(node.NodeID, "-", "", -1)
|
||||
node.NodeID = strings.Replace(node.NodeID, " ", "", -1)
|
||||
}
|
||||
|
||||
if seen, ok := seenRepos[repo.ID]; ok {
|
||||
l.Warnf("Multiple repositories with ID %q; disabling", repo.ID)
|
||||
|
||||
@@ -390,8 +374,9 @@ func convertV1V2(cfg *Configuration) {
|
||||
for i, repo := range cfg.Repositories {
|
||||
cfg.Repositories[i].ReadOnly = cfg.Options.Deprecated_ReadOnly
|
||||
for j, node := range repo.Nodes {
|
||||
if _, ok := nodes[node.NodeID]; !ok {
|
||||
nodes[node.NodeID] = node
|
||||
id := node.NodeID.String()
|
||||
if _, ok := nodes[id]; !ok {
|
||||
nodes[id] = node
|
||||
}
|
||||
cfg.Repositories[i].Nodes[j] = NodeConfiguration{NodeID: node.NodeID}
|
||||
}
|
||||
@@ -416,7 +401,7 @@ func convertV1V2(cfg *Configuration) {
|
||||
type NodeConfigurationList []NodeConfiguration
|
||||
|
||||
func (l NodeConfigurationList) Less(a, b int) bool {
|
||||
return l[a].NodeID < l[b].NodeID
|
||||
return l[a].NodeID.Compare(l[b].NodeID) == -1
|
||||
}
|
||||
func (l NodeConfigurationList) Swap(a, b int) {
|
||||
l[a], l[b] = l[b], l[a]
|
||||
@@ -425,10 +410,10 @@ func (l NodeConfigurationList) Len() int {
|
||||
return len(l)
|
||||
}
|
||||
|
||||
func ensureNodePresent(nodes []NodeConfiguration, myID string) []NodeConfiguration {
|
||||
func ensureNodePresent(nodes []NodeConfiguration, myID protocol.NodeID) []NodeConfiguration {
|
||||
var myIDExists bool
|
||||
for _, node := range nodes {
|
||||
if node.NodeID == myID {
|
||||
if node.NodeID.Equals(myID) {
|
||||
myIDExists = true
|
||||
break
|
||||
}
|
||||
|
||||
+41
-95
@@ -12,9 +12,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/files"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
var node1, node2, node3, node4 protocol.NodeID
|
||||
|
||||
func init() {
|
||||
node1, _ = protocol.NodeIDFromString("AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ")
|
||||
node2, _ = protocol.NodeIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
|
||||
node3, _ = protocol.NodeIDFromString("LGFPDIT-7SKNNJL-VJZA4FC-7QNCRKA-CE753K7-2BW5QDK-2FOZ7FR-FEP57QJ")
|
||||
node4, _ = protocol.NodeIDFromString("P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2")
|
||||
}
|
||||
|
||||
func TestDefaultValues(t *testing.T) {
|
||||
expected := OptionsConfiguration{
|
||||
ListenAddress: []string{"0.0.0.0:22000"},
|
||||
@@ -31,7 +41,7 @@ func TestDefaultValues(t *testing.T) {
|
||||
UPnPEnabled: true,
|
||||
}
|
||||
|
||||
cfg, err := Load(bytes.NewReader(nil), "nodeID")
|
||||
cfg, err := Load(bytes.NewReader(nil), node1)
|
||||
if err != io.EOF {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -45,10 +55,10 @@ func TestNodeConfig(t *testing.T) {
|
||||
v1data := []byte(`
|
||||
<configuration version="1">
|
||||
<repository id="test" directory="~/Sync">
|
||||
<node id="NODE1" name="node one">
|
||||
<node id="AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ" name="node one">
|
||||
<address>a</address>
|
||||
</node>
|
||||
<node id="NODE2" name="node two">
|
||||
<node id="P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ" name="node two">
|
||||
<address>b</address>
|
||||
</node>
|
||||
</repository>
|
||||
@@ -61,20 +71,20 @@ func TestNodeConfig(t *testing.T) {
|
||||
v2data := []byte(`
|
||||
<configuration version="2">
|
||||
<repository id="test" directory="~/Sync" ro="true">
|
||||
<node id="NODE1"/>
|
||||
<node id="NODE2"/>
|
||||
<node id="AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ"/>
|
||||
<node id="P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ"/>
|
||||
</repository>
|
||||
<node id="NODE1" name="node one">
|
||||
<node id="AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ" name="node one">
|
||||
<address>a</address>
|
||||
</node>
|
||||
<node id="NODE2" name="node two">
|
||||
<node id="P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ" name="node two">
|
||||
<address>b</address>
|
||||
</node>
|
||||
</configuration>
|
||||
`)
|
||||
|
||||
for i, data := range [][]byte{v1data, v2data} {
|
||||
cfg, err := Load(bytes.NewReader(data), "NODE1")
|
||||
cfg, err := Load(bytes.NewReader(data), node1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -83,23 +93,23 @@ func TestNodeConfig(t *testing.T) {
|
||||
{
|
||||
ID: "test",
|
||||
Directory: "~/Sync",
|
||||
Nodes: []NodeConfiguration{{NodeID: "NODE1"}, {NodeID: "NODE2"}},
|
||||
Nodes: []NodeConfiguration{{NodeID: node1}, {NodeID: node4}},
|
||||
ReadOnly: true,
|
||||
},
|
||||
}
|
||||
expectedNodes := []NodeConfiguration{
|
||||
{
|
||||
NodeID: "NODE1",
|
||||
NodeID: node1,
|
||||
Name: "node one",
|
||||
Addresses: []string{"a"},
|
||||
},
|
||||
{
|
||||
NodeID: "NODE2",
|
||||
NodeID: node4,
|
||||
Name: "node two",
|
||||
Addresses: []string{"b"},
|
||||
},
|
||||
}
|
||||
expectedNodeIDs := []string{"NODE1", "NODE2"}
|
||||
expectedNodeIDs := []protocol.NodeID{node1, node4}
|
||||
|
||||
if cfg.Version != 2 {
|
||||
t.Errorf("%d: Incorrect version %d != 2", i, cfg.Version)
|
||||
@@ -118,18 +128,13 @@ func TestNodeConfig(t *testing.T) {
|
||||
|
||||
func TestNoListenAddress(t *testing.T) {
|
||||
data := []byte(`<configuration version="1">
|
||||
<repository directory="~/Sync">
|
||||
<node id="..." name="...">
|
||||
<address>dynamic</address>
|
||||
</node>
|
||||
</repository>
|
||||
<options>
|
||||
<listenAddress></listenAddress>
|
||||
</options>
|
||||
</configuration>
|
||||
`)
|
||||
|
||||
cfg, err := Load(bytes.NewReader(data), "nodeID")
|
||||
cfg, err := Load(bytes.NewReader(data), node1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -142,11 +147,6 @@ func TestNoListenAddress(t *testing.T) {
|
||||
|
||||
func TestOverriddenValues(t *testing.T) {
|
||||
data := []byte(`<configuration version="2">
|
||||
<repository directory="~/Sync">
|
||||
<node id="..." name="...">
|
||||
<address>dynamic</address>
|
||||
</node>
|
||||
</repository>
|
||||
<options>
|
||||
<listenAddress>:23000</listenAddress>
|
||||
<allowDelete>false</allowDelete>
|
||||
@@ -180,7 +180,7 @@ func TestOverriddenValues(t *testing.T) {
|
||||
UPnPEnabled: false,
|
||||
}
|
||||
|
||||
cfg, err := Load(bytes.NewReader(data), "nodeID")
|
||||
cfg, err := Load(bytes.NewReader(data), node1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -193,13 +193,13 @@ func TestOverriddenValues(t *testing.T) {
|
||||
func TestNodeAddresses(t *testing.T) {
|
||||
data := []byte(`
|
||||
<configuration version="2">
|
||||
<node id="n1">
|
||||
<address>dynamic</address>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<node id="AIR6LPZ7K4PTTUXQSMUUCPQ5YWOEDFIIQJUG7772YQXXR5YD6AWQ">
|
||||
<address></address>
|
||||
</node>
|
||||
<node id="n3">
|
||||
<node id="GYRZZQBIRNPV4T7TC52WEQYJ3TFDQW6MWDFLMU4SSSU6EMFBK2VA">
|
||||
</node>
|
||||
<node id="LGFPDIT7SKNNJVJZA4FC7QNCRKCE753K72BW5QD2FOZ7FRFEP57Q">
|
||||
<address>dynamic</address>
|
||||
</node>
|
||||
</configuration>
|
||||
`)
|
||||
@@ -207,25 +207,25 @@ func TestNodeAddresses(t *testing.T) {
|
||||
name, _ := os.Hostname()
|
||||
expected := []NodeConfiguration{
|
||||
{
|
||||
NodeID: "N1",
|
||||
NodeID: node1,
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
{
|
||||
NodeID: "N2",
|
||||
NodeID: node2,
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
{
|
||||
NodeID: "N3",
|
||||
NodeID: node3,
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
{
|
||||
NodeID: "N4",
|
||||
NodeID: node4,
|
||||
Name: name, // Set when auto created
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := Load(bytes.NewReader(data), "N4")
|
||||
cfg, err := Load(bytes.NewReader(data), node4)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -235,86 +235,32 @@ func TestNodeAddresses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripNodeIs(t *testing.T) {
|
||||
data := []byte(`
|
||||
<configuration version="2">
|
||||
<node id="AAAA-BBBB-CCCC">
|
||||
<address>dynamic</address>
|
||||
</node>
|
||||
<node id="AAAA BBBB DDDD">
|
||||
<address></address>
|
||||
</node>
|
||||
<node id="AAAABBBBEEEE">
|
||||
<address></address>
|
||||
</node>
|
||||
<repository directory="~/Sync">
|
||||
<node id="AAA ABBB-BCC CC" name=""></node>
|
||||
<node id="AA-AAB BBBD-DDD" name=""></node>
|
||||
<node id="AAA AB-BBB EEE-E" name=""></node>
|
||||
</repository>
|
||||
</configuration>
|
||||
`)
|
||||
|
||||
expected := []NodeConfiguration{
|
||||
{
|
||||
NodeID: "AAAABBBBCCCC",
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
{
|
||||
NodeID: "AAAABBBBDDDD",
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
{
|
||||
NodeID: "AAAABBBBEEEE",
|
||||
Addresses: []string{"dynamic"},
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := Load(bytes.NewReader(data), "n4")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for i := range expected {
|
||||
if !reflect.DeepEqual(cfg.Nodes[i], expected[i]) {
|
||||
t.Errorf("Nodes[%d] differ;\n E: %#v\n A: %#v", i, expected[i], cfg.Nodes[i])
|
||||
}
|
||||
if cfg.Repositories[0].Nodes[i].NodeID != expected[i].NodeID {
|
||||
t.Errorf("Repo nodes[%d] differ;\n E: %#v\n A: %#v", i, expected[i].NodeID, cfg.Repositories[0].Nodes[i].NodeID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncOrders(t *testing.T) {
|
||||
data := []byte(`
|
||||
<configuration version="2">
|
||||
<node id="AAAA-BBBB-CCCC">
|
||||
<address>dynamic</address>
|
||||
</node>
|
||||
<repository directory="~/Sync">
|
||||
<syncorder>
|
||||
<pattern pattern="\.jpg$" priority="1" />
|
||||
</syncorder>
|
||||
<node id="AAAA-BBBB-CCCC" name=""></node>
|
||||
</repository>
|
||||
</configuration>
|
||||
`)
|
||||
|
||||
expected := []SyncOrderPattern{
|
||||
{
|
||||
Pattern: "\\.jpg$",
|
||||
Priority: 1,
|
||||
Pattern: "\\.jpg$",
|
||||
Priority: 1,
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := Load(bytes.NewReader(data), "n4")
|
||||
cfg, err := Load(bytes.NewReader(data), node1)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for i := range expected {
|
||||
if !reflect.DeepEqual(cfg.Repositories[0].SyncOrderPatterns[i], expected[i]) {
|
||||
t.Errorf("Nodes[%d] differ;\n E: %#v\n A: %#v", i, expected[i], cfg.Repositories[0].SyncOrderPatterns[i])
|
||||
t.Errorf("Patterns[%d] differ;\n E: %#v\n A: %#v", i, expected[i], cfg.Repositories[0].SyncOrderPatterns[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,9 +307,9 @@ func TestFileSorter(t *testing.T) {
|
||||
if !reflect.DeepEqual(f, expected) {
|
||||
t.Errorf(
|
||||
"\n\nexpected:\n" +
|
||||
formatFiles(expected) + "\n" +
|
||||
"got:\n" +
|
||||
formatFiles(f) + "\n\n",
|
||||
formatFiles(expected) + "\n" +
|
||||
"got:\n" +
|
||||
formatFiles(f) + "\n\n",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/discover"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
"github.com/golang/groupcache/lru"
|
||||
"github.com/juju/ratelimit"
|
||||
)
|
||||
@@ -32,7 +33,7 @@ type address struct {
|
||||
}
|
||||
|
||||
var (
|
||||
nodes = make(map[string]node)
|
||||
nodes = make(map[protocol.NodeID]node)
|
||||
lock sync.Mutex
|
||||
queries = 0
|
||||
announces = 0
|
||||
@@ -182,8 +183,16 @@ func handleAnnounceV2(addr *net.UDPAddr, buf []byte) {
|
||||
updated: time.Now(),
|
||||
}
|
||||
|
||||
var id protocol.NodeID
|
||||
if len(pkt.This.ID) == 32 {
|
||||
// Raw node ID
|
||||
copy(id[:], pkt.This.ID)
|
||||
} else {
|
||||
id.UnmarshalText(pkt.This.ID)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
nodes[pkt.This.ID] = node
|
||||
nodes[id] = node
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
@@ -199,8 +208,16 @@ func handleQueryV2(conn *net.UDPConn, addr *net.UDPAddr, buf []byte) {
|
||||
log.Printf("<- %v %#v", addr, pkt)
|
||||
}
|
||||
|
||||
var id protocol.NodeID
|
||||
if len(pkt.NodeID) == 32 {
|
||||
// Raw node ID
|
||||
copy(id[:], pkt.NodeID)
|
||||
} else {
|
||||
id.UnmarshalText(pkt.NodeID)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
node, ok := nodes[pkt.NodeID]
|
||||
node, ok := nodes[id]
|
||||
queries++
|
||||
lock.Unlock()
|
||||
|
||||
|
||||
+24
-18
@@ -5,6 +5,7 @@
|
||||
package discover
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,15 +15,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/calmh/syncthing/beacon"
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
)
|
||||
|
||||
type Discoverer struct {
|
||||
myID string
|
||||
myID protocol.NodeID
|
||||
listenAddrs []string
|
||||
localBcastIntv time.Duration
|
||||
globalBcastIntv time.Duration
|
||||
beacon *beacon.Beacon
|
||||
registry map[string][]string
|
||||
registry map[protocol.NodeID][]string
|
||||
registryLock sync.RWMutex
|
||||
extServer string
|
||||
extPort uint16
|
||||
@@ -41,7 +43,7 @@ var (
|
||||
// When we hit this many errors in succession, we stop.
|
||||
const maxErrors = 30
|
||||
|
||||
func NewDiscoverer(id string, addresses []string, localPort int) (*Discoverer, error) {
|
||||
func NewDiscoverer(id protocol.NodeID, addresses []string, localPort int) (*Discoverer, error) {
|
||||
b, err := beacon.New(localPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -52,7 +54,7 @@ func NewDiscoverer(id string, addresses []string, localPort int) (*Discoverer, e
|
||||
localBcastIntv: 30 * time.Second,
|
||||
globalBcastIntv: 1800 * time.Second,
|
||||
beacon: b,
|
||||
registry: make(map[string][]string),
|
||||
registry: make(map[protocol.NodeID][]string),
|
||||
}
|
||||
|
||||
go disc.recvAnnouncements()
|
||||
@@ -78,7 +80,7 @@ func (d *Discoverer) ExtAnnounceOK() bool {
|
||||
return d.extAnnounceOK
|
||||
}
|
||||
|
||||
func (d *Discoverer) Lookup(node string) []string {
|
||||
func (d *Discoverer) Lookup(node protocol.NodeID) []string {
|
||||
d.registryLock.Lock()
|
||||
addr, ok := d.registry[node]
|
||||
d.registryLock.Unlock()
|
||||
@@ -94,15 +96,17 @@ func (d *Discoverer) Lookup(node string) []string {
|
||||
|
||||
func (d *Discoverer) Hint(node string, addrs []string) {
|
||||
resAddrs := resolveAddrs(addrs)
|
||||
var id protocol.NodeID
|
||||
id.UnmarshalText([]byte(node))
|
||||
d.registerNode(nil, Node{
|
||||
ID: node,
|
||||
Addresses: resAddrs,
|
||||
ID: id[:],
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Discoverer) All() map[string][]string {
|
||||
func (d *Discoverer) All() map[protocol.NodeID][]string {
|
||||
d.registryLock.RLock()
|
||||
nodes := make(map[string][]string, len(d.registry))
|
||||
nodes := make(map[protocol.NodeID][]string, len(d.registry))
|
||||
for node, addrs := range d.registry {
|
||||
addrsCopy := make([]string, len(addrs))
|
||||
copy(addrsCopy, addrs)
|
||||
@@ -132,7 +136,7 @@ func (d *Discoverer) announcementPkt() []byte {
|
||||
}
|
||||
var pkt = AnnounceV2{
|
||||
Magic: AnnouncementMagicV2,
|
||||
This: Node{d.myID, addrs},
|
||||
This: Node{d.myID[:], addrs},
|
||||
}
|
||||
return pkt.MarshalXDR()
|
||||
}
|
||||
@@ -142,7 +146,7 @@ func (d *Discoverer) sendLocalAnnouncements() {
|
||||
|
||||
var pkt = AnnounceV2{
|
||||
Magic: AnnouncementMagicV2,
|
||||
This: Node{d.myID, addrs},
|
||||
This: Node{d.myID[:], addrs},
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -153,7 +157,7 @@ func (d *Discoverer) sendLocalAnnouncements() {
|
||||
break
|
||||
}
|
||||
|
||||
anode := Node{node, resolveAddrs(addrs)}
|
||||
anode := Node{node[:], resolveAddrs(addrs)}
|
||||
pkt.Extra = append(pkt.Extra, anode)
|
||||
}
|
||||
d.registryLock.RUnlock()
|
||||
@@ -184,7 +188,7 @@ func (d *Discoverer) sendExternalAnnouncements() {
|
||||
if d.extPort != 0 {
|
||||
var pkt = AnnounceV2{
|
||||
Magic: AnnouncementMagicV2,
|
||||
This: Node{d.myID, []Address{{Port: d.extPort}}},
|
||||
This: Node{d.myID[:], []Address{{Port: d.extPort}}},
|
||||
}
|
||||
buf = pkt.MarshalXDR()
|
||||
} else {
|
||||
@@ -246,11 +250,11 @@ func (d *Discoverer) recvAnnouncements() {
|
||||
}
|
||||
|
||||
var newNode bool
|
||||
if pkt.This.ID != d.myID {
|
||||
if bytes.Compare(pkt.This.ID, d.myID[:]) != 0 {
|
||||
n := d.registerNode(addr, pkt.This)
|
||||
newNode = newNode || n
|
||||
for _, node := range pkt.Extra {
|
||||
if node.ID != d.myID {
|
||||
if bytes.Compare(node.ID, d.myID[:]) != 0 {
|
||||
n := d.registerNode(nil, node)
|
||||
newNode = newNode || n
|
||||
}
|
||||
@@ -287,14 +291,16 @@ func (d *Discoverer) registerNode(addr net.Addr, node Node) bool {
|
||||
if debug {
|
||||
l.Debugf("discover: register: %s -> %#v", node.ID, addrs)
|
||||
}
|
||||
var id protocol.NodeID
|
||||
copy(id[:], node.ID)
|
||||
d.registryLock.Lock()
|
||||
_, seen := d.registry[node.ID]
|
||||
d.registry[node.ID] = addrs
|
||||
_, seen := d.registry[id]
|
||||
d.registry[id] = addrs
|
||||
d.registryLock.Unlock()
|
||||
return !seen
|
||||
}
|
||||
|
||||
func (d *Discoverer) externalLookup(node string) []string {
|
||||
func (d *Discoverer) externalLookup(node protocol.NodeID) []string {
|
||||
extIP, err := net.ResolveUDPAddr("udp", d.extServer)
|
||||
if err != nil {
|
||||
if debug {
|
||||
@@ -320,7 +326,7 @@ func (d *Discoverer) externalLookup(node string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
buf := QueryV2{QueryMagicV2, node}.MarshalXDR()
|
||||
buf := QueryV2{QueryMagicV2, node[:]}.MarshalXDR()
|
||||
_, err = conn.Write(buf)
|
||||
if err != nil {
|
||||
if debug {
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ const (
|
||||
|
||||
type QueryV2 struct {
|
||||
Magic uint32
|
||||
NodeID string // max:64
|
||||
NodeID []byte // max:32
|
||||
}
|
||||
|
||||
type AnnounceV2 struct {
|
||||
@@ -21,7 +21,7 @@ type AnnounceV2 struct {
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
ID string // max:64
|
||||
ID []byte // max:32
|
||||
Addresses []Address // max:16
|
||||
}
|
||||
|
||||
|
||||
+6
-10
@@ -1,7 +1,3 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
@@ -25,10 +21,10 @@ func (o QueryV2) MarshalXDR() []byte {
|
||||
|
||||
func (o QueryV2) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
xw.WriteUint32(o.Magic)
|
||||
if len(o.NodeID) > 64 {
|
||||
if len(o.NodeID) > 32 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.NodeID)
|
||||
xw.WriteBytes(o.NodeID)
|
||||
return xw.Tot(), xw.Error()
|
||||
}
|
||||
|
||||
@@ -45,7 +41,7 @@ func (o *QueryV2) UnmarshalXDR(bs []byte) error {
|
||||
|
||||
func (o *QueryV2) decodeXDR(xr *xdr.Reader) error {
|
||||
o.Magic = xr.ReadUint32()
|
||||
o.NodeID = xr.ReadStringMax(64)
|
||||
o.NodeID = xr.ReadBytesMax(32)
|
||||
return xr.Error()
|
||||
}
|
||||
|
||||
@@ -112,10 +108,10 @@ func (o Node) MarshalXDR() []byte {
|
||||
}
|
||||
|
||||
func (o Node) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
if len(o.ID) > 64 {
|
||||
if len(o.ID) > 32 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.ID)
|
||||
xw.WriteBytes(o.ID)
|
||||
if len(o.Addresses) > 16 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
@@ -138,7 +134,7 @@ func (o *Node) UnmarshalXDR(bs []byte) error {
|
||||
}
|
||||
|
||||
func (o *Node) decodeXDR(xr *xdr.Reader) error {
|
||||
o.ID = xr.ReadStringMax(64)
|
||||
o.ID = xr.ReadBytesMax(32)
|
||||
_AddressesSize := int(xr.ReadUint32())
|
||||
if _AddressesSize > 16 {
|
||||
return xdr.ErrElementSizeExceeded
|
||||
|
||||
+4
-15
@@ -410,7 +410,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||
|
||||
$('#editNode').modal('hide');
|
||||
nodeCfg = $scope.currentNode;
|
||||
nodeCfg.NodeID = nodeCfg.NodeID.replace(/ /g, '').replace(/-/g, '').toUpperCase().trim();
|
||||
nodeCfg.NodeID = nodeCfg.NodeID.replace(/ /g, '').replace(/-/g, '').toLowerCase().trim();
|
||||
nodeCfg.Addresses = nodeCfg.AddressesStr.split(',').map(function (x) { return x.trim(); });
|
||||
|
||||
done = false;
|
||||
@@ -711,7 +711,7 @@ function randomString(len, bits)
|
||||
newStr = Math.random().toString(bits).slice(2);
|
||||
outStr += newStr.slice(0, Math.min(newStr.length, (len - outStr.length)));
|
||||
}
|
||||
return outStr.toUpperCase();
|
||||
return outStr.toLowerCase();
|
||||
}
|
||||
|
||||
syncthing.filter('natural', function () {
|
||||
@@ -777,17 +777,6 @@ syncthing.filter('alwaysNumber', function () {
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.filter('chunkID', function () {
|
||||
return function (input) {
|
||||
if (input === undefined)
|
||||
return "";
|
||||
var parts = input.match(/.{1,6}/g);
|
||||
if (!parts)
|
||||
return "";
|
||||
return parts.join('-');
|
||||
};
|
||||
});
|
||||
|
||||
syncthing.filter('shortPath', function () {
|
||||
return function (input) {
|
||||
if (input === undefined)
|
||||
@@ -860,8 +849,8 @@ syncthing.directive('validNodeid', function() {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('validNodeid', true);
|
||||
} else {
|
||||
var cleaned = viewValue.replace(/ /g, '').replace(/-/g, '').toUpperCase().trim();
|
||||
if (cleaned.match(/^[A-Z2-7]{52}$/)) {
|
||||
var cleaned = viewValue.replace(/ /g, '').replace(/-/g, '').toLowerCase().trim();
|
||||
if (cleaned.match(/^[a-z2-7]{52}$/)) {
|
||||
ctrl.$setValidity('validNodeid', true);
|
||||
} else {
|
||||
ctrl.$setValidity('validNodeid', false);
|
||||
|
||||
+3
-3
@@ -418,8 +418,8 @@ found in the LICENSE file.
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="well well-sm text-monospace text-center">{{myID | chunkID}}</div>
|
||||
<img ng-if="myID" class="center-block img-thumbnail" src="qr/{{myID | chunkID}}"/>
|
||||
<div class="well well-sm text-monospace text-center">{{myID}}</div>
|
||||
<img ng-if="myID" class="center-block img-thumbnail" src="qr/{{myID}}"/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> Close</button>
|
||||
@@ -442,7 +442,7 @@ found in the LICENSE file.
|
||||
<div class="form-group" ng-class="{'has-error': nodeEditor.nodeID.$invalid && nodeEditor.nodeID.$dirty}">
|
||||
<label for="nodeID">Node ID</label>
|
||||
<input ng-if="!editingExisting" name="nodeID" id="nodeID" class="form-control text-monospace" type="text" ng-model="currentNode.NodeID" required valid-nodeid></input>
|
||||
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentNode.NodeID | chunkID}}</div>
|
||||
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentNode.NodeID}}</div>
|
||||
<p class="help-block">
|
||||
<span ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Edit > Show ID" dialog on the other node. Spaces and dashes are optional (ignored).
|
||||
<span ng-show="!editingExisting">When adding a new node, keep in mind that <em>this node</em> must be added on the other side too.</span>
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
<configuration version="2">
|
||||
<repository id="default" directory="s2" ro="false" ignorePerms="false">
|
||||
<node id="373HSRPQLPNLIJYKZVQFP4PKZ6R2ZE6K3YD442UJHBGBQGWWXAHA"></node>
|
||||
<node id="I6KAH7666SLLL5PFXSOAUFJCDZYAOMLEKCP2GB3BV5RQST3PSROA"></node>
|
||||
<node id="JMFJCXBGZDE4BOCJE3VF65GYZNAIVJRET3J6HMRAUQIGJOFKNHMQ"></node>
|
||||
<node id="373HSRPQLPNLIJYKZVQFP4PKZ6R2ZE6K3YD442UJHBGBQGWWXAHA"></node>
|
||||
<versioning></versioning>
|
||||
<syncorder></syncorder>
|
||||
</repository>
|
||||
<repository id="s12" directory="s12-2" ro="false" ignorePerms="false">
|
||||
<node id="I6KAH7666SLLL5PFXSOAUFJCDZYAOMLEKCP2GB3BV5RQST3PSROA"></node>
|
||||
<node id="JMFJCXBGZDE4BOCJE3VF65GYZNAIVJRET3J6HMRAUQIGJOFKNHMQ"></node>
|
||||
<versioning></versioning>
|
||||
<syncorder></syncorder>
|
||||
</repository>
|
||||
<repository id="s23" directory="s23-2" ro="false" ignorePerms="false">
|
||||
<node id="373HSRPQLPNLIJYKZVQFP4PKZ6R2ZE6K3YD442UJHBGBQGWWXAHA"></node>
|
||||
<node id="JMFJCXBGZDE4BOCJE3VF65GYZNAIVJRET3J6HMRAUQIGJOFKNHMQ"></node>
|
||||
<node id="373HSRPQLPNLIJYKZVQFP4PKZ6R2ZE6K3YD442UJHBGBQGWWXAHA"></node>
|
||||
<versioning></versioning>
|
||||
<syncorder></syncorder>
|
||||
</repository>
|
||||
<node id="I6KAH7666SLLL5PFXSOAUFJCDZYAOMLEKCP2GB3BV5RQST3PSROA" name="s1">
|
||||
<address>127.0.0.1:22001</address>
|
||||
@@ -41,5 +44,6 @@
|
||||
<maxChangeKbps>10000</maxChangeKbps>
|
||||
<startBrowser>false</startBrowser>
|
||||
<upnpEnabled>true</upnpEnabled>
|
||||
<urAccepted>-1</urAccepted>
|
||||
</options>
|
||||
</configuration>
|
||||
+3
-3
@@ -6,9 +6,9 @@
|
||||
|
||||
iterations=${1:-5}
|
||||
|
||||
id1=I6KAH7666SLLL5PFXSOAUFJCDZYAOMLEKCP2GB3BV5RQST3PSROA
|
||||
id2=JMFJCXBGZDE4BOCJE3VF65GYZNAIVJRET3J6HMRAUQIGJOFKNHMQ
|
||||
id3=373HSRPQLPNLIJYKZVQFP4PKZ6R2ZE6K3YD442UJHBGBQGWWXAHA
|
||||
id1=I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU
|
||||
id2=JMFJCXB-GZDE4BN-OCJE3VF-65GYZNU-AIVJRET-3J6HMRQ-AUQIGJO-FKNHMQU
|
||||
id3=373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU
|
||||
|
||||
go build genfiles.go
|
||||
go build md5r.go
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package luhn
|
||||
|
||||
import "strings"
|
||||
|
||||
type Alphabet string
|
||||
|
||||
var (
|
||||
Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
|
||||
Base32Trimmed Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
||||
)
|
||||
|
||||
func (a Alphabet) Generate(s string) rune {
|
||||
factor := 1
|
||||
sum := 0
|
||||
n := len(a)
|
||||
|
||||
for i := range s {
|
||||
codepoint := strings.IndexByte(string(a), s[i])
|
||||
addend := factor * codepoint
|
||||
if factor == 2 {
|
||||
factor = 1
|
||||
} else {
|
||||
factor = 2
|
||||
}
|
||||
addend = (addend / n) + (addend % n)
|
||||
sum += addend
|
||||
}
|
||||
remainder := sum % n
|
||||
checkCodepoint := (n - remainder) % n
|
||||
return rune(a[checkCodepoint])
|
||||
}
|
||||
|
||||
func (a Alphabet) Validate(s string) bool {
|
||||
t := s[:len(s)-1]
|
||||
c := a.Generate(t)
|
||||
return rune(s[len(s)-1]) == c
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package luhn_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/luhn"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
a := luhn.Alphabet("abcdef")
|
||||
c := a.Generate("abcdef")
|
||||
if c != 'e' {
|
||||
t.Errorf("Incorrect check digit %c != e", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
a := luhn.Alphabet("abcdef")
|
||||
if !a.Validate("abcdefe") {
|
||||
t.Errorf("Incorrect validation response for abcdefe")
|
||||
}
|
||||
if a.Validate("abcdefd") {
|
||||
t.Errorf("Incorrect validation response for abcdefd")
|
||||
}
|
||||
}
|
||||
+23
-23
@@ -49,8 +49,8 @@ type Model struct {
|
||||
|
||||
repoCfgs map[string]config.RepositoryConfiguration // repo -> cfg
|
||||
repoFiles map[string]*files.Set // repo -> files
|
||||
repoNodes map[string][]string // repo -> nodeIDs
|
||||
nodeRepos map[string][]string // nodeID -> repos
|
||||
repoNodes map[string][]protocol.NodeID // repo -> nodeIDs
|
||||
nodeRepos map[protocol.NodeID][]string // nodeID -> repos
|
||||
suppressor map[string]*suppressor // repo -> suppressor
|
||||
rmut sync.RWMutex // protects the above
|
||||
|
||||
@@ -59,9 +59,9 @@ type Model struct {
|
||||
|
||||
cm *cid.Map
|
||||
|
||||
protoConn map[string]protocol.Connection
|
||||
rawConn map[string]io.Closer
|
||||
nodeVer map[string]string
|
||||
protoConn map[protocol.NodeID]protocol.Connection
|
||||
rawConn map[protocol.NodeID]io.Closer
|
||||
nodeVer map[protocol.NodeID]string
|
||||
pmut sync.RWMutex // protects protoConn and rawConn
|
||||
|
||||
sup suppressor
|
||||
@@ -86,14 +86,14 @@ func NewModel(indexDir string, cfg *config.Configuration, clientName, clientVers
|
||||
clientVersion: clientVersion,
|
||||
repoCfgs: make(map[string]config.RepositoryConfiguration),
|
||||
repoFiles: make(map[string]*files.Set),
|
||||
repoNodes: make(map[string][]string),
|
||||
nodeRepos: make(map[string][]string),
|
||||
repoNodes: make(map[string][]protocol.NodeID),
|
||||
nodeRepos: make(map[protocol.NodeID][]string),
|
||||
repoState: make(map[string]repoState),
|
||||
suppressor: make(map[string]*suppressor),
|
||||
cm: cid.NewMap(),
|
||||
protoConn: make(map[string]protocol.Connection),
|
||||
rawConn: make(map[string]io.Closer),
|
||||
nodeVer: make(map[string]string),
|
||||
protoConn: make(map[protocol.NodeID]protocol.Connection),
|
||||
rawConn: make(map[protocol.NodeID]io.Closer),
|
||||
nodeVer: make(map[protocol.NodeID]string),
|
||||
sup: suppressor{threshold: int64(cfg.Options.MaxChangeKbps)},
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
|
||||
ci.Completion = int(100 * have / tot)
|
||||
}
|
||||
|
||||
res[node] = ci
|
||||
res[node.String()] = ci
|
||||
}
|
||||
|
||||
m.rmut.RUnlock()
|
||||
@@ -261,7 +261,7 @@ func (m *Model) NeedFilesRepo(repo string) []scanner.File {
|
||||
|
||||
// Index is called when a new node is connected and we receive their full index.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Index(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
func (m *Model) Index(nodeID protocol.NodeID, repo string, fs []protocol.FileInfo) {
|
||||
if debug {
|
||||
l.Debugf("IDX(in): %s %q: %d files", nodeID, repo, len(fs))
|
||||
}
|
||||
@@ -297,7 +297,7 @@ func (m *Model) Index(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
|
||||
// IndexUpdate is called for incremental updates to connected nodes' indexes.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) IndexUpdate(nodeID string, repo string, fs []protocol.FileInfo) {
|
||||
func (m *Model) IndexUpdate(nodeID protocol.NodeID, repo string, fs []protocol.FileInfo) {
|
||||
if debug {
|
||||
l.Debugf("IDXUP(in): %s / %q: %d files", nodeID, repo, len(fs))
|
||||
}
|
||||
@@ -331,7 +331,7 @@ func (m *Model) IndexUpdate(nodeID string, repo string, fs []protocol.FileInfo)
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
|
||||
func (m *Model) repoSharedWith(repo, nodeID string) bool {
|
||||
func (m *Model) repoSharedWith(repo string, nodeID protocol.NodeID) bool {
|
||||
m.rmut.RLock()
|
||||
defer m.rmut.RUnlock()
|
||||
for _, nrepo := range m.nodeRepos[nodeID] {
|
||||
@@ -342,7 +342,7 @@ func (m *Model) repoSharedWith(repo, nodeID string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Model) ClusterConfig(nodeID string, config protocol.ClusterConfigMessage) {
|
||||
func (m *Model) ClusterConfig(nodeID protocol.NodeID, config protocol.ClusterConfigMessage) {
|
||||
compErr := compareClusterConfig(m.clusterConfig(nodeID), config)
|
||||
if debug {
|
||||
l.Debugf("ClusterConfig: %s: %#v", nodeID, config)
|
||||
@@ -365,7 +365,7 @@ func (m *Model) ClusterConfig(nodeID string, config protocol.ClusterConfigMessag
|
||||
|
||||
// Close removes the peer from the model and closes the underlying connection if possible.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Close(node string, err error) {
|
||||
func (m *Model) Close(node protocol.NodeID, err error) {
|
||||
if debug {
|
||||
l.Debugf("%s: %v", node, err)
|
||||
}
|
||||
@@ -397,7 +397,7 @@ func (m *Model) Close(node string, err error) {
|
||||
|
||||
// Request returns the specified data segment by reading it from local disk.
|
||||
// Implements the protocol.Model interface.
|
||||
func (m *Model) Request(nodeID, repo, name string, offset int64, size int) ([]byte, error) {
|
||||
func (m *Model) Request(nodeID protocol.NodeID, repo, name string, offset int64, size int) ([]byte, error) {
|
||||
// Verify that the requested file exists in the local model.
|
||||
m.rmut.RLock()
|
||||
r, ok := m.repoFiles[repo]
|
||||
@@ -423,7 +423,7 @@ func (m *Model) Request(nodeID, repo, name string, offset int64, size int) ([]by
|
||||
return nil, ErrNoSuchFile
|
||||
}
|
||||
|
||||
if debug && nodeID != "<local>" {
|
||||
if debug && nodeID != cid.LocalNodeID {
|
||||
l.Debugf("REQ(in): %s: %q / %q o=%d s=%d", nodeID, repo, name, offset, size)
|
||||
}
|
||||
m.rmut.RLock()
|
||||
@@ -489,7 +489,7 @@ func (cf cFiler) CurrentFile(file string) scanner.File {
|
||||
}
|
||||
|
||||
// ConnectedTo returns true if we are connected to the named node.
|
||||
func (m *Model) ConnectedTo(nodeID string) bool {
|
||||
func (m *Model) ConnectedTo(nodeID protocol.NodeID) bool {
|
||||
m.pmut.RLock()
|
||||
_, ok := m.protoConn[nodeID]
|
||||
m.pmut.RUnlock()
|
||||
@@ -560,7 +560,7 @@ func (m *Model) updateLocal(repo string, f scanner.File) {
|
||||
m.rmut.RUnlock()
|
||||
}
|
||||
|
||||
func (m *Model) requestGlobal(nodeID, repo, name string, offset int64, size int, hash []byte) ([]byte, error) {
|
||||
func (m *Model) requestGlobal(nodeID protocol.NodeID, repo, name string, offset int64, size int, hash []byte) ([]byte, error) {
|
||||
m.pmut.RLock()
|
||||
nc, ok := m.protoConn[nodeID]
|
||||
m.pmut.RUnlock()
|
||||
@@ -639,7 +639,7 @@ func (m *Model) AddRepo(cfg config.RepositoryConfiguration) {
|
||||
m.repoFiles[cfg.ID] = files.NewSet()
|
||||
m.suppressor[cfg.ID] = &suppressor{threshold: int64(m.cfg.Options.MaxChangeKbps)}
|
||||
|
||||
m.repoNodes[cfg.ID] = make([]string, len(cfg.Nodes))
|
||||
m.repoNodes[cfg.ID] = make([]protocol.NodeID, len(cfg.Nodes))
|
||||
for i, node := range cfg.Nodes {
|
||||
m.repoNodes[cfg.ID][i] = node.NodeID
|
||||
m.nodeRepos[node.NodeID] = append(m.nodeRepos[node.NodeID], cfg.ID)
|
||||
@@ -805,7 +805,7 @@ func (m *Model) loadIndex(repo string, dir string) []protocol.FileInfo {
|
||||
}
|
||||
|
||||
// clusterConfig returns a ClusterConfigMessage that is correct for the given peer node
|
||||
func (m *Model) clusterConfig(node string) protocol.ClusterConfigMessage {
|
||||
func (m *Model) clusterConfig(node protocol.NodeID) protocol.ClusterConfigMessage {
|
||||
cm := protocol.ClusterConfigMessage{
|
||||
ClientName: m.clientName,
|
||||
ClientVersion: m.clientVersion,
|
||||
@@ -819,7 +819,7 @@ func (m *Model) clusterConfig(node string) protocol.ClusterConfigMessage {
|
||||
for _, node := range m.repoNodes[repo] {
|
||||
// TODO: Set read only bit when relevant
|
||||
cr.Nodes = append(cr.Nodes, protocol.Node{
|
||||
ID: node,
|
||||
ID: node[:],
|
||||
Flags: protocol.FlagShareTrusted,
|
||||
})
|
||||
}
|
||||
|
||||
+29
-22
@@ -17,6 +17,13 @@ import (
|
||||
"github.com/calmh/syncthing/scanner"
|
||||
)
|
||||
|
||||
var node1, node2 protocol.NodeID
|
||||
|
||||
func init() {
|
||||
node1, _ = protocol.NodeIDFromString("AIR6LPZ-7K4PTTV-UXQSMUU-CPQ5YWH-OEDFIIQ-JUG777G-2YQXXR5-YD6AWQR")
|
||||
node2, _ = protocol.NodeIDFromString("GYRZZQB-IRNPV4Z-T7TC52W-EQYJ3TT-FDQW6MW-DFLMU42-SSSU6EM-FBK2VAY")
|
||||
}
|
||||
|
||||
var testDataExpected = map[string]scanner.File{
|
||||
"foo": scanner.File{
|
||||
Name: "foo",
|
||||
@@ -56,7 +63,7 @@ func TestRequest(t *testing.T) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
|
||||
bs, err := m.Request("some node", "default", "foo", 0, 6)
|
||||
bs, err := m.Request(node1, "default", "foo", 0, 6)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -64,7 +71,7 @@ func TestRequest(t *testing.T) {
|
||||
t.Errorf("Incorrect data from request: %q", string(bs))
|
||||
}
|
||||
|
||||
bs, err = m.Request("some node", "default", "../walk.go", 0, 6)
|
||||
bs, err = m.Request(node1, "default", "../walk.go", 0, 6)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error on insecure file read")
|
||||
}
|
||||
@@ -95,7 +102,7 @@ func BenchmarkIndex10000(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +114,7 @@ func BenchmarkIndex00100(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,11 +123,11 @@ func BenchmarkIndexUpdate10000f10000(b *testing.B) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", "default", files)
|
||||
m.IndexUpdate(node1, "default", files)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +136,12 @@ func BenchmarkIndexUpdate10000f00100(b *testing.B) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
ufiles := genFiles(100)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", "default", ufiles)
|
||||
m.IndexUpdate(node1, "default", ufiles)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,17 +150,17 @@ func BenchmarkIndexUpdate10000f00001(b *testing.B) {
|
||||
m.AddRepo(config.RepositoryConfiguration{ID: "default", Directory: "testdata"})
|
||||
m.ScanRepo("default")
|
||||
files := genFiles(10000)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
ufiles := genFiles(1)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.IndexUpdate("42", "default", ufiles)
|
||||
m.IndexUpdate(node1, "default", ufiles)
|
||||
}
|
||||
}
|
||||
|
||||
type FakeConnection struct {
|
||||
id string
|
||||
id protocol.NodeID
|
||||
requestData []byte
|
||||
}
|
||||
|
||||
@@ -161,8 +168,8 @@ func (FakeConnection) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f FakeConnection) ID() string {
|
||||
return string(f.id)
|
||||
func (f FakeConnection) ID() protocol.NodeID {
|
||||
return f.id
|
||||
}
|
||||
|
||||
func (f FakeConnection) Option(string) string {
|
||||
@@ -202,15 +209,15 @@ func BenchmarkRequest(b *testing.B) {
|
||||
}
|
||||
|
||||
fc := FakeConnection{
|
||||
id: "42",
|
||||
id: node1,
|
||||
requestData: []byte("some data to return"),
|
||||
}
|
||||
m.AddConnection(fc, fc)
|
||||
m.Index("42", "default", files)
|
||||
m.Index(node1, "default", files)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
data, err := m.requestGlobal("42", "default", files[i%n].Name, 0, 32, nil)
|
||||
data, err := m.requestGlobal(node1, "default", files[i%n].Name, 0, 32, nil)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
@@ -222,26 +229,26 @@ func BenchmarkRequest(b *testing.B) {
|
||||
|
||||
func TestActivityMap(t *testing.T) {
|
||||
cm := cid.NewMap()
|
||||
fooID := cm.Get("foo")
|
||||
fooID := cm.Get(node1)
|
||||
if fooID == 0 {
|
||||
t.Fatal("ID cannot be zero")
|
||||
}
|
||||
barID := cm.Get("bar")
|
||||
barID := cm.Get(node2)
|
||||
if barID == 0 {
|
||||
t.Fatal("ID cannot be zero")
|
||||
}
|
||||
|
||||
m := make(activityMap)
|
||||
if node := m.leastBusyNode(1<<fooID, cm); node != "foo" {
|
||||
if node := m.leastBusyNode(1<<fooID, cm); node != node1 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<barID, cm); node != "bar" {
|
||||
if node := m.leastBusyNode(1<<barID, cm); node != node2 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "foo" {
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != node1 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != "bar" {
|
||||
if node := m.leastBusyNode(1<<fooID|1<<barID, cm); node != node2 {
|
||||
t.Errorf("Incorrect least busy node %q", node)
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
type requestResult struct {
|
||||
node string
|
||||
node protocol.NodeID
|
||||
file scanner.File
|
||||
filepath string // full filepath name
|
||||
offset int64
|
||||
@@ -39,11 +39,11 @@ type openFile struct {
|
||||
done bool // we have sent all requests for this file
|
||||
}
|
||||
|
||||
type activityMap map[string]int
|
||||
type activityMap map[protocol.NodeID]int
|
||||
|
||||
func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) string {
|
||||
func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) protocol.NodeID {
|
||||
var low int = 2<<30 - 1
|
||||
var selected string
|
||||
var selected protocol.NodeID
|
||||
for _, node := range cm.Names() {
|
||||
id := cm.Get(node)
|
||||
if id == cid.LocalID {
|
||||
@@ -61,7 +61,7 @@ func (m activityMap) leastBusyNode(availability uint64, cm *cid.Map) string {
|
||||
return selected
|
||||
}
|
||||
|
||||
func (m activityMap) decrease(node string) {
|
||||
func (m activityMap) decrease(node protocol.NodeID) {
|
||||
m[node]--
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ func (p *puller) handleRequestBlock(b bqBlock) bool {
|
||||
of.outstanding++
|
||||
p.openFiles[f.Name] = of
|
||||
|
||||
go func(node string, b bqBlock) {
|
||||
go func(node protocol.NodeID, b bqBlock) {
|
||||
if debug {
|
||||
l.Debugf("pull: requesting %q / %q offset %d size %d from %q outstanding %d", p.repoCfg.ID, f.Name, b.block.Offset, b.block.Size, node, of.outstanding)
|
||||
}
|
||||
|
||||
+7
-5
@@ -58,12 +58,14 @@ func fileInfoFromFile(f scanner.File) protocol.FileInfo {
|
||||
return pf
|
||||
}
|
||||
|
||||
func cmMap(cm protocol.ClusterConfigMessage) map[string]map[string]uint32 {
|
||||
m := make(map[string]map[string]uint32)
|
||||
func cmMap(cm protocol.ClusterConfigMessage) map[string]map[protocol.NodeID]uint32 {
|
||||
m := make(map[string]map[protocol.NodeID]uint32)
|
||||
for _, repo := range cm.Repositories {
|
||||
m[repo.ID] = make(map[string]uint32)
|
||||
m[repo.ID] = make(map[protocol.NodeID]uint32)
|
||||
for _, node := range repo.Nodes {
|
||||
m[repo.ID][node.ID] = node.Flags
|
||||
var id protocol.NodeID
|
||||
copy(id[:], node.ID)
|
||||
m[repo.ID][id] = node.Flags
|
||||
}
|
||||
}
|
||||
return m
|
||||
@@ -72,7 +74,7 @@ func cmMap(cm protocol.ClusterConfigMessage) map[string]map[string]uint32 {
|
||||
type ClusterConfigMismatch error
|
||||
|
||||
// compareClusterConfig returns nil for two equivalent configurations,
|
||||
// otherwise a decriptive error
|
||||
// otherwise a descriptive error
|
||||
func compareClusterConfig(local, remote protocol.ClusterConfigMessage) error {
|
||||
lm := cmMap(local)
|
||||
rm := cmMap(remote)
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/syncthing/protocol"
|
||||
)
|
||||
|
||||
var testcases = []struct {
|
||||
local, remote protocol.ClusterConfigMessage
|
||||
err string
|
||||
}{
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{},
|
||||
remote: protocol.ClusterConfigMessage{},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{ClientName: "a", ClientVersion: "b"},
|
||||
remote: protocol.ClusterConfigMessage{ClientName: "c", ClientVersion: "d"},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{ID: "a"},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{ID: "foo"},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{ID: "a"},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{ID: "a"},
|
||||
{ID: "b"},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
|
||||
{
|
||||
local: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{
|
||||
ID: "a",
|
||||
Flags: protocol.FlagShareReadOnly,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
remote: protocol.ClusterConfigMessage{
|
||||
Repositories: []protocol.Repository{
|
||||
{
|
||||
ID: "foo",
|
||||
Nodes: []protocol.Node{
|
||||
{
|
||||
ID: "a",
|
||||
Flags: protocol.FlagShareTrusted,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ID: "bar"},
|
||||
},
|
||||
},
|
||||
err: `remote has different sharing flags for node "a" in repository "foo"`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestCompareClusterConfig(t *testing.T) {
|
||||
for i, tc := range testcases {
|
||||
err := compareClusterConfig(tc.local, tc.remote)
|
||||
switch {
|
||||
case tc.err == "" && err != nil:
|
||||
t.Errorf("#%d: unexpected error: %v", i, err)
|
||||
|
||||
case tc.err != "" && err == nil:
|
||||
t.Errorf("#%d: unexpected nil error", i)
|
||||
|
||||
case tc.err != "" && err != nil && tc.err != err.Error():
|
||||
t.Errorf("#%d: incorrect error: %q != %q", i, err, tc.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,13 @@ func newTestModel() *TestModel {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TestModel) Index(nodeID string, repo string, files []FileInfo) {
|
||||
func (t *TestModel) Index(nodeID NodeID, repo string, files []FileInfo) {
|
||||
}
|
||||
|
||||
func (t *TestModel) IndexUpdate(nodeID string, repo string, files []FileInfo) {
|
||||
func (t *TestModel) IndexUpdate(nodeID NodeID, repo string, files []FileInfo) {
|
||||
}
|
||||
|
||||
func (t *TestModel) Request(nodeID, repo, name string, offset int64, size int) ([]byte, error) {
|
||||
func (t *TestModel) Request(nodeID NodeID, repo, name string, offset int64, size int) ([]byte, error) {
|
||||
t.repo = repo
|
||||
t.name = name
|
||||
t.offset = offset
|
||||
@@ -38,11 +38,11 @@ func (t *TestModel) Request(nodeID, repo, name string, offset int64, size int) (
|
||||
return t.data, nil
|
||||
}
|
||||
|
||||
func (t *TestModel) Close(nodeID string, err error) {
|
||||
func (t *TestModel) Close(nodeID NodeID, err error) {
|
||||
close(t.closedCh)
|
||||
}
|
||||
|
||||
func (t *TestModel) ClusterConfig(nodeID string, config ClusterConfigMessage) {
|
||||
func (t *TestModel) ClusterConfig(nodeID NodeID, config ClusterConfigMessage) {
|
||||
}
|
||||
|
||||
func (t *TestModel) isClosed() bool {
|
||||
|
||||
@@ -42,7 +42,7 @@ type Repository struct {
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
ID string // max:64
|
||||
ID []byte // max:32
|
||||
Flags uint32
|
||||
MaxVersion uint64
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Copyright (C) 2014 Jakob Borg and other contributors. All rights reserved.
|
||||
// Use of this source code is governed by an MIT-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
@@ -337,10 +333,10 @@ func (o Node) MarshalXDR() []byte {
|
||||
}
|
||||
|
||||
func (o Node) encodeXDR(xw *xdr.Writer) (int, error) {
|
||||
if len(o.ID) > 64 {
|
||||
if len(o.ID) > 32 {
|
||||
return xw.Tot(), xdr.ErrElementSizeExceeded
|
||||
}
|
||||
xw.WriteString(o.ID)
|
||||
xw.WriteBytes(o.ID)
|
||||
xw.WriteUint32(o.Flags)
|
||||
xw.WriteUint64(o.MaxVersion)
|
||||
return xw.Tot(), xw.Error()
|
||||
@@ -358,7 +354,7 @@ func (o *Node) UnmarshalXDR(bs []byte) error {
|
||||
}
|
||||
|
||||
func (o *Node) decodeXDR(xr *xdr.Reader) error {
|
||||
o.ID = xr.ReadStringMax(64)
|
||||
o.ID = xr.ReadBytesMax(32)
|
||||
o.Flags = xr.ReadUint32()
|
||||
o.MaxVersion = xr.ReadUint64()
|
||||
return xr.Error()
|
||||
|
||||
@@ -14,29 +14,29 @@ type nativeModel struct {
|
||||
next Model
|
||||
}
|
||||
|
||||
func (m nativeModel) Index(nodeID string, repo string, files []FileInfo) {
|
||||
func (m nativeModel) Index(nodeID NodeID, repo string, files []FileInfo) {
|
||||
for i := range files {
|
||||
files[i].Name = norm.NFD.String(files[i].Name)
|
||||
}
|
||||
m.next.Index(nodeID, repo, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) IndexUpdate(nodeID string, repo string, files []FileInfo) {
|
||||
func (m nativeModel) IndexUpdate(nodeID NodeID, repo string, files []FileInfo) {
|
||||
for i := range files {
|
||||
files[i].Name = norm.NFD.String(files[i].Name)
|
||||
}
|
||||
m.next.IndexUpdate(nodeID, repo, files)
|
||||
}
|
||||
|
||||
func (m nativeModel) Request(nodeID, repo string, name string, offset int64, size int) ([]byte, error) {
|
||||
func (m nativeModel) Request(nodeID NodeID, repo string, name string, offset int64, size int) ([]byte, error) {
|
||||
name = norm.NFD.String(name)
|
||||
return m.next.Request(nodeID, repo, name, offset, size)
|
||||
}
|
||||
|
||||
func (m nativeModel) ClusterConfig(nodeID string, config ClusterConfigMessage) {
|
||||
func (m nativeModel) ClusterConfig(nodeID NodeID, config ClusterConfigMessage) {
|
||||
m.next.ClusterConfig(nodeID, config)
|
||||
}
|
||||
|
||||
func (m nativeModel) Close(nodeID string, err error) {
|
||||
func (m nativeModel) Close(nodeID NodeID, err error) {
|
||||
m.next.Close(nodeID, err)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/calmh/syncthing/luhn"
|
||||
)
|
||||
|
||||
type NodeID [32]byte
|
||||
|
||||
// NewNodeID generates a new node ID from the raw bytes of a certificate
|
||||
func NewNodeID(rawCert []byte) NodeID {
|
||||
var n NodeID
|
||||
hf := sha256.New()
|
||||
hf.Write(rawCert)
|
||||
hf.Sum(n[:0])
|
||||
return n
|
||||
}
|
||||
|
||||
func NodeIDFromString(s string) (NodeID, error) {
|
||||
var n NodeID
|
||||
err := n.UnmarshalText([]byte(s))
|
||||
return n, err
|
||||
}
|
||||
|
||||
// String returns the canonical string representation of the node ID
|
||||
func (n NodeID) String() string {
|
||||
id := base32.StdEncoding.EncodeToString(n[:])
|
||||
id = strings.Trim(id, "=")
|
||||
id = luhnify(id)
|
||||
id = chunkify(id)
|
||||
return id
|
||||
}
|
||||
|
||||
func (n NodeID) GoString() string {
|
||||
return n.String()
|
||||
}
|
||||
|
||||
func (n NodeID) Compare(other NodeID) int {
|
||||
return bytes.Compare(n[:], other[:])
|
||||
}
|
||||
|
||||
func (n NodeID) Equals(other NodeID) bool {
|
||||
return bytes.Compare(n[:], other[:]) == 0
|
||||
}
|
||||
|
||||
func (n *NodeID) MarshalText() ([]byte, error) {
|
||||
return []byte(n.String()), nil
|
||||
}
|
||||
|
||||
func (n *NodeID) UnmarshalText(bs []byte) error {
|
||||
id := string(bs)
|
||||
id = strings.Trim(id, "=")
|
||||
id = strings.ToUpper(id)
|
||||
id = untypeoify(id)
|
||||
id = unchunkify(id)
|
||||
|
||||
var err error
|
||||
switch len(id) {
|
||||
case 56:
|
||||
// New style, with check digits
|
||||
id, err = unluhnify(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fallthrough
|
||||
case 52:
|
||||
// Old style, no check digits
|
||||
dec, err := base32.StdEncoding.DecodeString(id + "====")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(n[:], dec)
|
||||
return nil
|
||||
default:
|
||||
return errors.New("node ID invalid: incorrect length")
|
||||
}
|
||||
}
|
||||
|
||||
func luhnify(s string) string {
|
||||
if len(s) != 52 {
|
||||
panic("unsupported string length")
|
||||
}
|
||||
|
||||
res := make([]string, 0, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
p := s[i*13 : (i+1)*13]
|
||||
l := luhn.Base32Trimmed.Generate(p)
|
||||
res = append(res, fmt.Sprintf("%s%c", p, l))
|
||||
}
|
||||
return res[0] + res[1] + res[2] + res[3]
|
||||
}
|
||||
|
||||
func unluhnify(s string) (string, error) {
|
||||
if len(s) != 56 {
|
||||
return "", fmt.Errorf("unsupported string length %d", len(s))
|
||||
}
|
||||
|
||||
res := make([]string, 0, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
p := s[i*14 : (i+1)*14-1]
|
||||
l := luhn.Base32Trimmed.Generate(p)
|
||||
if g := fmt.Sprintf("%s%c", p, l); g != s[i*14:(i+1)*14] {
|
||||
log.Printf("%q; %q", g, s[i*14:(i+1)*14])
|
||||
return "", errors.New("check digit incorrect")
|
||||
}
|
||||
res = append(res, p)
|
||||
}
|
||||
return res[0] + res[1] + res[2] + res[3], nil
|
||||
}
|
||||
|
||||
func chunkify(s string) string {
|
||||
s = regexp.MustCompile("(.{7})").ReplaceAllString(s, "$1-")
|
||||
s = strings.Trim(s, "-")
|
||||
return s
|
||||
}
|
||||
|
||||
func unchunkify(s string) string {
|
||||
s = strings.Replace(s, "-", "", -1)
|
||||
s = strings.Replace(s, " ", "", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
func untypeoify(s string) string {
|
||||
s = strings.Replace(s, "0", "O", -1)
|
||||
s = strings.Replace(s, "1", "I", -1)
|
||||
s = strings.Replace(s, "8", "B", -1)
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package protocol
|
||||
|
||||
import "testing"
|
||||
|
||||
var formatted = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2"
|
||||
var formatCases = []string{
|
||||
"P56IOI-7MZJNU-2IQGDR-EYDM2M-GTMGL3-BXNPQ6-W5BTBB-Z4TJXZ-WICQ",
|
||||
"P56IOI-7MZJNU2Y-IQGDR-EYDM2M-GTI-MGL3-BXNPQ6-W5BM-TBB-Z4TJXZ-WICQ2",
|
||||
"P56IOI7 MZJNU2I QGDREYD M2MGTMGL 3BXNPQ6W 5BTB BZ4T JXZWICQ",
|
||||
"P56IOI7 MZJNU2Y IQGDREY DM2MGTI MGL3BXN PQ6W5BM TBBZ4TJ XZWICQ2",
|
||||
"P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ",
|
||||
"p56ioi7mzjnu2iqgdreydm2mgtmgl3bxnpq6w5btbbz4tjxzwicq",
|
||||
"P56IOI7MZJNU2YIQGDREYDM2MGTIMGL3BXNPQ6W5BMTBBZ4TJXZWICQ2",
|
||||
"P561017MZJNU2YIQGDREYDM2MGTIMGL3BXNPQ6W5BMT88Z4TJXZWICQ2",
|
||||
"p56ioi7mzjnu2yiqgdreydm2mgtimgl3bxnpq6w5bmtbbz4tjxzwicq2",
|
||||
"p561017mzjnu2yiqgdreydm2mgtimgl3bxnpq6w5bmt88z4tjxzwicq2",
|
||||
}
|
||||
|
||||
func TestFormatNodeID(t *testing.T) {
|
||||
for i, tc := range formatCases {
|
||||
var id NodeID
|
||||
err := id.UnmarshalText([]byte(tc))
|
||||
if err != nil {
|
||||
t.Errorf("#%d UnmarshalText(%q); %v", i, tc, err)
|
||||
} else if f := id.String(); f != formatted {
|
||||
t.Errorf("#%d FormatNodeID(%q)\n\t%q !=\n\t%q", i, tc, f, formatted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var validateCases = []struct {
|
||||
s string
|
||||
ok bool
|
||||
}{
|
||||
{"", false},
|
||||
{"P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2", true},
|
||||
{"P56IOI7-MZJNU2-IQGDREY-DM2MGT-MGL3BXN-PQ6W5B-TBBZ4TJ-XZWICQ", true},
|
||||
{"P56IOI7 MZJNU2I QGDREYD M2MGTMGL 3BXNPQ6W 5BTB BZ4T JXZWICQ", true},
|
||||
{"P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQ", true},
|
||||
{"P56IOI7MZJNU2IQGDREYDM2MGTMGL3BXNPQ6W5BTBBZ4TJXZWICQCCCC", false},
|
||||
{"p56ioi7mzjnu2iqgdreydm2mgtmgl3bxnpq6w5btbbz4tjxzwicq", true},
|
||||
{"p56ioi7mzjnu2iqgdreydm2mgtmgl3bxnpq6w5btbbz4tjxzwicqCCCC", false},
|
||||
}
|
||||
|
||||
func TestValidateNodeID(t *testing.T) {
|
||||
for _, tc := range validateCases {
|
||||
var id NodeID
|
||||
err := id.UnmarshalText([]byte(tc.s))
|
||||
if (err == nil && !tc.ok) || (err != nil && tc.ok) {
|
||||
t.Errorf("ValidateNodeID(%q); %v != %v", tc.s, err, tc.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshallingNodeID(t *testing.T) {
|
||||
n0 := NodeID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}
|
||||
n1 := NodeID{}
|
||||
n2 := NodeID{}
|
||||
|
||||
bs, _ := n0.MarshalText()
|
||||
n1.UnmarshalText(bs)
|
||||
bs, _ = n1.MarshalText()
|
||||
n2.UnmarshalText(bs)
|
||||
|
||||
if n2.String() != n0.String() {
|
||||
t.Errorf("String marshalling error; %q != %q", n2.String(), n0.String())
|
||||
}
|
||||
if !n2.Equals(n0) {
|
||||
t.Error("Equals error")
|
||||
}
|
||||
if n2.Compare(n0) != 0 {
|
||||
t.Error("Compare error")
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -48,19 +48,19 @@ var (
|
||||
|
||||
type Model interface {
|
||||
// An index was received from the peer node
|
||||
Index(nodeID string, repo string, files []FileInfo)
|
||||
Index(nodeID NodeID, repo string, files []FileInfo)
|
||||
// An index update was received from the peer node
|
||||
IndexUpdate(nodeID string, repo string, files []FileInfo)
|
||||
IndexUpdate(nodeID NodeID, repo string, files []FileInfo)
|
||||
// A request was made by the peer node
|
||||
Request(nodeID string, repo string, name string, offset int64, size int) ([]byte, error)
|
||||
Request(nodeID NodeID, repo string, name string, offset int64, size int) ([]byte, error)
|
||||
// A cluster configuration message was received
|
||||
ClusterConfig(nodeID string, config ClusterConfigMessage)
|
||||
ClusterConfig(nodeID NodeID, config ClusterConfigMessage)
|
||||
// The peer node closed the connection
|
||||
Close(nodeID string, err error)
|
||||
Close(nodeID NodeID, err error)
|
||||
}
|
||||
|
||||
type Connection interface {
|
||||
ID() string
|
||||
ID() NodeID
|
||||
Index(repo string, files []FileInfo)
|
||||
Request(repo string, name string, offset int64, size int) ([]byte, error)
|
||||
ClusterConfig(config ClusterConfigMessage)
|
||||
@@ -68,7 +68,7 @@ type Connection interface {
|
||||
}
|
||||
|
||||
type rawConnection struct {
|
||||
id string
|
||||
id NodeID
|
||||
receiver Model
|
||||
|
||||
reader io.ReadCloser
|
||||
@@ -102,7 +102,7 @@ const (
|
||||
pingIdleTime = 60 * time.Second
|
||||
)
|
||||
|
||||
func NewConnection(nodeID string, reader io.Reader, writer io.Writer, receiver Model) Connection {
|
||||
func NewConnection(nodeID NodeID, reader io.Reader, writer io.Writer, receiver Model) Connection {
|
||||
cr := &countingReader{Reader: reader}
|
||||
cw := &countingWriter{Writer: writer}
|
||||
|
||||
@@ -139,7 +139,7 @@ func NewConnection(nodeID string, reader io.Reader, writer io.Writer, receiver M
|
||||
return wireFormatConnection{&c}
|
||||
}
|
||||
|
||||
func (c *rawConnection) ID() string {
|
||||
func (c *rawConnection) ID() NodeID {
|
||||
return c.id
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ func (c *rawConnection) readerLoop() (err error) {
|
||||
|
||||
type incomingIndex struct {
|
||||
update bool
|
||||
id string
|
||||
id NodeID
|
||||
repo string
|
||||
files []FileInfo
|
||||
}
|
||||
|
||||
+17
-12
@@ -11,6 +11,11 @@ import (
|
||||
"testing/quick"
|
||||
)
|
||||
|
||||
var (
|
||||
c0ID = NewNodeID([]byte{1})
|
||||
c1ID = NewNodeID([]byte{2})
|
||||
)
|
||||
|
||||
func TestHeaderFunctions(t *testing.T) {
|
||||
f := func(ver, id, typ int) bool {
|
||||
ver = int(uint(ver) % 16)
|
||||
@@ -54,8 +59,8 @@ func TestPing(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection("c0", ar, bw, nil).(wireFormatConnection).next.(*rawConnection)
|
||||
c1 := NewConnection("c1", br, aw, nil).(wireFormatConnection).next.(*rawConnection)
|
||||
c0 := NewConnection(c0ID, ar, bw, nil).(wireFormatConnection).next.(*rawConnection)
|
||||
c1 := NewConnection(c1ID, br, aw, nil).(wireFormatConnection).next.(*rawConnection)
|
||||
|
||||
if ok := c0.ping(); !ok {
|
||||
t.Error("c0 ping failed")
|
||||
@@ -78,8 +83,8 @@ func TestPingErr(t *testing.T) {
|
||||
eaw := &ErrPipe{PipeWriter: *aw, max: i, err: e}
|
||||
ebw := &ErrPipe{PipeWriter: *bw, max: j, err: e}
|
||||
|
||||
c0 := NewConnection("c0", ar, ebw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection("c1", br, eaw, m1)
|
||||
c0 := NewConnection(c0ID, ar, ebw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection(c1ID, br, eaw, m1)
|
||||
|
||||
res := c0.ping()
|
||||
if (i < 4 || j < 4) && res {
|
||||
@@ -106,8 +111,8 @@ func TestPingErr(t *testing.T) {
|
||||
// eaw := &ErrPipe{PipeWriter: *aw, max: i, err: e}
|
||||
// ebw := &ErrPipe{PipeWriter: *bw, max: j, err: e}
|
||||
|
||||
// NewConnection("c0", ar, ebw, m0, nil)
|
||||
// c1 := NewConnection("c1", br, eaw, m1, nil).(wireFormatConnection).next.(*rawConnection)
|
||||
// NewConnection(c0ID, ar, ebw, m0, nil)
|
||||
// c1 := NewConnection(c1ID, br, eaw, m1, nil).(wireFormatConnection).next.(*rawConnection)
|
||||
|
||||
// d, err := c1.Request("default", "tn", 1234, 5678)
|
||||
// if err == e || err == ErrClosed {
|
||||
@@ -154,8 +159,8 @@ func TestVersionErr(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection("c0", ar, bw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection("c1", br, aw, m1)
|
||||
c0 := NewConnection(c0ID, ar, bw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection(c1ID, br, aw, m1)
|
||||
|
||||
c0.xw.WriteUint32(encodeHeader(header{
|
||||
version: 2,
|
||||
@@ -176,8 +181,8 @@ func TestTypeErr(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection("c0", ar, bw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection("c1", br, aw, m1)
|
||||
c0 := NewConnection(c0ID, ar, bw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection(c1ID, br, aw, m1)
|
||||
|
||||
c0.xw.WriteUint32(encodeHeader(header{
|
||||
version: 0,
|
||||
@@ -198,8 +203,8 @@ func TestClose(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := NewConnection("c0", ar, bw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection("c1", br, aw, m1)
|
||||
c0 := NewConnection(c0ID, ar, bw, m0).(wireFormatConnection).next.(*rawConnection)
|
||||
NewConnection(c1ID, br, aw, m1)
|
||||
|
||||
c0.close(nil)
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ type wireFormatConnection struct {
|
||||
next Connection
|
||||
}
|
||||
|
||||
func (c wireFormatConnection) ID() string {
|
||||
func (c wireFormatConnection) ID() NodeID {
|
||||
return c.next.ID()
|
||||
}
|
||||
|
||||
func (c wireFormatConnection) Index(node string, fs []FileInfo) {
|
||||
func (c wireFormatConnection) Index(repo string, fs []FileInfo) {
|
||||
var myFs = make([]FileInfo, len(fs))
|
||||
copy(myFs, fs)
|
||||
|
||||
@@ -26,7 +26,7 @@ func (c wireFormatConnection) Index(node string, fs []FileInfo) {
|
||||
myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
|
||||
}
|
||||
|
||||
c.next.Index(node, myFs)
|
||||
c.next.Index(repo, myFs)
|
||||
}
|
||||
|
||||
func (c wireFormatConnection) Request(repo, name string, offset int64, size int) ([]byte, error) {
|
||||
|
||||
Reference in new issue
Block a user